##// END OF EJS Templates
fix: again allow formatting the working copy while merging...
Martin von Zweigbergk -
r49115:86a60679 5.9.1 stable
parent child Browse files
Show More
@@ -1,940 +1,939 b''
1 # fix - rewrite file content in changesets and working copy
1 # fix - rewrite file content in changesets and working copy
2 #
2 #
3 # Copyright 2018 Google LLC.
3 # Copyright 2018 Google LLC.
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 or any later version.
6 # GNU General Public License version 2 or any later version.
7 """rewrite file content in changesets or working copy (EXPERIMENTAL)
7 """rewrite file content in changesets or working copy (EXPERIMENTAL)
8
8
9 Provides a command that runs configured tools on the contents of modified files,
9 Provides a command that runs configured tools on the contents of modified files,
10 writing back any fixes to the working copy or replacing changesets.
10 writing back any fixes to the working copy or replacing changesets.
11
11
12 Here is an example configuration that causes :hg:`fix` to apply automatic
12 Here is an example configuration that causes :hg:`fix` to apply automatic
13 formatting fixes to modified lines in C++ code::
13 formatting fixes to modified lines in C++ code::
14
14
15 [fix]
15 [fix]
16 clang-format:command=clang-format --assume-filename={rootpath}
16 clang-format:command=clang-format --assume-filename={rootpath}
17 clang-format:linerange=--lines={first}:{last}
17 clang-format:linerange=--lines={first}:{last}
18 clang-format:pattern=set:**.cpp or **.hpp
18 clang-format:pattern=set:**.cpp or **.hpp
19
19
20 The :command suboption forms the first part of the shell command that will be
20 The :command suboption forms the first part of the shell command that will be
21 used to fix a file. The content of the file is passed on standard input, and the
21 used to fix a file. The content of the file is passed on standard input, and the
22 fixed file content is expected on standard output. Any output on standard error
22 fixed file content is expected on standard output. Any output on standard error
23 will be displayed as a warning. If the exit status is not zero, the file will
23 will be displayed as a warning. If the exit status is not zero, the file will
24 not be affected. A placeholder warning is displayed if there is a non-zero exit
24 not be affected. A placeholder warning is displayed if there is a non-zero exit
25 status but no standard error output. Some values may be substituted into the
25 status but no standard error output. Some values may be substituted into the
26 command::
26 command::
27
27
28 {rootpath} The path of the file being fixed, relative to the repo root
28 {rootpath} The path of the file being fixed, relative to the repo root
29 {basename} The name of the file being fixed, without the directory path
29 {basename} The name of the file being fixed, without the directory path
30
30
31 If the :linerange suboption is set, the tool will only be run if there are
31 If the :linerange suboption is set, the tool will only be run if there are
32 changed lines in a file. The value of this suboption is appended to the shell
32 changed lines in a file. The value of this suboption is appended to the shell
33 command once for every range of changed lines in the file. Some values may be
33 command once for every range of changed lines in the file. Some values may be
34 substituted into the command::
34 substituted into the command::
35
35
36 {first} The 1-based line number of the first line in the modified range
36 {first} The 1-based line number of the first line in the modified range
37 {last} The 1-based line number of the last line in the modified range
37 {last} The 1-based line number of the last line in the modified range
38
38
39 Deleted sections of a file will be ignored by :linerange, because there is no
39 Deleted sections of a file will be ignored by :linerange, because there is no
40 corresponding line range in the version being fixed.
40 corresponding line range in the version being fixed.
41
41
42 By default, tools that set :linerange will only be executed if there is at least
42 By default, tools that set :linerange will only be executed if there is at least
43 one changed line range. This is meant to prevent accidents like running a code
43 one changed line range. This is meant to prevent accidents like running a code
44 formatter in such a way that it unexpectedly reformats the whole file. If such a
44 formatter in such a way that it unexpectedly reformats the whole file. If such a
45 tool needs to operate on unchanged files, it should set the :skipclean suboption
45 tool needs to operate on unchanged files, it should set the :skipclean suboption
46 to false.
46 to false.
47
47
48 The :pattern suboption determines which files will be passed through each
48 The :pattern suboption determines which files will be passed through each
49 configured tool. See :hg:`help patterns` for possible values. However, all
49 configured tool. See :hg:`help patterns` for possible values. However, all
50 patterns are relative to the repo root, even if that text says they are relative
50 patterns are relative to the repo root, even if that text says they are relative
51 to the current working directory. If there are file arguments to :hg:`fix`, the
51 to the current working directory. If there are file arguments to :hg:`fix`, the
52 intersection of these patterns is used.
52 intersection of these patterns is used.
53
53
54 There is also a configurable limit for the maximum size of file that will be
54 There is also a configurable limit for the maximum size of file that will be
55 processed by :hg:`fix`::
55 processed by :hg:`fix`::
56
56
57 [fix]
57 [fix]
58 maxfilesize = 2MB
58 maxfilesize = 2MB
59
59
60 Normally, execution of configured tools will continue after a failure (indicated
60 Normally, execution of configured tools will continue after a failure (indicated
61 by a non-zero exit status). It can also be configured to abort after the first
61 by a non-zero exit status). It can also be configured to abort after the first
62 such failure, so that no files will be affected if any tool fails. This abort
62 such failure, so that no files will be affected if any tool fails. This abort
63 will also cause :hg:`fix` to exit with a non-zero status::
63 will also cause :hg:`fix` to exit with a non-zero status::
64
64
65 [fix]
65 [fix]
66 failure = abort
66 failure = abort
67
67
68 When multiple tools are configured to affect a file, they execute in an order
68 When multiple tools are configured to affect a file, they execute in an order
69 defined by the :priority suboption. The priority suboption has a default value
69 defined by the :priority suboption. The priority suboption has a default value
70 of zero for each tool. Tools are executed in order of descending priority. The
70 of zero for each tool. Tools are executed in order of descending priority. The
71 execution order of tools with equal priority is unspecified. For example, you
71 execution order of tools with equal priority is unspecified. For example, you
72 could use the 'sort' and 'head' utilities to keep only the 10 smallest numbers
72 could use the 'sort' and 'head' utilities to keep only the 10 smallest numbers
73 in a text file by ensuring that 'sort' runs before 'head'::
73 in a text file by ensuring that 'sort' runs before 'head'::
74
74
75 [fix]
75 [fix]
76 sort:command = sort -n
76 sort:command = sort -n
77 head:command = head -n 10
77 head:command = head -n 10
78 sort:pattern = numbers.txt
78 sort:pattern = numbers.txt
79 head:pattern = numbers.txt
79 head:pattern = numbers.txt
80 sort:priority = 2
80 sort:priority = 2
81 head:priority = 1
81 head:priority = 1
82
82
83 To account for changes made by each tool, the line numbers used for incremental
83 To account for changes made by each tool, the line numbers used for incremental
84 formatting are recomputed before executing the next tool. So, each tool may see
84 formatting are recomputed before executing the next tool. So, each tool may see
85 different values for the arguments added by the :linerange suboption.
85 different values for the arguments added by the :linerange suboption.
86
86
87 Each fixer tool is allowed to return some metadata in addition to the fixed file
87 Each fixer tool is allowed to return some metadata in addition to the fixed file
88 content. The metadata must be placed before the file content on stdout,
88 content. The metadata must be placed before the file content on stdout,
89 separated from the file content by a zero byte. The metadata is parsed as a JSON
89 separated from the file content by a zero byte. The metadata is parsed as a JSON
90 value (so, it should be UTF-8 encoded and contain no zero bytes). A fixer tool
90 value (so, it should be UTF-8 encoded and contain no zero bytes). A fixer tool
91 is expected to produce this metadata encoding if and only if the :metadata
91 is expected to produce this metadata encoding if and only if the :metadata
92 suboption is true::
92 suboption is true::
93
93
94 [fix]
94 [fix]
95 tool:command = tool --prepend-json-metadata
95 tool:command = tool --prepend-json-metadata
96 tool:metadata = true
96 tool:metadata = true
97
97
98 The metadata values are passed to hooks, which can be used to print summaries or
98 The metadata values are passed to hooks, which can be used to print summaries or
99 perform other post-fixing work. The supported hooks are::
99 perform other post-fixing work. The supported hooks are::
100
100
101 "postfixfile"
101 "postfixfile"
102 Run once for each file in each revision where any fixer tools made changes
102 Run once for each file in each revision where any fixer tools made changes
103 to the file content. Provides "$HG_REV" and "$HG_PATH" to identify the file,
103 to the file content. Provides "$HG_REV" and "$HG_PATH" to identify the file,
104 and "$HG_METADATA" with a map of fixer names to metadata values from fixer
104 and "$HG_METADATA" with a map of fixer names to metadata values from fixer
105 tools that affected the file. Fixer tools that didn't affect the file have a
105 tools that affected the file. Fixer tools that didn't affect the file have a
106 value of None. Only fixer tools that executed are present in the metadata.
106 value of None. Only fixer tools that executed are present in the metadata.
107
107
108 "postfix"
108 "postfix"
109 Run once after all files and revisions have been handled. Provides
109 Run once after all files and revisions have been handled. Provides
110 "$HG_REPLACEMENTS" with information about what revisions were created and
110 "$HG_REPLACEMENTS" with information about what revisions were created and
111 made obsolete. Provides a boolean "$HG_WDIRWRITTEN" to indicate whether any
111 made obsolete. Provides a boolean "$HG_WDIRWRITTEN" to indicate whether any
112 files in the working copy were updated. Provides a list "$HG_METADATA"
112 files in the working copy were updated. Provides a list "$HG_METADATA"
113 mapping fixer tool names to lists of metadata values returned from
113 mapping fixer tool names to lists of metadata values returned from
114 executions that modified a file. This aggregates the same metadata
114 executions that modified a file. This aggregates the same metadata
115 previously passed to the "postfixfile" hook.
115 previously passed to the "postfixfile" hook.
116
116
117 Fixer tools are run in the repository's root directory. This allows them to read
117 Fixer tools are run in the repository's root directory. This allows them to read
118 configuration files from the working copy, or even write to the working copy.
118 configuration files from the working copy, or even write to the working copy.
119 The working copy is not updated to match the revision being fixed. In fact,
119 The working copy is not updated to match the revision being fixed. In fact,
120 several revisions may be fixed in parallel. Writes to the working copy are not
120 several revisions may be fixed in parallel. Writes to the working copy are not
121 amended into the revision being fixed; fixer tools should always write fixed
121 amended into the revision being fixed; fixer tools should always write fixed
122 file content back to stdout as documented above.
122 file content back to stdout as documented above.
123 """
123 """
124
124
125 from __future__ import absolute_import
125 from __future__ import absolute_import
126
126
127 import collections
127 import collections
128 import itertools
128 import itertools
129 import os
129 import os
130 import re
130 import re
131 import subprocess
131 import subprocess
132
132
133 from mercurial.i18n import _
133 from mercurial.i18n import _
134 from mercurial.node import (
134 from mercurial.node import (
135 nullid,
135 nullid,
136 nullrev,
136 nullrev,
137 wdirrev,
137 wdirrev,
138 )
138 )
139
139
140 from mercurial.utils import procutil
140 from mercurial.utils import procutil
141
141
142 from mercurial import (
142 from mercurial import (
143 cmdutil,
143 cmdutil,
144 context,
144 context,
145 copies,
145 copies,
146 error,
146 error,
147 match as matchmod,
147 match as matchmod,
148 mdiff,
148 mdiff,
149 merge,
149 merge,
150 mergestate as mergestatemod,
150 mergestate as mergestatemod,
151 obsolete,
151 obsolete,
152 pycompat,
152 pycompat,
153 registrar,
153 registrar,
154 rewriteutil,
154 rewriteutil,
155 scmutil,
155 scmutil,
156 util,
156 util,
157 worker,
157 worker,
158 )
158 )
159
159
160 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
160 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
161 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
161 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
162 # be specifying the version(s) of Mercurial they are tested with, or
162 # be specifying the version(s) of Mercurial they are tested with, or
163 # leave the attribute unspecified.
163 # leave the attribute unspecified.
164 testedwith = b'ships-with-hg-core'
164 testedwith = b'ships-with-hg-core'
165
165
166 cmdtable = {}
166 cmdtable = {}
167 command = registrar.command(cmdtable)
167 command = registrar.command(cmdtable)
168
168
169 configtable = {}
169 configtable = {}
170 configitem = registrar.configitem(configtable)
170 configitem = registrar.configitem(configtable)
171
171
172 # Register the suboptions allowed for each configured fixer, and default values.
172 # Register the suboptions allowed for each configured fixer, and default values.
173 FIXER_ATTRS = {
173 FIXER_ATTRS = {
174 b'command': None,
174 b'command': None,
175 b'linerange': None,
175 b'linerange': None,
176 b'pattern': None,
176 b'pattern': None,
177 b'priority': 0,
177 b'priority': 0,
178 b'metadata': False,
178 b'metadata': False,
179 b'skipclean': True,
179 b'skipclean': True,
180 b'enabled': True,
180 b'enabled': True,
181 }
181 }
182
182
183 for key, default in FIXER_ATTRS.items():
183 for key, default in FIXER_ATTRS.items():
184 configitem(b'fix', b'.*:%s$' % key, default=default, generic=True)
184 configitem(b'fix', b'.*:%s$' % key, default=default, generic=True)
185
185
186 # A good default size allows most source code files to be fixed, but avoids
186 # A good default size allows most source code files to be fixed, but avoids
187 # letting fixer tools choke on huge inputs, which could be surprising to the
187 # letting fixer tools choke on huge inputs, which could be surprising to the
188 # user.
188 # user.
189 configitem(b'fix', b'maxfilesize', default=b'2MB')
189 configitem(b'fix', b'maxfilesize', default=b'2MB')
190
190
191 # Allow fix commands to exit non-zero if an executed fixer tool exits non-zero.
191 # Allow fix commands to exit non-zero if an executed fixer tool exits non-zero.
192 # This helps users do shell scripts that stop when a fixer tool signals a
192 # This helps users do shell scripts that stop when a fixer tool signals a
193 # problem.
193 # problem.
194 configitem(b'fix', b'failure', default=b'continue')
194 configitem(b'fix', b'failure', default=b'continue')
195
195
196
196
197 def checktoolfailureaction(ui, message, hint=None):
197 def checktoolfailureaction(ui, message, hint=None):
198 """Abort with 'message' if fix.failure=abort"""
198 """Abort with 'message' if fix.failure=abort"""
199 action = ui.config(b'fix', b'failure')
199 action = ui.config(b'fix', b'failure')
200 if action not in (b'continue', b'abort'):
200 if action not in (b'continue', b'abort'):
201 raise error.Abort(
201 raise error.Abort(
202 _(b'unknown fix.failure action: %s') % (action,),
202 _(b'unknown fix.failure action: %s') % (action,),
203 hint=_(b'use "continue" or "abort"'),
203 hint=_(b'use "continue" or "abort"'),
204 )
204 )
205 if action == b'abort':
205 if action == b'abort':
206 raise error.Abort(message, hint=hint)
206 raise error.Abort(message, hint=hint)
207
207
208
208
209 allopt = (b'', b'all', False, _(b'fix all non-public non-obsolete revisions'))
209 allopt = (b'', b'all', False, _(b'fix all non-public non-obsolete revisions'))
210 baseopt = (
210 baseopt = (
211 b'',
211 b'',
212 b'base',
212 b'base',
213 [],
213 [],
214 _(
214 _(
215 b'revisions to diff against (overrides automatic '
215 b'revisions to diff against (overrides automatic '
216 b'selection, and applies to every revision being '
216 b'selection, and applies to every revision being '
217 b'fixed)'
217 b'fixed)'
218 ),
218 ),
219 _(b'REV'),
219 _(b'REV'),
220 )
220 )
221 revopt = (b'r', b'rev', [], _(b'revisions to fix (ADVANCED)'), _(b'REV'))
221 revopt = (b'r', b'rev', [], _(b'revisions to fix (ADVANCED)'), _(b'REV'))
222 sourceopt = (
222 sourceopt = (
223 b's',
223 b's',
224 b'source',
224 b'source',
225 [],
225 [],
226 _(b'fix the specified revisions and their descendants'),
226 _(b'fix the specified revisions and their descendants'),
227 _(b'REV'),
227 _(b'REV'),
228 )
228 )
229 wdiropt = (b'w', b'working-dir', False, _(b'fix the working directory'))
229 wdiropt = (b'w', b'working-dir', False, _(b'fix the working directory'))
230 wholeopt = (b'', b'whole', False, _(b'always fix every line of a file'))
230 wholeopt = (b'', b'whole', False, _(b'always fix every line of a file'))
231 usage = _(b'[OPTION]... [FILE]...')
231 usage = _(b'[OPTION]... [FILE]...')
232
232
233
233
234 @command(
234 @command(
235 b'fix',
235 b'fix',
236 [allopt, baseopt, revopt, sourceopt, wdiropt, wholeopt],
236 [allopt, baseopt, revopt, sourceopt, wdiropt, wholeopt],
237 usage,
237 usage,
238 helpcategory=command.CATEGORY_FILE_CONTENTS,
238 helpcategory=command.CATEGORY_FILE_CONTENTS,
239 )
239 )
240 def fix(ui, repo, *pats, **opts):
240 def fix(ui, repo, *pats, **opts):
241 """rewrite file content in changesets or working directory
241 """rewrite file content in changesets or working directory
242
242
243 Runs any configured tools to fix the content of files. Only affects files
243 Runs any configured tools to fix the content of files. Only affects files
244 with changes, unless file arguments are provided. Only affects changed lines
244 with changes, unless file arguments are provided. Only affects changed lines
245 of files, unless the --whole flag is used. Some tools may always affect the
245 of files, unless the --whole flag is used. Some tools may always affect the
246 whole file regardless of --whole.
246 whole file regardless of --whole.
247
247
248 If --working-dir is used, files with uncommitted changes in the working copy
248 If --working-dir is used, files with uncommitted changes in the working copy
249 will be fixed. Note that no backup are made.
249 will be fixed. Note that no backup are made.
250
250
251 If revisions are specified with --source, those revisions and their
251 If revisions are specified with --source, those revisions and their
252 descendants will be checked, and they may be replaced with new revisions
252 descendants will be checked, and they may be replaced with new revisions
253 that have fixed file content. By automatically including the descendants,
253 that have fixed file content. By automatically including the descendants,
254 no merging, rebasing, or evolution will be required. If an ancestor of the
254 no merging, rebasing, or evolution will be required. If an ancestor of the
255 working copy is included, then the working copy itself will also be fixed,
255 working copy is included, then the working copy itself will also be fixed,
256 and the working copy will be updated to the fixed parent.
256 and the working copy will be updated to the fixed parent.
257
257
258 When determining what lines of each file to fix at each revision, the whole
258 When determining what lines of each file to fix at each revision, the whole
259 set of revisions being fixed is considered, so that fixes to earlier
259 set of revisions being fixed is considered, so that fixes to earlier
260 revisions are not forgotten in later ones. The --base flag can be used to
260 revisions are not forgotten in later ones. The --base flag can be used to
261 override this default behavior, though it is not usually desirable to do so.
261 override this default behavior, though it is not usually desirable to do so.
262 """
262 """
263 opts = pycompat.byteskwargs(opts)
263 opts = pycompat.byteskwargs(opts)
264 cmdutil.check_at_most_one_arg(opts, b'all', b'source', b'rev')
264 cmdutil.check_at_most_one_arg(opts, b'all', b'source', b'rev')
265 cmdutil.check_incompatible_arguments(
265 cmdutil.check_incompatible_arguments(
266 opts, b'working_dir', [b'all', b'source']
266 opts, b'working_dir', [b'all', b'source']
267 )
267 )
268
268
269 with repo.wlock(), repo.lock(), repo.transaction(b'fix'):
269 with repo.wlock(), repo.lock(), repo.transaction(b'fix'):
270 revstofix = getrevstofix(ui, repo, opts)
270 revstofix = getrevstofix(ui, repo, opts)
271 basectxs = getbasectxs(repo, opts, revstofix)
271 basectxs = getbasectxs(repo, opts, revstofix)
272 workqueue, numitems = getworkqueue(
272 workqueue, numitems = getworkqueue(
273 ui, repo, pats, opts, revstofix, basectxs
273 ui, repo, pats, opts, revstofix, basectxs
274 )
274 )
275 basepaths = getbasepaths(repo, opts, workqueue, basectxs)
275 basepaths = getbasepaths(repo, opts, workqueue, basectxs)
276 fixers = getfixers(ui)
276 fixers = getfixers(ui)
277
277
278 # Rather than letting each worker independently fetch the files
278 # Rather than letting each worker independently fetch the files
279 # (which also would add complications for shared/keepalive
279 # (which also would add complications for shared/keepalive
280 # connections), prefetch them all first.
280 # connections), prefetch them all first.
281 _prefetchfiles(repo, workqueue, basepaths)
281 _prefetchfiles(repo, workqueue, basepaths)
282
282
283 # There are no data dependencies between the workers fixing each file
283 # There are no data dependencies between the workers fixing each file
284 # revision, so we can use all available parallelism.
284 # revision, so we can use all available parallelism.
285 def getfixes(items):
285 def getfixes(items):
286 for rev, path in items:
286 for rev, path in items:
287 ctx = repo[rev]
287 ctx = repo[rev]
288 olddata = ctx[path].data()
288 olddata = ctx[path].data()
289 metadata, newdata = fixfile(
289 metadata, newdata = fixfile(
290 ui, repo, opts, fixers, ctx, path, basepaths, basectxs[rev]
290 ui, repo, opts, fixers, ctx, path, basepaths, basectxs[rev]
291 )
291 )
292 # Don't waste memory/time passing unchanged content back, but
292 # Don't waste memory/time passing unchanged content back, but
293 # produce one result per item either way.
293 # produce one result per item either way.
294 yield (
294 yield (
295 rev,
295 rev,
296 path,
296 path,
297 metadata,
297 metadata,
298 newdata if newdata != olddata else None,
298 newdata if newdata != olddata else None,
299 )
299 )
300
300
301 results = worker.worker(
301 results = worker.worker(
302 ui, 1.0, getfixes, tuple(), workqueue, threadsafe=False
302 ui, 1.0, getfixes, tuple(), workqueue, threadsafe=False
303 )
303 )
304
304
305 # We have to hold on to the data for each successor revision in memory
305 # We have to hold on to the data for each successor revision in memory
306 # until all its parents are committed. We ensure this by committing and
306 # until all its parents are committed. We ensure this by committing and
307 # freeing memory for the revisions in some topological order. This
307 # freeing memory for the revisions in some topological order. This
308 # leaves a little bit of memory efficiency on the table, but also makes
308 # leaves a little bit of memory efficiency on the table, but also makes
309 # the tests deterministic. It might also be considered a feature since
309 # the tests deterministic. It might also be considered a feature since
310 # it makes the results more easily reproducible.
310 # it makes the results more easily reproducible.
311 filedata = collections.defaultdict(dict)
311 filedata = collections.defaultdict(dict)
312 aggregatemetadata = collections.defaultdict(list)
312 aggregatemetadata = collections.defaultdict(list)
313 replacements = {}
313 replacements = {}
314 wdirwritten = False
314 wdirwritten = False
315 commitorder = sorted(revstofix, reverse=True)
315 commitorder = sorted(revstofix, reverse=True)
316 with ui.makeprogress(
316 with ui.makeprogress(
317 topic=_(b'fixing'), unit=_(b'files'), total=sum(numitems.values())
317 topic=_(b'fixing'), unit=_(b'files'), total=sum(numitems.values())
318 ) as progress:
318 ) as progress:
319 for rev, path, filerevmetadata, newdata in results:
319 for rev, path, filerevmetadata, newdata in results:
320 progress.increment(item=path)
320 progress.increment(item=path)
321 for fixername, fixermetadata in filerevmetadata.items():
321 for fixername, fixermetadata in filerevmetadata.items():
322 aggregatemetadata[fixername].append(fixermetadata)
322 aggregatemetadata[fixername].append(fixermetadata)
323 if newdata is not None:
323 if newdata is not None:
324 filedata[rev][path] = newdata
324 filedata[rev][path] = newdata
325 hookargs = {
325 hookargs = {
326 b'rev': rev,
326 b'rev': rev,
327 b'path': path,
327 b'path': path,
328 b'metadata': filerevmetadata,
328 b'metadata': filerevmetadata,
329 }
329 }
330 repo.hook(
330 repo.hook(
331 b'postfixfile',
331 b'postfixfile',
332 throw=False,
332 throw=False,
333 **pycompat.strkwargs(hookargs)
333 **pycompat.strkwargs(hookargs)
334 )
334 )
335 numitems[rev] -= 1
335 numitems[rev] -= 1
336 # Apply the fixes for this and any other revisions that are
336 # Apply the fixes for this and any other revisions that are
337 # ready and sitting at the front of the queue. Using a loop here
337 # ready and sitting at the front of the queue. Using a loop here
338 # prevents the queue from being blocked by the first revision to
338 # prevents the queue from being blocked by the first revision to
339 # be ready out of order.
339 # be ready out of order.
340 while commitorder and not numitems[commitorder[-1]]:
340 while commitorder and not numitems[commitorder[-1]]:
341 rev = commitorder.pop()
341 rev = commitorder.pop()
342 ctx = repo[rev]
342 ctx = repo[rev]
343 if rev == wdirrev:
343 if rev == wdirrev:
344 writeworkingdir(repo, ctx, filedata[rev], replacements)
344 writeworkingdir(repo, ctx, filedata[rev], replacements)
345 wdirwritten = bool(filedata[rev])
345 wdirwritten = bool(filedata[rev])
346 else:
346 else:
347 replacerev(ui, repo, ctx, filedata[rev], replacements)
347 replacerev(ui, repo, ctx, filedata[rev], replacements)
348 del filedata[rev]
348 del filedata[rev]
349
349
350 cleanup(repo, replacements, wdirwritten)
350 cleanup(repo, replacements, wdirwritten)
351 hookargs = {
351 hookargs = {
352 b'replacements': replacements,
352 b'replacements': replacements,
353 b'wdirwritten': wdirwritten,
353 b'wdirwritten': wdirwritten,
354 b'metadata': aggregatemetadata,
354 b'metadata': aggregatemetadata,
355 }
355 }
356 repo.hook(b'postfix', throw=True, **pycompat.strkwargs(hookargs))
356 repo.hook(b'postfix', throw=True, **pycompat.strkwargs(hookargs))
357
357
358
358
359 def cleanup(repo, replacements, wdirwritten):
359 def cleanup(repo, replacements, wdirwritten):
360 """Calls scmutil.cleanupnodes() with the given replacements.
360 """Calls scmutil.cleanupnodes() with the given replacements.
361
361
362 "replacements" is a dict from nodeid to nodeid, with one key and one value
362 "replacements" is a dict from nodeid to nodeid, with one key and one value
363 for every revision that was affected by fixing. This is slightly different
363 for every revision that was affected by fixing. This is slightly different
364 from cleanupnodes().
364 from cleanupnodes().
365
365
366 "wdirwritten" is a bool which tells whether the working copy was affected by
366 "wdirwritten" is a bool which tells whether the working copy was affected by
367 fixing, since it has no entry in "replacements".
367 fixing, since it has no entry in "replacements".
368
368
369 Useful as a hook point for extending "hg fix" with output summarizing the
369 Useful as a hook point for extending "hg fix" with output summarizing the
370 effects of the command, though we choose not to output anything here.
370 effects of the command, though we choose not to output anything here.
371 """
371 """
372 replacements = {
372 replacements = {
373 prec: [succ] for prec, succ in pycompat.iteritems(replacements)
373 prec: [succ] for prec, succ in pycompat.iteritems(replacements)
374 }
374 }
375 scmutil.cleanupnodes(repo, replacements, b'fix', fixphase=True)
375 scmutil.cleanupnodes(repo, replacements, b'fix', fixphase=True)
376
376
377
377
378 def getworkqueue(ui, repo, pats, opts, revstofix, basectxs):
378 def getworkqueue(ui, repo, pats, opts, revstofix, basectxs):
379 """Constructs the list of files to be fixed at specific revisions
379 """Constructs the list of files to be fixed at specific revisions
380
380
381 It is up to the caller how to consume the work items, and the only
381 It is up to the caller how to consume the work items, and the only
382 dependence between them is that replacement revisions must be committed in
382 dependence between them is that replacement revisions must be committed in
383 topological order. Each work item represents a file in the working copy or
383 topological order. Each work item represents a file in the working copy or
384 in some revision that should be fixed and written back to the working copy
384 in some revision that should be fixed and written back to the working copy
385 or into a replacement revision.
385 or into a replacement revision.
386
386
387 Work items for the same revision are grouped together, so that a worker
387 Work items for the same revision are grouped together, so that a worker
388 pool starting with the first N items in parallel is likely to finish the
388 pool starting with the first N items in parallel is likely to finish the
389 first revision's work before other revisions. This can allow us to write
389 first revision's work before other revisions. This can allow us to write
390 the result to disk and reduce memory footprint. At time of writing, the
390 the result to disk and reduce memory footprint. At time of writing, the
391 partition strategy in worker.py seems favorable to this. We also sort the
391 partition strategy in worker.py seems favorable to this. We also sort the
392 items by ascending revision number to match the order in which we commit
392 items by ascending revision number to match the order in which we commit
393 the fixes later.
393 the fixes later.
394 """
394 """
395 workqueue = []
395 workqueue = []
396 numitems = collections.defaultdict(int)
396 numitems = collections.defaultdict(int)
397 maxfilesize = ui.configbytes(b'fix', b'maxfilesize')
397 maxfilesize = ui.configbytes(b'fix', b'maxfilesize')
398 for rev in sorted(revstofix):
398 for rev in sorted(revstofix):
399 fixctx = repo[rev]
399 fixctx = repo[rev]
400 match = scmutil.match(fixctx, pats, opts)
400 match = scmutil.match(fixctx, pats, opts)
401 for path in sorted(
401 for path in sorted(
402 pathstofix(ui, repo, pats, opts, match, basectxs[rev], fixctx)
402 pathstofix(ui, repo, pats, opts, match, basectxs[rev], fixctx)
403 ):
403 ):
404 fctx = fixctx[path]
404 fctx = fixctx[path]
405 if fctx.islink():
405 if fctx.islink():
406 continue
406 continue
407 if fctx.size() > maxfilesize:
407 if fctx.size() > maxfilesize:
408 ui.warn(
408 ui.warn(
409 _(b'ignoring file larger than %s: %s\n')
409 _(b'ignoring file larger than %s: %s\n')
410 % (util.bytecount(maxfilesize), path)
410 % (util.bytecount(maxfilesize), path)
411 )
411 )
412 continue
412 continue
413 workqueue.append((rev, path))
413 workqueue.append((rev, path))
414 numitems[rev] += 1
414 numitems[rev] += 1
415 return workqueue, numitems
415 return workqueue, numitems
416
416
417
417
418 def getrevstofix(ui, repo, opts):
418 def getrevstofix(ui, repo, opts):
419 """Returns the set of revision numbers that should be fixed"""
419 """Returns the set of revision numbers that should be fixed"""
420 if opts[b'all']:
420 if opts[b'all']:
421 revs = repo.revs(b'(not public() and not obsolete()) or wdir()')
421 revs = repo.revs(b'(not public() and not obsolete()) or wdir()')
422 elif opts[b'source']:
422 elif opts[b'source']:
423 source_revs = scmutil.revrange(repo, opts[b'source'])
423 source_revs = scmutil.revrange(repo, opts[b'source'])
424 revs = set(repo.revs(b'(%ld::) - obsolete()', source_revs))
424 revs = set(repo.revs(b'(%ld::) - obsolete()', source_revs))
425 if wdirrev in source_revs:
425 if wdirrev in source_revs:
426 # `wdir()::` is currently empty, so manually add wdir
426 # `wdir()::` is currently empty, so manually add wdir
427 revs.add(wdirrev)
427 revs.add(wdirrev)
428 if repo[b'.'].rev() in revs:
428 if repo[b'.'].rev() in revs:
429 revs.add(wdirrev)
429 revs.add(wdirrev)
430 else:
430 else:
431 revs = set(scmutil.revrange(repo, opts[b'rev']))
431 revs = set(scmutil.revrange(repo, opts[b'rev']))
432 if opts.get(b'working_dir'):
432 if opts.get(b'working_dir'):
433 revs.add(wdirrev)
433 revs.add(wdirrev)
434 for rev in revs:
434 for rev in revs:
435 checkfixablectx(ui, repo, repo[rev])
435 checkfixablectx(ui, repo, repo[rev])
436 # Allow fixing only wdir() even if there's an unfinished operation
436 # Allow fixing only wdir() even if there's an unfinished operation
437 if not (len(revs) == 1 and wdirrev in revs):
437 if not (len(revs) == 1 and wdirrev in revs):
438 cmdutil.checkunfinished(repo)
438 cmdutil.checkunfinished(repo)
439 rewriteutil.precheck(repo, revs, b'fix')
439 rewriteutil.precheck(repo, revs, b'fix')
440 if (
440 if (
441 wdirrev in revs
441 wdirrev in revs
442 and mergestatemod.mergestate.read(repo).unresolvedcount()
442 and mergestatemod.mergestate.read(repo).unresolvedcount()
443 ):
443 ):
444 raise error.Abort(b'unresolved conflicts', hint=b"use 'hg resolve'")
444 raise error.Abort(b'unresolved conflicts', hint=b"use 'hg resolve'")
445 if not revs:
445 if not revs:
446 raise error.Abort(
446 raise error.Abort(
447 b'no changesets specified', hint=b'use --source or --working-dir'
447 b'no changesets specified', hint=b'use --source or --working-dir'
448 )
448 )
449 return revs
449 return revs
450
450
451
451
452 def checkfixablectx(ui, repo, ctx):
452 def checkfixablectx(ui, repo, ctx):
453 """Aborts if the revision shouldn't be replaced with a fixed one."""
453 """Aborts if the revision shouldn't be replaced with a fixed one."""
454 if ctx.obsolete():
454 if ctx.obsolete():
455 # It would be better to actually check if the revision has a successor.
455 # It would be better to actually check if the revision has a successor.
456 if not obsolete.isenabled(repo, obsolete.allowdivergenceopt):
456 if not obsolete.isenabled(repo, obsolete.allowdivergenceopt):
457 raise error.Abort(
457 raise error.Abort(
458 b'fixing obsolete revision could cause divergence'
458 b'fixing obsolete revision could cause divergence'
459 )
459 )
460
460
461
461
462 def pathstofix(ui, repo, pats, opts, match, basectxs, fixctx):
462 def pathstofix(ui, repo, pats, opts, match, basectxs, fixctx):
463 """Returns the set of files that should be fixed in a context
463 """Returns the set of files that should be fixed in a context
464
464
465 The result depends on the base contexts; we include any file that has
465 The result depends on the base contexts; we include any file that has
466 changed relative to any of the base contexts. Base contexts should be
466 changed relative to any of the base contexts. Base contexts should be
467 ancestors of the context being fixed.
467 ancestors of the context being fixed.
468 """
468 """
469 files = set()
469 files = set()
470 for basectx in basectxs:
470 for basectx in basectxs:
471 stat = basectx.status(
471 stat = basectx.status(
472 fixctx, match=match, listclean=bool(pats), listunknown=bool(pats)
472 fixctx, match=match, listclean=bool(pats), listunknown=bool(pats)
473 )
473 )
474 files.update(
474 files.update(
475 set(
475 set(
476 itertools.chain(
476 itertools.chain(
477 stat.added, stat.modified, stat.clean, stat.unknown
477 stat.added, stat.modified, stat.clean, stat.unknown
478 )
478 )
479 )
479 )
480 )
480 )
481 return files
481 return files
482
482
483
483
484 def lineranges(opts, path, basepaths, basectxs, fixctx, content2):
484 def lineranges(opts, path, basepaths, basectxs, fixctx, content2):
485 """Returns the set of line ranges that should be fixed in a file
485 """Returns the set of line ranges that should be fixed in a file
486
486
487 Of the form [(10, 20), (30, 40)].
487 Of the form [(10, 20), (30, 40)].
488
488
489 This depends on the given base contexts; we must consider lines that have
489 This depends on the given base contexts; we must consider lines that have
490 changed versus any of the base contexts, and whether the file has been
490 changed versus any of the base contexts, and whether the file has been
491 renamed versus any of them.
491 renamed versus any of them.
492
492
493 Another way to understand this is that we exclude line ranges that are
493 Another way to understand this is that we exclude line ranges that are
494 common to the file in all base contexts.
494 common to the file in all base contexts.
495 """
495 """
496 if opts.get(b'whole'):
496 if opts.get(b'whole'):
497 # Return a range containing all lines. Rely on the diff implementation's
497 # Return a range containing all lines. Rely on the diff implementation's
498 # idea of how many lines are in the file, instead of reimplementing it.
498 # idea of how many lines are in the file, instead of reimplementing it.
499 return difflineranges(b'', content2)
499 return difflineranges(b'', content2)
500
500
501 rangeslist = []
501 rangeslist = []
502 for basectx in basectxs:
502 for basectx in basectxs:
503 basepath = basepaths.get((basectx.rev(), fixctx.rev(), path), path)
503 basepath = basepaths.get((basectx.rev(), fixctx.rev(), path), path)
504
504
505 if basepath in basectx:
505 if basepath in basectx:
506 content1 = basectx[basepath].data()
506 content1 = basectx[basepath].data()
507 else:
507 else:
508 content1 = b''
508 content1 = b''
509 rangeslist.extend(difflineranges(content1, content2))
509 rangeslist.extend(difflineranges(content1, content2))
510 return unionranges(rangeslist)
510 return unionranges(rangeslist)
511
511
512
512
513 def getbasepaths(repo, opts, workqueue, basectxs):
513 def getbasepaths(repo, opts, workqueue, basectxs):
514 if opts.get(b'whole'):
514 if opts.get(b'whole'):
515 # Base paths will never be fetched for line range determination.
515 # Base paths will never be fetched for line range determination.
516 return {}
516 return {}
517
517
518 basepaths = {}
518 basepaths = {}
519 for rev, path in workqueue:
519 for rev, path in workqueue:
520 fixctx = repo[rev]
520 fixctx = repo[rev]
521 for basectx in basectxs[rev]:
521 for basectx in basectxs[rev]:
522 basepath = copies.pathcopies(basectx, fixctx).get(path, path)
522 basepath = copies.pathcopies(basectx, fixctx).get(path, path)
523 if basepath in basectx:
523 if basepath in basectx:
524 basepaths[(basectx.rev(), fixctx.rev(), path)] = basepath
524 basepaths[(basectx.rev(), fixctx.rev(), path)] = basepath
525 return basepaths
525 return basepaths
526
526
527
527
528 def unionranges(rangeslist):
528 def unionranges(rangeslist):
529 """Return the union of some closed intervals
529 """Return the union of some closed intervals
530
530
531 >>> unionranges([])
531 >>> unionranges([])
532 []
532 []
533 >>> unionranges([(1, 100)])
533 >>> unionranges([(1, 100)])
534 [(1, 100)]
534 [(1, 100)]
535 >>> unionranges([(1, 100), (1, 100)])
535 >>> unionranges([(1, 100), (1, 100)])
536 [(1, 100)]
536 [(1, 100)]
537 >>> unionranges([(1, 100), (2, 100)])
537 >>> unionranges([(1, 100), (2, 100)])
538 [(1, 100)]
538 [(1, 100)]
539 >>> unionranges([(1, 99), (1, 100)])
539 >>> unionranges([(1, 99), (1, 100)])
540 [(1, 100)]
540 [(1, 100)]
541 >>> unionranges([(1, 100), (40, 60)])
541 >>> unionranges([(1, 100), (40, 60)])
542 [(1, 100)]
542 [(1, 100)]
543 >>> unionranges([(1, 49), (50, 100)])
543 >>> unionranges([(1, 49), (50, 100)])
544 [(1, 100)]
544 [(1, 100)]
545 >>> unionranges([(1, 48), (50, 100)])
545 >>> unionranges([(1, 48), (50, 100)])
546 [(1, 48), (50, 100)]
546 [(1, 48), (50, 100)]
547 >>> unionranges([(1, 2), (3, 4), (5, 6)])
547 >>> unionranges([(1, 2), (3, 4), (5, 6)])
548 [(1, 6)]
548 [(1, 6)]
549 """
549 """
550 rangeslist = sorted(set(rangeslist))
550 rangeslist = sorted(set(rangeslist))
551 unioned = []
551 unioned = []
552 if rangeslist:
552 if rangeslist:
553 unioned, rangeslist = [rangeslist[0]], rangeslist[1:]
553 unioned, rangeslist = [rangeslist[0]], rangeslist[1:]
554 for a, b in rangeslist:
554 for a, b in rangeslist:
555 c, d = unioned[-1]
555 c, d = unioned[-1]
556 if a > d + 1:
556 if a > d + 1:
557 unioned.append((a, b))
557 unioned.append((a, b))
558 else:
558 else:
559 unioned[-1] = (c, max(b, d))
559 unioned[-1] = (c, max(b, d))
560 return unioned
560 return unioned
561
561
562
562
563 def difflineranges(content1, content2):
563 def difflineranges(content1, content2):
564 """Return list of line number ranges in content2 that differ from content1.
564 """Return list of line number ranges in content2 that differ from content1.
565
565
566 Line numbers are 1-based. The numbers are the first and last line contained
566 Line numbers are 1-based. The numbers are the first and last line contained
567 in the range. Single-line ranges have the same line number for the first and
567 in the range. Single-line ranges have the same line number for the first and
568 last line. Excludes any empty ranges that result from lines that are only
568 last line. Excludes any empty ranges that result from lines that are only
569 present in content1. Relies on mdiff's idea of where the line endings are in
569 present in content1. Relies on mdiff's idea of where the line endings are in
570 the string.
570 the string.
571
571
572 >>> from mercurial import pycompat
572 >>> from mercurial import pycompat
573 >>> lines = lambda s: b'\\n'.join([c for c in pycompat.iterbytestr(s)])
573 >>> lines = lambda s: b'\\n'.join([c for c in pycompat.iterbytestr(s)])
574 >>> difflineranges2 = lambda a, b: difflineranges(lines(a), lines(b))
574 >>> difflineranges2 = lambda a, b: difflineranges(lines(a), lines(b))
575 >>> difflineranges2(b'', b'')
575 >>> difflineranges2(b'', b'')
576 []
576 []
577 >>> difflineranges2(b'a', b'')
577 >>> difflineranges2(b'a', b'')
578 []
578 []
579 >>> difflineranges2(b'', b'A')
579 >>> difflineranges2(b'', b'A')
580 [(1, 1)]
580 [(1, 1)]
581 >>> difflineranges2(b'a', b'a')
581 >>> difflineranges2(b'a', b'a')
582 []
582 []
583 >>> difflineranges2(b'a', b'A')
583 >>> difflineranges2(b'a', b'A')
584 [(1, 1)]
584 [(1, 1)]
585 >>> difflineranges2(b'ab', b'')
585 >>> difflineranges2(b'ab', b'')
586 []
586 []
587 >>> difflineranges2(b'', b'AB')
587 >>> difflineranges2(b'', b'AB')
588 [(1, 2)]
588 [(1, 2)]
589 >>> difflineranges2(b'abc', b'ac')
589 >>> difflineranges2(b'abc', b'ac')
590 []
590 []
591 >>> difflineranges2(b'ab', b'aCb')
591 >>> difflineranges2(b'ab', b'aCb')
592 [(2, 2)]
592 [(2, 2)]
593 >>> difflineranges2(b'abc', b'aBc')
593 >>> difflineranges2(b'abc', b'aBc')
594 [(2, 2)]
594 [(2, 2)]
595 >>> difflineranges2(b'ab', b'AB')
595 >>> difflineranges2(b'ab', b'AB')
596 [(1, 2)]
596 [(1, 2)]
597 >>> difflineranges2(b'abcde', b'aBcDe')
597 >>> difflineranges2(b'abcde', b'aBcDe')
598 [(2, 2), (4, 4)]
598 [(2, 2), (4, 4)]
599 >>> difflineranges2(b'abcde', b'aBCDe')
599 >>> difflineranges2(b'abcde', b'aBCDe')
600 [(2, 4)]
600 [(2, 4)]
601 """
601 """
602 ranges = []
602 ranges = []
603 for lines, kind in mdiff.allblocks(content1, content2):
603 for lines, kind in mdiff.allblocks(content1, content2):
604 firstline, lastline = lines[2:4]
604 firstline, lastline = lines[2:4]
605 if kind == b'!' and firstline != lastline:
605 if kind == b'!' and firstline != lastline:
606 ranges.append((firstline + 1, lastline))
606 ranges.append((firstline + 1, lastline))
607 return ranges
607 return ranges
608
608
609
609
610 def getbasectxs(repo, opts, revstofix):
610 def getbasectxs(repo, opts, revstofix):
611 """Returns a map of the base contexts for each revision
611 """Returns a map of the base contexts for each revision
612
612
613 The base contexts determine which lines are considered modified when we
613 The base contexts determine which lines are considered modified when we
614 attempt to fix just the modified lines in a file. It also determines which
614 attempt to fix just the modified lines in a file. It also determines which
615 files we attempt to fix, so it is important to compute this even when
615 files we attempt to fix, so it is important to compute this even when
616 --whole is used.
616 --whole is used.
617 """
617 """
618 # The --base flag overrides the usual logic, and we give every revision
618 # The --base flag overrides the usual logic, and we give every revision
619 # exactly the set of baserevs that the user specified.
619 # exactly the set of baserevs that the user specified.
620 if opts.get(b'base'):
620 if opts.get(b'base'):
621 baserevs = set(scmutil.revrange(repo, opts.get(b'base')))
621 baserevs = set(scmutil.revrange(repo, opts.get(b'base')))
622 if not baserevs:
622 if not baserevs:
623 baserevs = {nullrev}
623 baserevs = {nullrev}
624 basectxs = {repo[rev] for rev in baserevs}
624 basectxs = {repo[rev] for rev in baserevs}
625 return {rev: basectxs for rev in revstofix}
625 return {rev: basectxs for rev in revstofix}
626
626
627 # Proceed in topological order so that we can easily determine each
627 # Proceed in topological order so that we can easily determine each
628 # revision's baserevs by looking at its parents and their baserevs.
628 # revision's baserevs by looking at its parents and their baserevs.
629 basectxs = collections.defaultdict(set)
629 basectxs = collections.defaultdict(set)
630 for rev in sorted(revstofix):
630 for rev in sorted(revstofix):
631 ctx = repo[rev]
631 ctx = repo[rev]
632 for pctx in ctx.parents():
632 for pctx in ctx.parents():
633 if pctx.rev() in basectxs:
633 if pctx.rev() in basectxs:
634 basectxs[rev].update(basectxs[pctx.rev()])
634 basectxs[rev].update(basectxs[pctx.rev()])
635 else:
635 else:
636 basectxs[rev].add(pctx)
636 basectxs[rev].add(pctx)
637 return basectxs
637 return basectxs
638
638
639
639
640 def _prefetchfiles(repo, workqueue, basepaths):
640 def _prefetchfiles(repo, workqueue, basepaths):
641 toprefetch = set()
641 toprefetch = set()
642
642
643 # Prefetch the files that will be fixed.
643 # Prefetch the files that will be fixed.
644 for rev, path in workqueue:
644 for rev, path in workqueue:
645 if rev == wdirrev:
645 if rev == wdirrev:
646 continue
646 continue
647 toprefetch.add((rev, path))
647 toprefetch.add((rev, path))
648
648
649 # Prefetch the base contents for lineranges().
649 # Prefetch the base contents for lineranges().
650 for (baserev, fixrev, path), basepath in basepaths.items():
650 for (baserev, fixrev, path), basepath in basepaths.items():
651 toprefetch.add((baserev, basepath))
651 toprefetch.add((baserev, basepath))
652
652
653 if toprefetch:
653 if toprefetch:
654 scmutil.prefetchfiles(
654 scmutil.prefetchfiles(
655 repo,
655 repo,
656 [
656 [
657 (rev, scmutil.matchfiles(repo, [path]))
657 (rev, scmutil.matchfiles(repo, [path]))
658 for rev, path in toprefetch
658 for rev, path in toprefetch
659 ],
659 ],
660 )
660 )
661
661
662
662
663 def fixfile(ui, repo, opts, fixers, fixctx, path, basepaths, basectxs):
663 def fixfile(ui, repo, opts, fixers, fixctx, path, basepaths, basectxs):
664 """Run any configured fixers that should affect the file in this context
664 """Run any configured fixers that should affect the file in this context
665
665
666 Returns the file content that results from applying the fixers in some order
666 Returns the file content that results from applying the fixers in some order
667 starting with the file's content in the fixctx. Fixers that support line
667 starting with the file's content in the fixctx. Fixers that support line
668 ranges will affect lines that have changed relative to any of the basectxs
668 ranges will affect lines that have changed relative to any of the basectxs
669 (i.e. they will only avoid lines that are common to all basectxs).
669 (i.e. they will only avoid lines that are common to all basectxs).
670
670
671 A fixer tool's stdout will become the file's new content if and only if it
671 A fixer tool's stdout will become the file's new content if and only if it
672 exits with code zero. The fixer tool's working directory is the repository's
672 exits with code zero. The fixer tool's working directory is the repository's
673 root.
673 root.
674 """
674 """
675 metadata = {}
675 metadata = {}
676 newdata = fixctx[path].data()
676 newdata = fixctx[path].data()
677 for fixername, fixer in pycompat.iteritems(fixers):
677 for fixername, fixer in pycompat.iteritems(fixers):
678 if fixer.affects(opts, fixctx, path):
678 if fixer.affects(opts, fixctx, path):
679 ranges = lineranges(
679 ranges = lineranges(
680 opts, path, basepaths, basectxs, fixctx, newdata
680 opts, path, basepaths, basectxs, fixctx, newdata
681 )
681 )
682 command = fixer.command(ui, path, ranges)
682 command = fixer.command(ui, path, ranges)
683 if command is None:
683 if command is None:
684 continue
684 continue
685 ui.debug(b'subprocess: %s\n' % (command,))
685 ui.debug(b'subprocess: %s\n' % (command,))
686 proc = subprocess.Popen(
686 proc = subprocess.Popen(
687 procutil.tonativestr(command),
687 procutil.tonativestr(command),
688 shell=True,
688 shell=True,
689 cwd=procutil.tonativestr(repo.root),
689 cwd=procutil.tonativestr(repo.root),
690 stdin=subprocess.PIPE,
690 stdin=subprocess.PIPE,
691 stdout=subprocess.PIPE,
691 stdout=subprocess.PIPE,
692 stderr=subprocess.PIPE,
692 stderr=subprocess.PIPE,
693 )
693 )
694 stdout, stderr = proc.communicate(newdata)
694 stdout, stderr = proc.communicate(newdata)
695 if stderr:
695 if stderr:
696 showstderr(ui, fixctx.rev(), fixername, stderr)
696 showstderr(ui, fixctx.rev(), fixername, stderr)
697 newerdata = stdout
697 newerdata = stdout
698 if fixer.shouldoutputmetadata():
698 if fixer.shouldoutputmetadata():
699 try:
699 try:
700 metadatajson, newerdata = stdout.split(b'\0', 1)
700 metadatajson, newerdata = stdout.split(b'\0', 1)
701 metadata[fixername] = pycompat.json_loads(metadatajson)
701 metadata[fixername] = pycompat.json_loads(metadatajson)
702 except ValueError:
702 except ValueError:
703 ui.warn(
703 ui.warn(
704 _(b'ignored invalid output from fixer tool: %s\n')
704 _(b'ignored invalid output from fixer tool: %s\n')
705 % (fixername,)
705 % (fixername,)
706 )
706 )
707 continue
707 continue
708 else:
708 else:
709 metadata[fixername] = None
709 metadata[fixername] = None
710 if proc.returncode == 0:
710 if proc.returncode == 0:
711 newdata = newerdata
711 newdata = newerdata
712 else:
712 else:
713 if not stderr:
713 if not stderr:
714 message = _(b'exited with status %d\n') % (proc.returncode,)
714 message = _(b'exited with status %d\n') % (proc.returncode,)
715 showstderr(ui, fixctx.rev(), fixername, message)
715 showstderr(ui, fixctx.rev(), fixername, message)
716 checktoolfailureaction(
716 checktoolfailureaction(
717 ui,
717 ui,
718 _(b'no fixes will be applied'),
718 _(b'no fixes will be applied'),
719 hint=_(
719 hint=_(
720 b'use --config fix.failure=continue to apply any '
720 b'use --config fix.failure=continue to apply any '
721 b'successful fixes anyway'
721 b'successful fixes anyway'
722 ),
722 ),
723 )
723 )
724 return metadata, newdata
724 return metadata, newdata
725
725
726
726
727 def showstderr(ui, rev, fixername, stderr):
727 def showstderr(ui, rev, fixername, stderr):
728 """Writes the lines of the stderr string as warnings on the ui
728 """Writes the lines of the stderr string as warnings on the ui
729
729
730 Uses the revision number and fixername to give more context to each line of
730 Uses the revision number and fixername to give more context to each line of
731 the error message. Doesn't include file names, since those take up a lot of
731 the error message. Doesn't include file names, since those take up a lot of
732 space and would tend to be included in the error message if they were
732 space and would tend to be included in the error message if they were
733 relevant.
733 relevant.
734 """
734 """
735 for line in re.split(b'[\r\n]+', stderr):
735 for line in re.split(b'[\r\n]+', stderr):
736 if line:
736 if line:
737 ui.warn(b'[')
737 ui.warn(b'[')
738 if rev is None:
738 if rev is None:
739 ui.warn(_(b'wdir'), label=b'evolve.rev')
739 ui.warn(_(b'wdir'), label=b'evolve.rev')
740 else:
740 else:
741 ui.warn(b'%d' % rev, label=b'evolve.rev')
741 ui.warn(b'%d' % rev, label=b'evolve.rev')
742 ui.warn(b'] %s: %s\n' % (fixername, line))
742 ui.warn(b'] %s: %s\n' % (fixername, line))
743
743
744
744
745 def writeworkingdir(repo, ctx, filedata, replacements):
745 def writeworkingdir(repo, ctx, filedata, replacements):
746 """Write new content to the working copy and check out the new p1 if any
746 """Write new content to the working copy and check out the new p1 if any
747
747
748 We check out a new revision if and only if we fixed something in both the
748 We check out a new revision if and only if we fixed something in both the
749 working directory and its parent revision. This avoids the need for a full
749 working directory and its parent revision. This avoids the need for a full
750 update/merge, and means that the working directory simply isn't affected
750 update/merge, and means that the working directory simply isn't affected
751 unless the --working-dir flag is given.
751 unless the --working-dir flag is given.
752
752
753 Directly updates the dirstate for the affected files.
753 Directly updates the dirstate for the affected files.
754 """
754 """
755 assert repo.dirstate.p2() == nullid
756
757 for path, data in pycompat.iteritems(filedata):
755 for path, data in pycompat.iteritems(filedata):
758 fctx = ctx[path]
756 fctx = ctx[path]
759 fctx.write(data, fctx.flags())
757 fctx.write(data, fctx.flags())
760
758
761 oldp1 = repo.dirstate.p1()
759 oldp1 = repo.dirstate.p1()
762 newp1 = replacements.get(oldp1, oldp1)
760 newp1 = replacements.get(oldp1, oldp1)
763 if newp1 != oldp1:
761 if newp1 != oldp1:
762 assert repo.dirstate.p2() == nullid
764 with repo.dirstate.parentchange():
763 with repo.dirstate.parentchange():
765 scmutil.movedirstate(repo, repo[newp1])
764 scmutil.movedirstate(repo, repo[newp1])
766
765
767
766
768 def replacerev(ui, repo, ctx, filedata, replacements):
767 def replacerev(ui, repo, ctx, filedata, replacements):
769 """Commit a new revision like the given one, but with file content changes
768 """Commit a new revision like the given one, but with file content changes
770
769
771 "ctx" is the original revision to be replaced by a modified one.
770 "ctx" is the original revision to be replaced by a modified one.
772
771
773 "filedata" is a dict that maps paths to their new file content. All other
772 "filedata" is a dict that maps paths to their new file content. All other
774 paths will be recreated from the original revision without changes.
773 paths will be recreated from the original revision without changes.
775 "filedata" may contain paths that didn't exist in the original revision;
774 "filedata" may contain paths that didn't exist in the original revision;
776 they will be added.
775 they will be added.
777
776
778 "replacements" is a dict that maps a single node to a single node, and it is
777 "replacements" is a dict that maps a single node to a single node, and it is
779 updated to indicate the original revision is replaced by the newly created
778 updated to indicate the original revision is replaced by the newly created
780 one. No entry is added if the replacement's node already exists.
779 one. No entry is added if the replacement's node already exists.
781
780
782 The new revision has the same parents as the old one, unless those parents
781 The new revision has the same parents as the old one, unless those parents
783 have already been replaced, in which case those replacements are the parents
782 have already been replaced, in which case those replacements are the parents
784 of this new revision. Thus, if revisions are replaced in topological order,
783 of this new revision. Thus, if revisions are replaced in topological order,
785 there is no need to rebase them into the original topology later.
784 there is no need to rebase them into the original topology later.
786 """
785 """
787
786
788 p1rev, p2rev = repo.changelog.parentrevs(ctx.rev())
787 p1rev, p2rev = repo.changelog.parentrevs(ctx.rev())
789 p1ctx, p2ctx = repo[p1rev], repo[p2rev]
788 p1ctx, p2ctx = repo[p1rev], repo[p2rev]
790 newp1node = replacements.get(p1ctx.node(), p1ctx.node())
789 newp1node = replacements.get(p1ctx.node(), p1ctx.node())
791 newp2node = replacements.get(p2ctx.node(), p2ctx.node())
790 newp2node = replacements.get(p2ctx.node(), p2ctx.node())
792
791
793 # We don't want to create a revision that has no changes from the original,
792 # We don't want to create a revision that has no changes from the original,
794 # but we should if the original revision's parent has been replaced.
793 # but we should if the original revision's parent has been replaced.
795 # Otherwise, we would produce an orphan that needs no actual human
794 # Otherwise, we would produce an orphan that needs no actual human
796 # intervention to evolve. We can't rely on commit() to avoid creating the
795 # intervention to evolve. We can't rely on commit() to avoid creating the
797 # un-needed revision because the extra field added below produces a new hash
796 # un-needed revision because the extra field added below produces a new hash
798 # regardless of file content changes.
797 # regardless of file content changes.
799 if (
798 if (
800 not filedata
799 not filedata
801 and p1ctx.node() not in replacements
800 and p1ctx.node() not in replacements
802 and p2ctx.node() not in replacements
801 and p2ctx.node() not in replacements
803 ):
802 ):
804 return
803 return
805
804
806 extra = ctx.extra().copy()
805 extra = ctx.extra().copy()
807 extra[b'fix_source'] = ctx.hex()
806 extra[b'fix_source'] = ctx.hex()
808
807
809 wctx = context.overlayworkingctx(repo)
808 wctx = context.overlayworkingctx(repo)
810 wctx.setbase(repo[newp1node])
809 wctx.setbase(repo[newp1node])
811 merge.revert_to(ctx, wc=wctx)
810 merge.revert_to(ctx, wc=wctx)
812 copies.graftcopies(wctx, ctx, ctx.p1())
811 copies.graftcopies(wctx, ctx, ctx.p1())
813
812
814 for path in filedata.keys():
813 for path in filedata.keys():
815 fctx = ctx[path]
814 fctx = ctx[path]
816 copysource = fctx.copysource()
815 copysource = fctx.copysource()
817 wctx.write(path, filedata[path], flags=fctx.flags())
816 wctx.write(path, filedata[path], flags=fctx.flags())
818 if copysource:
817 if copysource:
819 wctx.markcopied(path, copysource)
818 wctx.markcopied(path, copysource)
820
819
821 desc = rewriteutil.update_hash_refs(
820 desc = rewriteutil.update_hash_refs(
822 repo,
821 repo,
823 ctx.description(),
822 ctx.description(),
824 {oldnode: [newnode] for oldnode, newnode in replacements.items()},
823 {oldnode: [newnode] for oldnode, newnode in replacements.items()},
825 )
824 )
826
825
827 memctx = wctx.tomemctx(
826 memctx = wctx.tomemctx(
828 text=desc,
827 text=desc,
829 branch=ctx.branch(),
828 branch=ctx.branch(),
830 extra=extra,
829 extra=extra,
831 date=ctx.date(),
830 date=ctx.date(),
832 parents=(newp1node, newp2node),
831 parents=(newp1node, newp2node),
833 user=ctx.user(),
832 user=ctx.user(),
834 )
833 )
835
834
836 sucnode = memctx.commit()
835 sucnode = memctx.commit()
837 prenode = ctx.node()
836 prenode = ctx.node()
838 if prenode == sucnode:
837 if prenode == sucnode:
839 ui.debug(b'node %s already existed\n' % (ctx.hex()))
838 ui.debug(b'node %s already existed\n' % (ctx.hex()))
840 else:
839 else:
841 replacements[ctx.node()] = sucnode
840 replacements[ctx.node()] = sucnode
842
841
843
842
844 def getfixers(ui):
843 def getfixers(ui):
845 """Returns a map of configured fixer tools indexed by their names
844 """Returns a map of configured fixer tools indexed by their names
846
845
847 Each value is a Fixer object with methods that implement the behavior of the
846 Each value is a Fixer object with methods that implement the behavior of the
848 fixer's config suboptions. Does not validate the config values.
847 fixer's config suboptions. Does not validate the config values.
849 """
848 """
850 fixers = {}
849 fixers = {}
851 for name in fixernames(ui):
850 for name in fixernames(ui):
852 enabled = ui.configbool(b'fix', name + b':enabled')
851 enabled = ui.configbool(b'fix', name + b':enabled')
853 command = ui.config(b'fix', name + b':command')
852 command = ui.config(b'fix', name + b':command')
854 pattern = ui.config(b'fix', name + b':pattern')
853 pattern = ui.config(b'fix', name + b':pattern')
855 linerange = ui.config(b'fix', name + b':linerange')
854 linerange = ui.config(b'fix', name + b':linerange')
856 priority = ui.configint(b'fix', name + b':priority')
855 priority = ui.configint(b'fix', name + b':priority')
857 metadata = ui.configbool(b'fix', name + b':metadata')
856 metadata = ui.configbool(b'fix', name + b':metadata')
858 skipclean = ui.configbool(b'fix', name + b':skipclean')
857 skipclean = ui.configbool(b'fix', name + b':skipclean')
859 # Don't use a fixer if it has no pattern configured. It would be
858 # Don't use a fixer if it has no pattern configured. It would be
860 # dangerous to let it affect all files. It would be pointless to let it
859 # dangerous to let it affect all files. It would be pointless to let it
861 # affect no files. There is no reasonable subset of files to use as the
860 # affect no files. There is no reasonable subset of files to use as the
862 # default.
861 # default.
863 if command is None:
862 if command is None:
864 ui.warn(
863 ui.warn(
865 _(b'fixer tool has no command configuration: %s\n') % (name,)
864 _(b'fixer tool has no command configuration: %s\n') % (name,)
866 )
865 )
867 elif pattern is None:
866 elif pattern is None:
868 ui.warn(
867 ui.warn(
869 _(b'fixer tool has no pattern configuration: %s\n') % (name,)
868 _(b'fixer tool has no pattern configuration: %s\n') % (name,)
870 )
869 )
871 elif not enabled:
870 elif not enabled:
872 ui.debug(b'ignoring disabled fixer tool: %s\n' % (name,))
871 ui.debug(b'ignoring disabled fixer tool: %s\n' % (name,))
873 else:
872 else:
874 fixers[name] = Fixer(
873 fixers[name] = Fixer(
875 command, pattern, linerange, priority, metadata, skipclean
874 command, pattern, linerange, priority, metadata, skipclean
876 )
875 )
877 return collections.OrderedDict(
876 return collections.OrderedDict(
878 sorted(fixers.items(), key=lambda item: item[1]._priority, reverse=True)
877 sorted(fixers.items(), key=lambda item: item[1]._priority, reverse=True)
879 )
878 )
880
879
881
880
882 def fixernames(ui):
881 def fixernames(ui):
883 """Returns the names of [fix] config options that have suboptions"""
882 """Returns the names of [fix] config options that have suboptions"""
884 names = set()
883 names = set()
885 for k, v in ui.configitems(b'fix'):
884 for k, v in ui.configitems(b'fix'):
886 if b':' in k:
885 if b':' in k:
887 names.add(k.split(b':', 1)[0])
886 names.add(k.split(b':', 1)[0])
888 return names
887 return names
889
888
890
889
891 class Fixer(object):
890 class Fixer(object):
892 """Wraps the raw config values for a fixer with methods"""
891 """Wraps the raw config values for a fixer with methods"""
893
892
894 def __init__(
893 def __init__(
895 self, command, pattern, linerange, priority, metadata, skipclean
894 self, command, pattern, linerange, priority, metadata, skipclean
896 ):
895 ):
897 self._command = command
896 self._command = command
898 self._pattern = pattern
897 self._pattern = pattern
899 self._linerange = linerange
898 self._linerange = linerange
900 self._priority = priority
899 self._priority = priority
901 self._metadata = metadata
900 self._metadata = metadata
902 self._skipclean = skipclean
901 self._skipclean = skipclean
903
902
904 def affects(self, opts, fixctx, path):
903 def affects(self, opts, fixctx, path):
905 """Should this fixer run on the file at the given path and context?"""
904 """Should this fixer run on the file at the given path and context?"""
906 repo = fixctx.repo()
905 repo = fixctx.repo()
907 matcher = matchmod.match(
906 matcher = matchmod.match(
908 repo.root, repo.root, [self._pattern], ctx=fixctx
907 repo.root, repo.root, [self._pattern], ctx=fixctx
909 )
908 )
910 return matcher(path)
909 return matcher(path)
911
910
912 def shouldoutputmetadata(self):
911 def shouldoutputmetadata(self):
913 """Should the stdout of this fixer start with JSON and a null byte?"""
912 """Should the stdout of this fixer start with JSON and a null byte?"""
914 return self._metadata
913 return self._metadata
915
914
916 def command(self, ui, path, ranges):
915 def command(self, ui, path, ranges):
917 """A shell command to use to invoke this fixer on the given file/lines
916 """A shell command to use to invoke this fixer on the given file/lines
918
917
919 May return None if there is no appropriate command to run for the given
918 May return None if there is no appropriate command to run for the given
920 parameters.
919 parameters.
921 """
920 """
922 expand = cmdutil.rendercommandtemplate
921 expand = cmdutil.rendercommandtemplate
923 parts = [
922 parts = [
924 expand(
923 expand(
925 ui,
924 ui,
926 self._command,
925 self._command,
927 {b'rootpath': path, b'basename': os.path.basename(path)},
926 {b'rootpath': path, b'basename': os.path.basename(path)},
928 )
927 )
929 ]
928 ]
930 if self._linerange:
929 if self._linerange:
931 if self._skipclean and not ranges:
930 if self._skipclean and not ranges:
932 # No line ranges to fix, so don't run the fixer.
931 # No line ranges to fix, so don't run the fixer.
933 return None
932 return None
934 for first, last in ranges:
933 for first, last in ranges:
935 parts.append(
934 parts.append(
936 expand(
935 expand(
937 ui, self._linerange, {b'first': first, b'last': last}
936 ui, self._linerange, {b'first': first, b'last': last}
938 )
937 )
939 )
938 )
940 return b' '.join(parts)
939 return b' '.join(parts)
@@ -1,1715 +1,1754 b''
1 A script that implements uppercasing of specific lines in a file. This
1 A script that implements uppercasing of specific lines in a file. This
2 approximates the behavior of code formatters well enough for our tests.
2 approximates the behavior of code formatters well enough for our tests.
3
3
4 $ UPPERCASEPY="$TESTTMP/uppercase.py"
4 $ UPPERCASEPY="$TESTTMP/uppercase.py"
5 $ cat > $UPPERCASEPY <<EOF
5 $ cat > $UPPERCASEPY <<EOF
6 > import re
6 > import re
7 > import sys
7 > import sys
8 > from mercurial.utils.procutil import setbinary
8 > from mercurial.utils.procutil import setbinary
9 > setbinary(sys.stdin)
9 > setbinary(sys.stdin)
10 > setbinary(sys.stdout)
10 > setbinary(sys.stdout)
11 > stdin = getattr(sys.stdin, 'buffer', sys.stdin)
11 > stdin = getattr(sys.stdin, 'buffer', sys.stdin)
12 > stdout = getattr(sys.stdout, 'buffer', sys.stdout)
12 > stdout = getattr(sys.stdout, 'buffer', sys.stdout)
13 > lines = set()
13 > lines = set()
14 > def format(text):
14 > def format(text):
15 > return re.sub(b' +', b' ', text.upper())
15 > return re.sub(b' +', b' ', text.upper())
16 > for arg in sys.argv[1:]:
16 > for arg in sys.argv[1:]:
17 > if arg == 'all':
17 > if arg == 'all':
18 > stdout.write(format(stdin.read()))
18 > stdout.write(format(stdin.read()))
19 > sys.exit(0)
19 > sys.exit(0)
20 > else:
20 > else:
21 > first, last = arg.split('-')
21 > first, last = arg.split('-')
22 > lines.update(range(int(first), int(last) + 1))
22 > lines.update(range(int(first), int(last) + 1))
23 > for i, line in enumerate(stdin.readlines()):
23 > for i, line in enumerate(stdin.readlines()):
24 > if i + 1 in lines:
24 > if i + 1 in lines:
25 > stdout.write(format(line))
25 > stdout.write(format(line))
26 > else:
26 > else:
27 > stdout.write(line)
27 > stdout.write(line)
28 > EOF
28 > EOF
29 $ TESTLINES="foo\nbar\nbaz\nqux\n"
29 $ TESTLINES="foo\nbar\nbaz\nqux\n"
30 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY
30 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY
31 foo
31 foo
32 bar
32 bar
33 baz
33 baz
34 qux
34 qux
35 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY all
35 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY all
36 FOO
36 FOO
37 BAR
37 BAR
38 BAZ
38 BAZ
39 QUX
39 QUX
40 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 1-1
40 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 1-1
41 FOO
41 FOO
42 bar
42 bar
43 baz
43 baz
44 qux
44 qux
45 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 1-2
45 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 1-2
46 FOO
46 FOO
47 BAR
47 BAR
48 baz
48 baz
49 qux
49 qux
50 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 2-3
50 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 2-3
51 foo
51 foo
52 BAR
52 BAR
53 BAZ
53 BAZ
54 qux
54 qux
55 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 2-2 4-4
55 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 2-2 4-4
56 foo
56 foo
57 BAR
57 BAR
58 baz
58 baz
59 QUX
59 QUX
60
60
61 Set up the config with two simple fixers: one that fixes specific line ranges,
61 Set up the config with two simple fixers: one that fixes specific line ranges,
62 and one that always fixes the whole file. They both "fix" files by converting
62 and one that always fixes the whole file. They both "fix" files by converting
63 letters to uppercase. They use different file extensions, so each test case can
63 letters to uppercase. They use different file extensions, so each test case can
64 choose which behavior to use by naming files.
64 choose which behavior to use by naming files.
65
65
66 $ cat >> $HGRCPATH <<EOF
66 $ cat >> $HGRCPATH <<EOF
67 > [extensions]
67 > [extensions]
68 > fix =
68 > fix =
69 > [experimental]
69 > [experimental]
70 > evolution.createmarkers=True
70 > evolution.createmarkers=True
71 > evolution.allowunstable=True
71 > evolution.allowunstable=True
72 > [fix]
72 > [fix]
73 > uppercase-whole-file:command="$PYTHON" $UPPERCASEPY all
73 > uppercase-whole-file:command="$PYTHON" $UPPERCASEPY all
74 > uppercase-whole-file:pattern=set:**.whole
74 > uppercase-whole-file:pattern=set:**.whole
75 > uppercase-changed-lines:command="$PYTHON" $UPPERCASEPY
75 > uppercase-changed-lines:command="$PYTHON" $UPPERCASEPY
76 > uppercase-changed-lines:linerange={first}-{last}
76 > uppercase-changed-lines:linerange={first}-{last}
77 > uppercase-changed-lines:pattern=set:**.changed
77 > uppercase-changed-lines:pattern=set:**.changed
78 > EOF
78 > EOF
79
79
80 Help text for fix.
80 Help text for fix.
81
81
82 $ hg help fix
82 $ hg help fix
83 hg fix [OPTION]... [FILE]...
83 hg fix [OPTION]... [FILE]...
84
84
85 rewrite file content in changesets or working directory
85 rewrite file content in changesets or working directory
86
86
87 Runs any configured tools to fix the content of files. Only affects files
87 Runs any configured tools to fix the content of files. Only affects files
88 with changes, unless file arguments are provided. Only affects changed
88 with changes, unless file arguments are provided. Only affects changed
89 lines of files, unless the --whole flag is used. Some tools may always
89 lines of files, unless the --whole flag is used. Some tools may always
90 affect the whole file regardless of --whole.
90 affect the whole file regardless of --whole.
91
91
92 If --working-dir is used, files with uncommitted changes in the working
92 If --working-dir is used, files with uncommitted changes in the working
93 copy will be fixed. Note that no backup are made.
93 copy will be fixed. Note that no backup are made.
94
94
95 If revisions are specified with --source, those revisions and their
95 If revisions are specified with --source, those revisions and their
96 descendants will be checked, and they may be replaced with new revisions
96 descendants will be checked, and they may be replaced with new revisions
97 that have fixed file content. By automatically including the descendants,
97 that have fixed file content. By automatically including the descendants,
98 no merging, rebasing, or evolution will be required. If an ancestor of the
98 no merging, rebasing, or evolution will be required. If an ancestor of the
99 working copy is included, then the working copy itself will also be fixed,
99 working copy is included, then the working copy itself will also be fixed,
100 and the working copy will be updated to the fixed parent.
100 and the working copy will be updated to the fixed parent.
101
101
102 When determining what lines of each file to fix at each revision, the
102 When determining what lines of each file to fix at each revision, the
103 whole set of revisions being fixed is considered, so that fixes to earlier
103 whole set of revisions being fixed is considered, so that fixes to earlier
104 revisions are not forgotten in later ones. The --base flag can be used to
104 revisions are not forgotten in later ones. The --base flag can be used to
105 override this default behavior, though it is not usually desirable to do
105 override this default behavior, though it is not usually desirable to do
106 so.
106 so.
107
107
108 (use 'hg help -e fix' to show help for the fix extension)
108 (use 'hg help -e fix' to show help for the fix extension)
109
109
110 options ([+] can be repeated):
110 options ([+] can be repeated):
111
111
112 --all fix all non-public non-obsolete revisions
112 --all fix all non-public non-obsolete revisions
113 --base REV [+] revisions to diff against (overrides automatic selection,
113 --base REV [+] revisions to diff against (overrides automatic selection,
114 and applies to every revision being fixed)
114 and applies to every revision being fixed)
115 -s --source REV [+] fix the specified revisions and their descendants
115 -s --source REV [+] fix the specified revisions and their descendants
116 -w --working-dir fix the working directory
116 -w --working-dir fix the working directory
117 --whole always fix every line of a file
117 --whole always fix every line of a file
118
118
119 (some details hidden, use --verbose to show complete help)
119 (some details hidden, use --verbose to show complete help)
120
120
121 $ hg help -e fix
121 $ hg help -e fix
122 fix extension - rewrite file content in changesets or working copy
122 fix extension - rewrite file content in changesets or working copy
123 (EXPERIMENTAL)
123 (EXPERIMENTAL)
124
124
125 Provides a command that runs configured tools on the contents of modified
125 Provides a command that runs configured tools on the contents of modified
126 files, writing back any fixes to the working copy or replacing changesets.
126 files, writing back any fixes to the working copy or replacing changesets.
127
127
128 Here is an example configuration that causes 'hg fix' to apply automatic
128 Here is an example configuration that causes 'hg fix' to apply automatic
129 formatting fixes to modified lines in C++ code:
129 formatting fixes to modified lines in C++ code:
130
130
131 [fix]
131 [fix]
132 clang-format:command=clang-format --assume-filename={rootpath}
132 clang-format:command=clang-format --assume-filename={rootpath}
133 clang-format:linerange=--lines={first}:{last}
133 clang-format:linerange=--lines={first}:{last}
134 clang-format:pattern=set:**.cpp or **.hpp
134 clang-format:pattern=set:**.cpp or **.hpp
135
135
136 The :command suboption forms the first part of the shell command that will be
136 The :command suboption forms the first part of the shell command that will be
137 used to fix a file. The content of the file is passed on standard input, and
137 used to fix a file. The content of the file is passed on standard input, and
138 the fixed file content is expected on standard output. Any output on standard
138 the fixed file content is expected on standard output. Any output on standard
139 error will be displayed as a warning. If the exit status is not zero, the file
139 error will be displayed as a warning. If the exit status is not zero, the file
140 will not be affected. A placeholder warning is displayed if there is a non-
140 will not be affected. A placeholder warning is displayed if there is a non-
141 zero exit status but no standard error output. Some values may be substituted
141 zero exit status but no standard error output. Some values may be substituted
142 into the command:
142 into the command:
143
143
144 {rootpath} The path of the file being fixed, relative to the repo root
144 {rootpath} The path of the file being fixed, relative to the repo root
145 {basename} The name of the file being fixed, without the directory path
145 {basename} The name of the file being fixed, without the directory path
146
146
147 If the :linerange suboption is set, the tool will only be run if there are
147 If the :linerange suboption is set, the tool will only be run if there are
148 changed lines in a file. The value of this suboption is appended to the shell
148 changed lines in a file. The value of this suboption is appended to the shell
149 command once for every range of changed lines in the file. Some values may be
149 command once for every range of changed lines in the file. Some values may be
150 substituted into the command:
150 substituted into the command:
151
151
152 {first} The 1-based line number of the first line in the modified range
152 {first} The 1-based line number of the first line in the modified range
153 {last} The 1-based line number of the last line in the modified range
153 {last} The 1-based line number of the last line in the modified range
154
154
155 Deleted sections of a file will be ignored by :linerange, because there is no
155 Deleted sections of a file will be ignored by :linerange, because there is no
156 corresponding line range in the version being fixed.
156 corresponding line range in the version being fixed.
157
157
158 By default, tools that set :linerange will only be executed if there is at
158 By default, tools that set :linerange will only be executed if there is at
159 least one changed line range. This is meant to prevent accidents like running
159 least one changed line range. This is meant to prevent accidents like running
160 a code formatter in such a way that it unexpectedly reformats the whole file.
160 a code formatter in such a way that it unexpectedly reformats the whole file.
161 If such a tool needs to operate on unchanged files, it should set the
161 If such a tool needs to operate on unchanged files, it should set the
162 :skipclean suboption to false.
162 :skipclean suboption to false.
163
163
164 The :pattern suboption determines which files will be passed through each
164 The :pattern suboption determines which files will be passed through each
165 configured tool. See 'hg help patterns' for possible values. However, all
165 configured tool. See 'hg help patterns' for possible values. However, all
166 patterns are relative to the repo root, even if that text says they are
166 patterns are relative to the repo root, even if that text says they are
167 relative to the current working directory. If there are file arguments to 'hg
167 relative to the current working directory. If there are file arguments to 'hg
168 fix', the intersection of these patterns is used.
168 fix', the intersection of these patterns is used.
169
169
170 There is also a configurable limit for the maximum size of file that will be
170 There is also a configurable limit for the maximum size of file that will be
171 processed by 'hg fix':
171 processed by 'hg fix':
172
172
173 [fix]
173 [fix]
174 maxfilesize = 2MB
174 maxfilesize = 2MB
175
175
176 Normally, execution of configured tools will continue after a failure
176 Normally, execution of configured tools will continue after a failure
177 (indicated by a non-zero exit status). It can also be configured to abort
177 (indicated by a non-zero exit status). It can also be configured to abort
178 after the first such failure, so that no files will be affected if any tool
178 after the first such failure, so that no files will be affected if any tool
179 fails. This abort will also cause 'hg fix' to exit with a non-zero status:
179 fails. This abort will also cause 'hg fix' to exit with a non-zero status:
180
180
181 [fix]
181 [fix]
182 failure = abort
182 failure = abort
183
183
184 When multiple tools are configured to affect a file, they execute in an order
184 When multiple tools are configured to affect a file, they execute in an order
185 defined by the :priority suboption. The priority suboption has a default value
185 defined by the :priority suboption. The priority suboption has a default value
186 of zero for each tool. Tools are executed in order of descending priority. The
186 of zero for each tool. Tools are executed in order of descending priority. The
187 execution order of tools with equal priority is unspecified. For example, you
187 execution order of tools with equal priority is unspecified. For example, you
188 could use the 'sort' and 'head' utilities to keep only the 10 smallest numbers
188 could use the 'sort' and 'head' utilities to keep only the 10 smallest numbers
189 in a text file by ensuring that 'sort' runs before 'head':
189 in a text file by ensuring that 'sort' runs before 'head':
190
190
191 [fix]
191 [fix]
192 sort:command = sort -n
192 sort:command = sort -n
193 head:command = head -n 10
193 head:command = head -n 10
194 sort:pattern = numbers.txt
194 sort:pattern = numbers.txt
195 head:pattern = numbers.txt
195 head:pattern = numbers.txt
196 sort:priority = 2
196 sort:priority = 2
197 head:priority = 1
197 head:priority = 1
198
198
199 To account for changes made by each tool, the line numbers used for
199 To account for changes made by each tool, the line numbers used for
200 incremental formatting are recomputed before executing the next tool. So, each
200 incremental formatting are recomputed before executing the next tool. So, each
201 tool may see different values for the arguments added by the :linerange
201 tool may see different values for the arguments added by the :linerange
202 suboption.
202 suboption.
203
203
204 Each fixer tool is allowed to return some metadata in addition to the fixed
204 Each fixer tool is allowed to return some metadata in addition to the fixed
205 file content. The metadata must be placed before the file content on stdout,
205 file content. The metadata must be placed before the file content on stdout,
206 separated from the file content by a zero byte. The metadata is parsed as a
206 separated from the file content by a zero byte. The metadata is parsed as a
207 JSON value (so, it should be UTF-8 encoded and contain no zero bytes). A fixer
207 JSON value (so, it should be UTF-8 encoded and contain no zero bytes). A fixer
208 tool is expected to produce this metadata encoding if and only if the
208 tool is expected to produce this metadata encoding if and only if the
209 :metadata suboption is true:
209 :metadata suboption is true:
210
210
211 [fix]
211 [fix]
212 tool:command = tool --prepend-json-metadata
212 tool:command = tool --prepend-json-metadata
213 tool:metadata = true
213 tool:metadata = true
214
214
215 The metadata values are passed to hooks, which can be used to print summaries
215 The metadata values are passed to hooks, which can be used to print summaries
216 or perform other post-fixing work. The supported hooks are:
216 or perform other post-fixing work. The supported hooks are:
217
217
218 "postfixfile"
218 "postfixfile"
219 Run once for each file in each revision where any fixer tools made changes
219 Run once for each file in each revision where any fixer tools made changes
220 to the file content. Provides "$HG_REV" and "$HG_PATH" to identify the file,
220 to the file content. Provides "$HG_REV" and "$HG_PATH" to identify the file,
221 and "$HG_METADATA" with a map of fixer names to metadata values from fixer
221 and "$HG_METADATA" with a map of fixer names to metadata values from fixer
222 tools that affected the file. Fixer tools that didn't affect the file have a
222 tools that affected the file. Fixer tools that didn't affect the file have a
223 value of None. Only fixer tools that executed are present in the metadata.
223 value of None. Only fixer tools that executed are present in the metadata.
224
224
225 "postfix"
225 "postfix"
226 Run once after all files and revisions have been handled. Provides
226 Run once after all files and revisions have been handled. Provides
227 "$HG_REPLACEMENTS" with information about what revisions were created and
227 "$HG_REPLACEMENTS" with information about what revisions were created and
228 made obsolete. Provides a boolean "$HG_WDIRWRITTEN" to indicate whether any
228 made obsolete. Provides a boolean "$HG_WDIRWRITTEN" to indicate whether any
229 files in the working copy were updated. Provides a list "$HG_METADATA"
229 files in the working copy were updated. Provides a list "$HG_METADATA"
230 mapping fixer tool names to lists of metadata values returned from
230 mapping fixer tool names to lists of metadata values returned from
231 executions that modified a file. This aggregates the same metadata
231 executions that modified a file. This aggregates the same metadata
232 previously passed to the "postfixfile" hook.
232 previously passed to the "postfixfile" hook.
233
233
234 Fixer tools are run in the repository's root directory. This allows them to
234 Fixer tools are run in the repository's root directory. This allows them to
235 read configuration files from the working copy, or even write to the working
235 read configuration files from the working copy, or even write to the working
236 copy. The working copy is not updated to match the revision being fixed. In
236 copy. The working copy is not updated to match the revision being fixed. In
237 fact, several revisions may be fixed in parallel. Writes to the working copy
237 fact, several revisions may be fixed in parallel. Writes to the working copy
238 are not amended into the revision being fixed; fixer tools should always write
238 are not amended into the revision being fixed; fixer tools should always write
239 fixed file content back to stdout as documented above.
239 fixed file content back to stdout as documented above.
240
240
241 list of commands:
241 list of commands:
242
242
243 fix rewrite file content in changesets or working directory
243 fix rewrite file content in changesets or working directory
244
244
245 (use 'hg help -v -e fix' to show built-in aliases and global options)
245 (use 'hg help -v -e fix' to show built-in aliases and global options)
246
246
247 There is no default behavior in the absence of --rev and --working-dir.
247 There is no default behavior in the absence of --rev and --working-dir.
248
248
249 $ hg init badusage
249 $ hg init badusage
250 $ cd badusage
250 $ cd badusage
251
251
252 $ hg fix
252 $ hg fix
253 abort: no changesets specified
253 abort: no changesets specified
254 (use --source or --working-dir)
254 (use --source or --working-dir)
255 [255]
255 [255]
256 $ hg fix --whole
256 $ hg fix --whole
257 abort: no changesets specified
257 abort: no changesets specified
258 (use --source or --working-dir)
258 (use --source or --working-dir)
259 [255]
259 [255]
260 $ hg fix --base 0
260 $ hg fix --base 0
261 abort: no changesets specified
261 abort: no changesets specified
262 (use --source or --working-dir)
262 (use --source or --working-dir)
263 [255]
263 [255]
264
264
265 Fixing a public revision isn't allowed. It should abort early enough that
265 Fixing a public revision isn't allowed. It should abort early enough that
266 nothing happens, even to the working directory.
266 nothing happens, even to the working directory.
267
267
268 $ printf "hello\n" > hello.whole
268 $ printf "hello\n" > hello.whole
269 $ hg commit -Aqm "hello"
269 $ hg commit -Aqm "hello"
270 $ hg phase -r 0 --public
270 $ hg phase -r 0 --public
271 $ hg fix -r 0
271 $ hg fix -r 0
272 abort: cannot fix public changesets: 6470986d2e7b
272 abort: cannot fix public changesets: 6470986d2e7b
273 (see 'hg help phases' for details)
273 (see 'hg help phases' for details)
274 [10]
274 [10]
275 $ hg fix -r 0 --working-dir
275 $ hg fix -r 0 --working-dir
276 abort: cannot fix public changesets: 6470986d2e7b
276 abort: cannot fix public changesets: 6470986d2e7b
277 (see 'hg help phases' for details)
277 (see 'hg help phases' for details)
278 [10]
278 [10]
279 $ hg cat -r tip hello.whole
279 $ hg cat -r tip hello.whole
280 hello
280 hello
281 $ cat hello.whole
281 $ cat hello.whole
282 hello
282 hello
283
283
284 $ cd ..
284 $ cd ..
285
285
286 Fixing a clean working directory should do nothing. Even the --whole flag
286 Fixing a clean working directory should do nothing. Even the --whole flag
287 shouldn't cause any clean files to be fixed. Specifying a clean file explicitly
287 shouldn't cause any clean files to be fixed. Specifying a clean file explicitly
288 should only fix it if the fixer always fixes the whole file. The combination of
288 should only fix it if the fixer always fixes the whole file. The combination of
289 an explicit filename and --whole should format the entire file regardless.
289 an explicit filename and --whole should format the entire file regardless.
290
290
291 $ hg init fixcleanwdir
291 $ hg init fixcleanwdir
292 $ cd fixcleanwdir
292 $ cd fixcleanwdir
293
293
294 $ printf "hello\n" > hello.changed
294 $ printf "hello\n" > hello.changed
295 $ printf "world\n" > hello.whole
295 $ printf "world\n" > hello.whole
296 $ hg commit -Aqm "foo"
296 $ hg commit -Aqm "foo"
297 $ hg fix --working-dir
297 $ hg fix --working-dir
298 $ hg diff
298 $ hg diff
299 $ hg fix --working-dir --whole
299 $ hg fix --working-dir --whole
300 $ hg diff
300 $ hg diff
301 $ hg fix --working-dir *
301 $ hg fix --working-dir *
302 $ cat *
302 $ cat *
303 hello
303 hello
304 WORLD
304 WORLD
305 $ hg revert --all --no-backup
305 $ hg revert --all --no-backup
306 reverting hello.whole
306 reverting hello.whole
307 $ hg fix --working-dir * --whole
307 $ hg fix --working-dir * --whole
308 $ cat *
308 $ cat *
309 HELLO
309 HELLO
310 WORLD
310 WORLD
311
311
312 The same ideas apply to fixing a revision, so we create a revision that doesn't
312 The same ideas apply to fixing a revision, so we create a revision that doesn't
313 modify either of the files in question and try fixing it. This also tests that
313 modify either of the files in question and try fixing it. This also tests that
314 we ignore a file that doesn't match any configured fixer.
314 we ignore a file that doesn't match any configured fixer.
315
315
316 $ hg revert --all --no-backup
316 $ hg revert --all --no-backup
317 reverting hello.changed
317 reverting hello.changed
318 reverting hello.whole
318 reverting hello.whole
319 $ printf "unimportant\n" > some.file
319 $ printf "unimportant\n" > some.file
320 $ hg commit -Aqm "some other file"
320 $ hg commit -Aqm "some other file"
321
321
322 $ hg fix -r .
322 $ hg fix -r .
323 $ hg cat -r tip *
323 $ hg cat -r tip *
324 hello
324 hello
325 world
325 world
326 unimportant
326 unimportant
327 $ hg fix -r . --whole
327 $ hg fix -r . --whole
328 $ hg cat -r tip *
328 $ hg cat -r tip *
329 hello
329 hello
330 world
330 world
331 unimportant
331 unimportant
332 $ hg fix -r . *
332 $ hg fix -r . *
333 $ hg cat -r tip *
333 $ hg cat -r tip *
334 hello
334 hello
335 WORLD
335 WORLD
336 unimportant
336 unimportant
337 $ hg fix -r . * --whole --config experimental.evolution.allowdivergence=true
337 $ hg fix -r . * --whole --config experimental.evolution.allowdivergence=true
338 2 new content-divergent changesets
338 2 new content-divergent changesets
339 $ hg cat -r tip *
339 $ hg cat -r tip *
340 HELLO
340 HELLO
341 WORLD
341 WORLD
342 unimportant
342 unimportant
343
343
344 $ cd ..
344 $ cd ..
345
345
346 Fixing the working directory should still work if there are no revisions.
346 Fixing the working directory should still work if there are no revisions.
347
347
348 $ hg init norevisions
348 $ hg init norevisions
349 $ cd norevisions
349 $ cd norevisions
350
350
351 $ printf "something\n" > something.whole
351 $ printf "something\n" > something.whole
352 $ hg add
352 $ hg add
353 adding something.whole
353 adding something.whole
354 $ hg fix --working-dir
354 $ hg fix --working-dir
355 $ cat something.whole
355 $ cat something.whole
356 SOMETHING
356 SOMETHING
357
357
358 $ cd ..
358 $ cd ..
359
359
360 Test that the working copy is reported clean if formatting of the parent makes
360 Test that the working copy is reported clean if formatting of the parent makes
361 it clean.
361 it clean.
362 $ hg init wc-already-formatted
362 $ hg init wc-already-formatted
363 $ cd wc-already-formatted
363 $ cd wc-already-formatted
364
364
365 $ printf "hello world\n" > hello.whole
365 $ printf "hello world\n" > hello.whole
366 $ hg commit -Am initial
366 $ hg commit -Am initial
367 adding hello.whole
367 adding hello.whole
368 $ hg fix -w *
368 $ hg fix -w *
369 $ hg st
369 $ hg st
370 M hello.whole
370 M hello.whole
371 $ hg fix -s . *
371 $ hg fix -s . *
372 $ hg st
372 $ hg st
373 $ hg diff
373 $ hg diff
374
374
375 $ cd ..
375 $ cd ..
376
376
377 Test the effect of fixing the working directory for each possible status, with
377 Test the effect of fixing the working directory for each possible status, with
378 and without providing explicit file arguments.
378 and without providing explicit file arguments.
379
379
380 $ hg init implicitlyfixstatus
380 $ hg init implicitlyfixstatus
381 $ cd implicitlyfixstatus
381 $ cd implicitlyfixstatus
382
382
383 $ printf "modified\n" > modified.whole
383 $ printf "modified\n" > modified.whole
384 $ printf "removed\n" > removed.whole
384 $ printf "removed\n" > removed.whole
385 $ printf "deleted\n" > deleted.whole
385 $ printf "deleted\n" > deleted.whole
386 $ printf "clean\n" > clean.whole
386 $ printf "clean\n" > clean.whole
387 $ printf "ignored.whole" > .hgignore
387 $ printf "ignored.whole" > .hgignore
388 $ hg commit -Aqm "stuff"
388 $ hg commit -Aqm "stuff"
389
389
390 $ printf "modified!!!\n" > modified.whole
390 $ printf "modified!!!\n" > modified.whole
391 $ printf "unknown\n" > unknown.whole
391 $ printf "unknown\n" > unknown.whole
392 $ printf "ignored\n" > ignored.whole
392 $ printf "ignored\n" > ignored.whole
393 $ printf "added\n" > added.whole
393 $ printf "added\n" > added.whole
394 $ hg add added.whole
394 $ hg add added.whole
395 $ hg remove removed.whole
395 $ hg remove removed.whole
396 $ rm deleted.whole
396 $ rm deleted.whole
397
397
398 $ hg status --all
398 $ hg status --all
399 M modified.whole
399 M modified.whole
400 A added.whole
400 A added.whole
401 R removed.whole
401 R removed.whole
402 ! deleted.whole
402 ! deleted.whole
403 ? unknown.whole
403 ? unknown.whole
404 I ignored.whole
404 I ignored.whole
405 C .hgignore
405 C .hgignore
406 C clean.whole
406 C clean.whole
407
407
408 $ hg fix --working-dir
408 $ hg fix --working-dir
409
409
410 $ hg status --all
410 $ hg status --all
411 M modified.whole
411 M modified.whole
412 A added.whole
412 A added.whole
413 R removed.whole
413 R removed.whole
414 ! deleted.whole
414 ! deleted.whole
415 ? unknown.whole
415 ? unknown.whole
416 I ignored.whole
416 I ignored.whole
417 C .hgignore
417 C .hgignore
418 C clean.whole
418 C clean.whole
419
419
420 $ cat *.whole
420 $ cat *.whole
421 ADDED
421 ADDED
422 clean
422 clean
423 ignored
423 ignored
424 MODIFIED!!!
424 MODIFIED!!!
425 unknown
425 unknown
426
426
427 $ printf "modified!!!\n" > modified.whole
427 $ printf "modified!!!\n" > modified.whole
428 $ printf "added\n" > added.whole
428 $ printf "added\n" > added.whole
429
429
430 Listing the files explicitly causes untracked files to also be fixed, but
430 Listing the files explicitly causes untracked files to also be fixed, but
431 ignored files are still unaffected.
431 ignored files are still unaffected.
432
432
433 $ hg fix --working-dir *.whole
433 $ hg fix --working-dir *.whole
434
434
435 $ hg status --all
435 $ hg status --all
436 M clean.whole
436 M clean.whole
437 M modified.whole
437 M modified.whole
438 A added.whole
438 A added.whole
439 R removed.whole
439 R removed.whole
440 ! deleted.whole
440 ! deleted.whole
441 ? unknown.whole
441 ? unknown.whole
442 I ignored.whole
442 I ignored.whole
443 C .hgignore
443 C .hgignore
444
444
445 $ cat *.whole
445 $ cat *.whole
446 ADDED
446 ADDED
447 CLEAN
447 CLEAN
448 ignored
448 ignored
449 MODIFIED!!!
449 MODIFIED!!!
450 UNKNOWN
450 UNKNOWN
451
451
452 $ cd ..
452 $ cd ..
453
453
454 Test that incremental fixing works on files with additions, deletions, and
454 Test that incremental fixing works on files with additions, deletions, and
455 changes in multiple line ranges. Note that deletions do not generally cause
455 changes in multiple line ranges. Note that deletions do not generally cause
456 neighboring lines to be fixed, so we don't return a line range for purely
456 neighboring lines to be fixed, so we don't return a line range for purely
457 deleted sections. In the future we should support a :deletion config that
457 deleted sections. In the future we should support a :deletion config that
458 allows fixers to know where deletions are located.
458 allows fixers to know where deletions are located.
459
459
460 $ hg init incrementalfixedlines
460 $ hg init incrementalfixedlines
461 $ cd incrementalfixedlines
461 $ cd incrementalfixedlines
462
462
463 $ printf "a\nb\nc\nd\ne\nf\ng\n" > foo.txt
463 $ printf "a\nb\nc\nd\ne\nf\ng\n" > foo.txt
464 $ hg commit -Aqm "foo"
464 $ hg commit -Aqm "foo"
465 $ printf "zz\na\nc\ndd\nee\nff\nf\ngg\n" > foo.txt
465 $ printf "zz\na\nc\ndd\nee\nff\nf\ngg\n" > foo.txt
466
466
467 $ hg --config "fix.fail:command=echo" \
467 $ hg --config "fix.fail:command=echo" \
468 > --config "fix.fail:linerange={first}:{last}" \
468 > --config "fix.fail:linerange={first}:{last}" \
469 > --config "fix.fail:pattern=foo.txt" \
469 > --config "fix.fail:pattern=foo.txt" \
470 > fix --working-dir
470 > fix --working-dir
471 $ cat foo.txt
471 $ cat foo.txt
472 1:1 4:6 8:8
472 1:1 4:6 8:8
473
473
474 $ cd ..
474 $ cd ..
475
475
476 Test that --whole fixes all lines regardless of the diffs present.
476 Test that --whole fixes all lines regardless of the diffs present.
477
477
478 $ hg init wholeignoresdiffs
478 $ hg init wholeignoresdiffs
479 $ cd wholeignoresdiffs
479 $ cd wholeignoresdiffs
480
480
481 $ printf "a\nb\nc\nd\ne\nf\ng\n" > foo.changed
481 $ printf "a\nb\nc\nd\ne\nf\ng\n" > foo.changed
482 $ hg commit -Aqm "foo"
482 $ hg commit -Aqm "foo"
483 $ printf "zz\na\nc\ndd\nee\nff\nf\ngg\n" > foo.changed
483 $ printf "zz\na\nc\ndd\nee\nff\nf\ngg\n" > foo.changed
484
484
485 $ hg fix --working-dir
485 $ hg fix --working-dir
486 $ cat foo.changed
486 $ cat foo.changed
487 ZZ
487 ZZ
488 a
488 a
489 c
489 c
490 DD
490 DD
491 EE
491 EE
492 FF
492 FF
493 f
493 f
494 GG
494 GG
495
495
496 $ hg fix --working-dir --whole
496 $ hg fix --working-dir --whole
497 $ cat foo.changed
497 $ cat foo.changed
498 ZZ
498 ZZ
499 A
499 A
500 C
500 C
501 DD
501 DD
502 EE
502 EE
503 FF
503 FF
504 F
504 F
505 GG
505 GG
506
506
507 $ cd ..
507 $ cd ..
508
508
509 We should do nothing with symlinks, and their targets should be unaffected. Any
509 We should do nothing with symlinks, and their targets should be unaffected. Any
510 other behavior would be more complicated to implement and harder to document.
510 other behavior would be more complicated to implement and harder to document.
511
511
512 #if symlink
512 #if symlink
513 $ hg init dontmesswithsymlinks
513 $ hg init dontmesswithsymlinks
514 $ cd dontmesswithsymlinks
514 $ cd dontmesswithsymlinks
515
515
516 $ printf "hello\n" > hello.whole
516 $ printf "hello\n" > hello.whole
517 $ ln -s hello.whole hellolink
517 $ ln -s hello.whole hellolink
518 $ hg add
518 $ hg add
519 adding hello.whole
519 adding hello.whole
520 adding hellolink
520 adding hellolink
521 $ hg fix --working-dir hellolink
521 $ hg fix --working-dir hellolink
522 $ hg status
522 $ hg status
523 A hello.whole
523 A hello.whole
524 A hellolink
524 A hellolink
525
525
526 $ cd ..
526 $ cd ..
527 #endif
527 #endif
528
528
529 We should allow fixers to run on binary files, even though this doesn't sound
529 We should allow fixers to run on binary files, even though this doesn't sound
530 like a common use case. There's not much benefit to disallowing it, and users
530 like a common use case. There's not much benefit to disallowing it, and users
531 can add "and not binary()" to their filesets if needed. The Mercurial
531 can add "and not binary()" to their filesets if needed. The Mercurial
532 philosophy is generally to not handle binary files specially anyway.
532 philosophy is generally to not handle binary files specially anyway.
533
533
534 $ hg init cantouchbinaryfiles
534 $ hg init cantouchbinaryfiles
535 $ cd cantouchbinaryfiles
535 $ cd cantouchbinaryfiles
536
536
537 $ printf "hello\0\n" > hello.whole
537 $ printf "hello\0\n" > hello.whole
538 $ hg add
538 $ hg add
539 adding hello.whole
539 adding hello.whole
540 $ hg fix --working-dir 'set:binary()'
540 $ hg fix --working-dir 'set:binary()'
541 $ cat hello.whole
541 $ cat hello.whole
542 HELLO\x00 (esc)
542 HELLO\x00 (esc)
543
543
544 $ cd ..
544 $ cd ..
545
545
546 We have a config for the maximum size of file we will attempt to fix. This can
546 We have a config for the maximum size of file we will attempt to fix. This can
547 be helpful to avoid running unsuspecting fixer tools on huge inputs, which
547 be helpful to avoid running unsuspecting fixer tools on huge inputs, which
548 could happen by accident without a well considered configuration. A more
548 could happen by accident without a well considered configuration. A more
549 precise configuration could use the size() fileset function if one global limit
549 precise configuration could use the size() fileset function if one global limit
550 is undesired.
550 is undesired.
551
551
552 $ hg init maxfilesize
552 $ hg init maxfilesize
553 $ cd maxfilesize
553 $ cd maxfilesize
554
554
555 $ printf "this file is huge\n" > hello.whole
555 $ printf "this file is huge\n" > hello.whole
556 $ hg add
556 $ hg add
557 adding hello.whole
557 adding hello.whole
558 $ hg --config fix.maxfilesize=10 fix --working-dir
558 $ hg --config fix.maxfilesize=10 fix --working-dir
559 ignoring file larger than 10 bytes: hello.whole
559 ignoring file larger than 10 bytes: hello.whole
560 $ cat hello.whole
560 $ cat hello.whole
561 this file is huge
561 this file is huge
562
562
563 $ cd ..
563 $ cd ..
564
564
565 If we specify a file to fix, other files should be left alone, even if they
565 If we specify a file to fix, other files should be left alone, even if they
566 have changes.
566 have changes.
567
567
568 $ hg init fixonlywhatitellyouto
568 $ hg init fixonlywhatitellyouto
569 $ cd fixonlywhatitellyouto
569 $ cd fixonlywhatitellyouto
570
570
571 $ printf "fix me!\n" > fixme.whole
571 $ printf "fix me!\n" > fixme.whole
572 $ printf "not me.\n" > notme.whole
572 $ printf "not me.\n" > notme.whole
573 $ hg add
573 $ hg add
574 adding fixme.whole
574 adding fixme.whole
575 adding notme.whole
575 adding notme.whole
576 $ hg fix --working-dir fixme.whole
576 $ hg fix --working-dir fixme.whole
577 $ cat *.whole
577 $ cat *.whole
578 FIX ME!
578 FIX ME!
579 not me.
579 not me.
580
580
581 $ cd ..
581 $ cd ..
582
582
583 If we try to fix a missing file, we still fix other files.
583 If we try to fix a missing file, we still fix other files.
584
584
585 $ hg init fixmissingfile
585 $ hg init fixmissingfile
586 $ cd fixmissingfile
586 $ cd fixmissingfile
587
587
588 $ printf "fix me!\n" > foo.whole
588 $ printf "fix me!\n" > foo.whole
589 $ hg add
589 $ hg add
590 adding foo.whole
590 adding foo.whole
591 $ hg fix --working-dir foo.whole bar.whole
591 $ hg fix --working-dir foo.whole bar.whole
592 bar.whole: $ENOENT$
592 bar.whole: $ENOENT$
593 $ cat *.whole
593 $ cat *.whole
594 FIX ME!
594 FIX ME!
595
595
596 $ cd ..
596 $ cd ..
597
597
598 Specifying a directory name should fix all its files and subdirectories.
598 Specifying a directory name should fix all its files and subdirectories.
599
599
600 $ hg init fixdirectory
600 $ hg init fixdirectory
601 $ cd fixdirectory
601 $ cd fixdirectory
602
602
603 $ mkdir -p dir1/dir2
603 $ mkdir -p dir1/dir2
604 $ printf "foo\n" > foo.whole
604 $ printf "foo\n" > foo.whole
605 $ printf "bar\n" > dir1/bar.whole
605 $ printf "bar\n" > dir1/bar.whole
606 $ printf "baz\n" > dir1/dir2/baz.whole
606 $ printf "baz\n" > dir1/dir2/baz.whole
607 $ hg add
607 $ hg add
608 adding dir1/bar.whole
608 adding dir1/bar.whole
609 adding dir1/dir2/baz.whole
609 adding dir1/dir2/baz.whole
610 adding foo.whole
610 adding foo.whole
611 $ hg fix --working-dir dir1
611 $ hg fix --working-dir dir1
612 $ cat foo.whole dir1/bar.whole dir1/dir2/baz.whole
612 $ cat foo.whole dir1/bar.whole dir1/dir2/baz.whole
613 foo
613 foo
614 BAR
614 BAR
615 BAZ
615 BAZ
616
616
617 $ cd ..
617 $ cd ..
618
618
619 Fixing a file in the working directory that needs no fixes should not actually
619 Fixing a file in the working directory that needs no fixes should not actually
620 write back to the file, so for example the mtime shouldn't change.
620 write back to the file, so for example the mtime shouldn't change.
621
621
622 $ hg init donttouchunfixedfiles
622 $ hg init donttouchunfixedfiles
623 $ cd donttouchunfixedfiles
623 $ cd donttouchunfixedfiles
624
624
625 $ printf "NO FIX NEEDED\n" > foo.whole
625 $ printf "NO FIX NEEDED\n" > foo.whole
626 $ hg add
626 $ hg add
627 adding foo.whole
627 adding foo.whole
628 $ cp -p foo.whole foo.whole.orig
628 $ cp -p foo.whole foo.whole.orig
629 $ cp -p foo.whole.orig foo.whole
629 $ cp -p foo.whole.orig foo.whole
630 $ sleep 2 # mtime has a resolution of one or two seconds.
630 $ sleep 2 # mtime has a resolution of one or two seconds.
631 $ hg fix --working-dir
631 $ hg fix --working-dir
632 $ f foo.whole.orig --newer foo.whole
632 $ f foo.whole.orig --newer foo.whole
633 foo.whole.orig: newer than foo.whole
633 foo.whole.orig: newer than foo.whole
634
634
635 $ cd ..
635 $ cd ..
636
636
637 When a fixer prints to stderr, we don't assume that it has failed. We show the
637 When a fixer prints to stderr, we don't assume that it has failed. We show the
638 error messages to the user, and we still let the fixer affect the file it was
638 error messages to the user, and we still let the fixer affect the file it was
639 fixing if its exit code is zero. Some code formatters might emit error messages
639 fixing if its exit code is zero. Some code formatters might emit error messages
640 on stderr and nothing on stdout, which would cause us the clear the file,
640 on stderr and nothing on stdout, which would cause us the clear the file,
641 except that they also exit with a non-zero code. We show the user which fixer
641 except that they also exit with a non-zero code. We show the user which fixer
642 emitted the stderr, and which revision, but we assume that the fixer will print
642 emitted the stderr, and which revision, but we assume that the fixer will print
643 the filename if it is relevant (since the issue may be non-specific). There is
643 the filename if it is relevant (since the issue may be non-specific). There is
644 also a config to abort (without affecting any files whatsoever) if we see any
644 also a config to abort (without affecting any files whatsoever) if we see any
645 tool with a non-zero exit status.
645 tool with a non-zero exit status.
646
646
647 $ hg init showstderr
647 $ hg init showstderr
648 $ cd showstderr
648 $ cd showstderr
649
649
650 $ printf "hello\n" > hello.txt
650 $ printf "hello\n" > hello.txt
651 $ hg add
651 $ hg add
652 adding hello.txt
652 adding hello.txt
653 $ cat > $TESTTMP/work.sh <<'EOF'
653 $ cat > $TESTTMP/work.sh <<'EOF'
654 > printf 'HELLO\n'
654 > printf 'HELLO\n'
655 > printf "$@: some\nerror that didn't stop the tool" >&2
655 > printf "$@: some\nerror that didn't stop the tool" >&2
656 > exit 0 # success despite the stderr output
656 > exit 0 # success despite the stderr output
657 > EOF
657 > EOF
658 $ hg --config "fix.work:command=sh $TESTTMP/work.sh {rootpath}" \
658 $ hg --config "fix.work:command=sh $TESTTMP/work.sh {rootpath}" \
659 > --config "fix.work:pattern=hello.txt" \
659 > --config "fix.work:pattern=hello.txt" \
660 > fix --working-dir
660 > fix --working-dir
661 [wdir] work: hello.txt: some
661 [wdir] work: hello.txt: some
662 [wdir] work: error that didn't stop the tool
662 [wdir] work: error that didn't stop the tool
663 $ cat hello.txt
663 $ cat hello.txt
664 HELLO
664 HELLO
665
665
666 $ printf "goodbye\n" > hello.txt
666 $ printf "goodbye\n" > hello.txt
667 $ printf "foo\n" > foo.whole
667 $ printf "foo\n" > foo.whole
668 $ hg add
668 $ hg add
669 adding foo.whole
669 adding foo.whole
670 $ cat > $TESTTMP/fail.sh <<'EOF'
670 $ cat > $TESTTMP/fail.sh <<'EOF'
671 > printf 'GOODBYE\n'
671 > printf 'GOODBYE\n'
672 > printf "$@: some\nerror that did stop the tool\n" >&2
672 > printf "$@: some\nerror that did stop the tool\n" >&2
673 > exit 42 # success despite the stdout output
673 > exit 42 # success despite the stdout output
674 > EOF
674 > EOF
675 $ hg --config "fix.fail:command=sh $TESTTMP/fail.sh {rootpath}" \
675 $ hg --config "fix.fail:command=sh $TESTTMP/fail.sh {rootpath}" \
676 > --config "fix.fail:pattern=hello.txt" \
676 > --config "fix.fail:pattern=hello.txt" \
677 > --config "fix.failure=abort" \
677 > --config "fix.failure=abort" \
678 > fix --working-dir
678 > fix --working-dir
679 [wdir] fail: hello.txt: some
679 [wdir] fail: hello.txt: some
680 [wdir] fail: error that did stop the tool
680 [wdir] fail: error that did stop the tool
681 abort: no fixes will be applied
681 abort: no fixes will be applied
682 (use --config fix.failure=continue to apply any successful fixes anyway)
682 (use --config fix.failure=continue to apply any successful fixes anyway)
683 [255]
683 [255]
684 $ cat hello.txt
684 $ cat hello.txt
685 goodbye
685 goodbye
686 $ cat foo.whole
686 $ cat foo.whole
687 foo
687 foo
688
688
689 $ hg --config "fix.fail:command=sh $TESTTMP/fail.sh {rootpath}" \
689 $ hg --config "fix.fail:command=sh $TESTTMP/fail.sh {rootpath}" \
690 > --config "fix.fail:pattern=hello.txt" \
690 > --config "fix.fail:pattern=hello.txt" \
691 > fix --working-dir
691 > fix --working-dir
692 [wdir] fail: hello.txt: some
692 [wdir] fail: hello.txt: some
693 [wdir] fail: error that did stop the tool
693 [wdir] fail: error that did stop the tool
694 $ cat hello.txt
694 $ cat hello.txt
695 goodbye
695 goodbye
696 $ cat foo.whole
696 $ cat foo.whole
697 FOO
697 FOO
698
698
699 $ hg --config "fix.fail:command=exit 42" \
699 $ hg --config "fix.fail:command=exit 42" \
700 > --config "fix.fail:pattern=hello.txt" \
700 > --config "fix.fail:pattern=hello.txt" \
701 > fix --working-dir
701 > fix --working-dir
702 [wdir] fail: exited with status 42
702 [wdir] fail: exited with status 42
703
703
704 $ cd ..
704 $ cd ..
705
705
706 Fixing the working directory and its parent revision at the same time should
706 Fixing the working directory and its parent revision at the same time should
707 check out the replacement revision for the parent. This prevents any new
707 check out the replacement revision for the parent. This prevents any new
708 uncommitted changes from appearing. We test this for a clean working directory
708 uncommitted changes from appearing. We test this for a clean working directory
709 and a dirty one. In both cases, all lines/files changed since the grandparent
709 and a dirty one. In both cases, all lines/files changed since the grandparent
710 will be fixed. The grandparent is the "baserev" for both the parent and the
710 will be fixed. The grandparent is the "baserev" for both the parent and the
711 working copy.
711 working copy.
712
712
713 $ hg init fixdotandcleanwdir
713 $ hg init fixdotandcleanwdir
714 $ cd fixdotandcleanwdir
714 $ cd fixdotandcleanwdir
715
715
716 $ printf "hello\n" > hello.whole
716 $ printf "hello\n" > hello.whole
717 $ printf "world\n" > world.whole
717 $ printf "world\n" > world.whole
718 $ hg commit -Aqm "the parent commit"
718 $ hg commit -Aqm "the parent commit"
719
719
720 $ hg parents --template '{rev} {desc}\n'
720 $ hg parents --template '{rev} {desc}\n'
721 0 the parent commit
721 0 the parent commit
722 $ hg fix --working-dir -r .
722 $ hg fix --working-dir -r .
723 $ hg parents --template '{rev} {desc}\n'
723 $ hg parents --template '{rev} {desc}\n'
724 1 the parent commit
724 1 the parent commit
725 $ hg cat -r . *.whole
725 $ hg cat -r . *.whole
726 HELLO
726 HELLO
727 WORLD
727 WORLD
728 $ cat *.whole
728 $ cat *.whole
729 HELLO
729 HELLO
730 WORLD
730 WORLD
731 $ hg status
731 $ hg status
732
732
733 $ cd ..
733 $ cd ..
734
734
735 Same test with a dirty working copy.
735 Same test with a dirty working copy.
736
736
737 $ hg init fixdotanddirtywdir
737 $ hg init fixdotanddirtywdir
738 $ cd fixdotanddirtywdir
738 $ cd fixdotanddirtywdir
739
739
740 $ printf "hello\n" > hello.whole
740 $ printf "hello\n" > hello.whole
741 $ printf "world\n" > world.whole
741 $ printf "world\n" > world.whole
742 $ hg commit -Aqm "the parent commit"
742 $ hg commit -Aqm "the parent commit"
743
743
744 $ printf "hello,\n" > hello.whole
744 $ printf "hello,\n" > hello.whole
745 $ printf "world!\n" > world.whole
745 $ printf "world!\n" > world.whole
746
746
747 $ hg parents --template '{rev} {desc}\n'
747 $ hg parents --template '{rev} {desc}\n'
748 0 the parent commit
748 0 the parent commit
749 $ hg fix --working-dir -r .
749 $ hg fix --working-dir -r .
750 $ hg parents --template '{rev} {desc}\n'
750 $ hg parents --template '{rev} {desc}\n'
751 1 the parent commit
751 1 the parent commit
752 $ hg cat -r . *.whole
752 $ hg cat -r . *.whole
753 HELLO
753 HELLO
754 WORLD
754 WORLD
755 $ cat *.whole
755 $ cat *.whole
756 HELLO,
756 HELLO,
757 WORLD!
757 WORLD!
758 $ hg status
758 $ hg status
759 M hello.whole
759 M hello.whole
760 M world.whole
760 M world.whole
761
761
762 $ cd ..
762 $ cd ..
763
763
764 When we have a chain of commits that change mutually exclusive lines of code,
764 When we have a chain of commits that change mutually exclusive lines of code,
765 we should be able to do incremental fixing that causes each commit in the chain
765 we should be able to do incremental fixing that causes each commit in the chain
766 to include fixes made to the previous commits. This prevents children from
766 to include fixes made to the previous commits. This prevents children from
767 backing out the fixes made in their parents. A dirty working directory is
767 backing out the fixes made in their parents. A dirty working directory is
768 conceptually similar to another commit in the chain.
768 conceptually similar to another commit in the chain.
769
769
770 $ hg init incrementallyfixchain
770 $ hg init incrementallyfixchain
771 $ cd incrementallyfixchain
771 $ cd incrementallyfixchain
772
772
773 $ cat > file.changed <<EOF
773 $ cat > file.changed <<EOF
774 > first
774 > first
775 > second
775 > second
776 > third
776 > third
777 > fourth
777 > fourth
778 > fifth
778 > fifth
779 > EOF
779 > EOF
780 $ hg commit -Aqm "the common ancestor (the baserev)"
780 $ hg commit -Aqm "the common ancestor (the baserev)"
781 $ cat > file.changed <<EOF
781 $ cat > file.changed <<EOF
782 > first (changed)
782 > first (changed)
783 > second
783 > second
784 > third
784 > third
785 > fourth
785 > fourth
786 > fifth
786 > fifth
787 > EOF
787 > EOF
788 $ hg commit -Aqm "the first commit to fix"
788 $ hg commit -Aqm "the first commit to fix"
789 $ cat > file.changed <<EOF
789 $ cat > file.changed <<EOF
790 > first (changed)
790 > first (changed)
791 > second
791 > second
792 > third (changed)
792 > third (changed)
793 > fourth
793 > fourth
794 > fifth
794 > fifth
795 > EOF
795 > EOF
796 $ hg commit -Aqm "the second commit to fix"
796 $ hg commit -Aqm "the second commit to fix"
797 $ cat > file.changed <<EOF
797 $ cat > file.changed <<EOF
798 > first (changed)
798 > first (changed)
799 > second
799 > second
800 > third (changed)
800 > third (changed)
801 > fourth
801 > fourth
802 > fifth (changed)
802 > fifth (changed)
803 > EOF
803 > EOF
804
804
805 $ hg fix -r . -r '.^' --working-dir
805 $ hg fix -r . -r '.^' --working-dir
806
806
807 $ hg parents --template '{rev}\n'
807 $ hg parents --template '{rev}\n'
808 4
808 4
809 $ hg cat -r '.^^' file.changed
809 $ hg cat -r '.^^' file.changed
810 first
810 first
811 second
811 second
812 third
812 third
813 fourth
813 fourth
814 fifth
814 fifth
815 $ hg cat -r '.^' file.changed
815 $ hg cat -r '.^' file.changed
816 FIRST (CHANGED)
816 FIRST (CHANGED)
817 second
817 second
818 third
818 third
819 fourth
819 fourth
820 fifth
820 fifth
821 $ hg cat -r . file.changed
821 $ hg cat -r . file.changed
822 FIRST (CHANGED)
822 FIRST (CHANGED)
823 second
823 second
824 THIRD (CHANGED)
824 THIRD (CHANGED)
825 fourth
825 fourth
826 fifth
826 fifth
827 $ cat file.changed
827 $ cat file.changed
828 FIRST (CHANGED)
828 FIRST (CHANGED)
829 second
829 second
830 THIRD (CHANGED)
830 THIRD (CHANGED)
831 fourth
831 fourth
832 FIFTH (CHANGED)
832 FIFTH (CHANGED)
833
833
834 $ cd ..
834 $ cd ..
835
835
836 If we incrementally fix a merge commit, we should fix any lines that changed
836 If we incrementally fix a merge commit, we should fix any lines that changed
837 versus either parent. You could imagine only fixing the intersection or some
837 versus either parent. You could imagine only fixing the intersection or some
838 other subset, but this is necessary if either parent is being fixed. It
838 other subset, but this is necessary if either parent is being fixed. It
839 prevents us from forgetting fixes made in either parent.
839 prevents us from forgetting fixes made in either parent.
840
840
841 $ hg init incrementallyfixmergecommit
841 $ hg init incrementallyfixmergecommit
842 $ cd incrementallyfixmergecommit
842 $ cd incrementallyfixmergecommit
843
843
844 $ printf "a\nb\nc\n" > file.changed
844 $ printf "a\nb\nc\n" > file.changed
845 $ hg commit -Aqm "ancestor"
845 $ hg commit -Aqm "ancestor"
846
846
847 $ printf "aa\nb\nc\n" > file.changed
847 $ printf "aa\nb\nc\n" > file.changed
848 $ hg commit -m "change a"
848 $ hg commit -m "change a"
849
849
850 $ hg checkout '.^'
850 $ hg checkout '.^'
851 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
851 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
852 $ printf "a\nb\ncc\n" > file.changed
852 $ printf "a\nb\ncc\n" > file.changed
853 $ hg commit -m "change c"
853 $ hg commit -m "change c"
854 created new head
854 created new head
855
855
856 $ hg merge
856 $ hg merge
857 merging file.changed
857 merging file.changed
858 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
858 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
859 (branch merge, don't forget to commit)
859 (branch merge, don't forget to commit)
860 $ hg commit -m "merge"
860 $ hg commit -m "merge"
861 $ hg cat -r . file.changed
861 $ hg cat -r . file.changed
862 aa
862 aa
863 b
863 b
864 cc
864 cc
865
865
866 $ hg fix -r . --working-dir
866 $ hg fix -r . --working-dir
867 $ hg cat -r . file.changed
867 $ hg cat -r . file.changed
868 AA
868 AA
869 b
869 b
870 CC
870 CC
871
871
872 $ cd ..
872 $ cd ..
873
873
874 We should be allowed to fix the working (and only the working copy) while
875 merging.
876
877 $ hg init fixworkingcopywhilemerging
878 $ cd fixworkingcopywhilemerging
879
880 $ printf "a\nb\nc\n" > file.changed
881 $ hg commit -Aqm "ancestor"
882
883 $ printf "aa\nb\nc\n" > file.changed
884 $ hg commit -m "change a"
885
886 $ hg checkout '.^'
887 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
888 $ printf "a\nb\ncc\n" > file.changed
889 $ hg commit -m "change c"
890 created new head
891
892 $ hg merge
893 merging file.changed
894 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
895 (branch merge, don't forget to commit)
896 $ cat file.changed
897 aa
898 b
899 cc
900 Not allowed to fix a parent of the working copy while merging
901 $ hg fix -r . --working-dir
902 abort: outstanding uncommitted merge
903 (use 'hg commit' or 'hg merge --abort')
904 [20]
905 $ hg fix --working-dir
906 $ cat file.changed
907 AA
908 b
909 CC
910
911 $ cd ..
912
874 Abort fixing revisions if there is an unfinished operation. We don't want to
913 Abort fixing revisions if there is an unfinished operation. We don't want to
875 make things worse by editing files or stripping/obsoleting things. Also abort
914 make things worse by editing files or stripping/obsoleting things. Also abort
876 fixing the working directory if there are unresolved merge conflicts.
915 fixing the working directory if there are unresolved merge conflicts.
877
916
878 $ hg init abortunresolved
917 $ hg init abortunresolved
879 $ cd abortunresolved
918 $ cd abortunresolved
880
919
881 $ echo "foo1" > foo.whole
920 $ echo "foo1" > foo.whole
882 $ hg commit -Aqm "foo 1"
921 $ hg commit -Aqm "foo 1"
883
922
884 $ hg update null
923 $ hg update null
885 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
924 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
886 $ echo "foo2" > foo.whole
925 $ echo "foo2" > foo.whole
887 $ hg commit -Aqm "foo 2"
926 $ hg commit -Aqm "foo 2"
888
927
889 $ hg --config extensions.rebase= rebase -r 1 -d 0
928 $ hg --config extensions.rebase= rebase -r 1 -d 0
890 rebasing 1:c3b6dc0e177a tip "foo 2"
929 rebasing 1:c3b6dc0e177a tip "foo 2"
891 merging foo.whole
930 merging foo.whole
892 warning: conflicts while merging foo.whole! (edit, then use 'hg resolve --mark')
931 warning: conflicts while merging foo.whole! (edit, then use 'hg resolve --mark')
893 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
932 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
894 [240]
933 [240]
895
934
896 $ hg --config extensions.rebase= fix --working-dir
935 $ hg --config extensions.rebase= fix --working-dir
897 abort: unresolved conflicts
936 abort: unresolved conflicts
898 (use 'hg resolve')
937 (use 'hg resolve')
899 [255]
938 [255]
900
939
901 $ hg --config extensions.rebase= fix -r .
940 $ hg --config extensions.rebase= fix -r .
902 abort: rebase in progress
941 abort: rebase in progress
903 (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop')
942 (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop')
904 [20]
943 [20]
905
944
906 $ cd ..
945 $ cd ..
907
946
908 When fixing a file that was renamed, we should diff against the source of the
947 When fixing a file that was renamed, we should diff against the source of the
909 rename for incremental fixing and we should correctly reproduce the rename in
948 rename for incremental fixing and we should correctly reproduce the rename in
910 the replacement revision.
949 the replacement revision.
911
950
912 $ hg init fixrenamecommit
951 $ hg init fixrenamecommit
913 $ cd fixrenamecommit
952 $ cd fixrenamecommit
914
953
915 $ printf "a\nb\nc\n" > source.changed
954 $ printf "a\nb\nc\n" > source.changed
916 $ hg commit -Aqm "source revision"
955 $ hg commit -Aqm "source revision"
917 $ hg move source.changed dest.changed
956 $ hg move source.changed dest.changed
918 $ printf "a\nb\ncc\n" > dest.changed
957 $ printf "a\nb\ncc\n" > dest.changed
919 $ hg commit -m "dest revision"
958 $ hg commit -m "dest revision"
920
959
921 $ hg fix -r .
960 $ hg fix -r .
922 $ hg log -r tip --copies --template "{file_copies}\n"
961 $ hg log -r tip --copies --template "{file_copies}\n"
923 dest.changed (source.changed)
962 dest.changed (source.changed)
924 $ hg cat -r tip dest.changed
963 $ hg cat -r tip dest.changed
925 a
964 a
926 b
965 b
927 CC
966 CC
928
967
929 $ cd ..
968 $ cd ..
930
969
931 When fixing revisions that remove files we must ensure that the replacement
970 When fixing revisions that remove files we must ensure that the replacement
932 actually removes the file, whereas it could accidentally leave it unchanged or
971 actually removes the file, whereas it could accidentally leave it unchanged or
933 write an empty string to it.
972 write an empty string to it.
934
973
935 $ hg init fixremovedfile
974 $ hg init fixremovedfile
936 $ cd fixremovedfile
975 $ cd fixremovedfile
937
976
938 $ printf "foo\n" > foo.whole
977 $ printf "foo\n" > foo.whole
939 $ printf "bar\n" > bar.whole
978 $ printf "bar\n" > bar.whole
940 $ hg commit -Aqm "add files"
979 $ hg commit -Aqm "add files"
941 $ hg remove bar.whole
980 $ hg remove bar.whole
942 $ hg commit -m "remove file"
981 $ hg commit -m "remove file"
943 $ hg status --change .
982 $ hg status --change .
944 R bar.whole
983 R bar.whole
945 $ hg fix -r . foo.whole
984 $ hg fix -r . foo.whole
946 $ hg status --change tip
985 $ hg status --change tip
947 M foo.whole
986 M foo.whole
948 R bar.whole
987 R bar.whole
949
988
950 $ cd ..
989 $ cd ..
951
990
952 If fixing a revision finds no fixes to make, no replacement revision should be
991 If fixing a revision finds no fixes to make, no replacement revision should be
953 created.
992 created.
954
993
955 $ hg init nofixesneeded
994 $ hg init nofixesneeded
956 $ cd nofixesneeded
995 $ cd nofixesneeded
957
996
958 $ printf "FOO\n" > foo.whole
997 $ printf "FOO\n" > foo.whole
959 $ hg commit -Aqm "add file"
998 $ hg commit -Aqm "add file"
960 $ hg log --template '{rev}\n'
999 $ hg log --template '{rev}\n'
961 0
1000 0
962 $ hg fix -r .
1001 $ hg fix -r .
963 $ hg log --template '{rev}\n'
1002 $ hg log --template '{rev}\n'
964 0
1003 0
965
1004
966 $ cd ..
1005 $ cd ..
967
1006
968 If fixing a commit reverts all the changes in the commit, we replace it with a
1007 If fixing a commit reverts all the changes in the commit, we replace it with a
969 commit that changes no files.
1008 commit that changes no files.
970
1009
971 $ hg init nochangesleft
1010 $ hg init nochangesleft
972 $ cd nochangesleft
1011 $ cd nochangesleft
973
1012
974 $ printf "FOO\n" > foo.whole
1013 $ printf "FOO\n" > foo.whole
975 $ hg commit -Aqm "add file"
1014 $ hg commit -Aqm "add file"
976 $ printf "foo\n" > foo.whole
1015 $ printf "foo\n" > foo.whole
977 $ hg commit -m "edit file"
1016 $ hg commit -m "edit file"
978 $ hg status --change .
1017 $ hg status --change .
979 M foo.whole
1018 M foo.whole
980 $ hg fix -r .
1019 $ hg fix -r .
981 $ hg status --change tip
1020 $ hg status --change tip
982
1021
983 $ cd ..
1022 $ cd ..
984
1023
985 If we fix a parent and child revision together, the child revision must be
1024 If we fix a parent and child revision together, the child revision must be
986 replaced if the parent is replaced, even if the diffs of the child needed no
1025 replaced if the parent is replaced, even if the diffs of the child needed no
987 fixes. However, we're free to not replace revisions that need no fixes and have
1026 fixes. However, we're free to not replace revisions that need no fixes and have
988 no ancestors that are replaced.
1027 no ancestors that are replaced.
989
1028
990 $ hg init mustreplacechild
1029 $ hg init mustreplacechild
991 $ cd mustreplacechild
1030 $ cd mustreplacechild
992
1031
993 $ printf "FOO\n" > foo.whole
1032 $ printf "FOO\n" > foo.whole
994 $ hg commit -Aqm "add foo"
1033 $ hg commit -Aqm "add foo"
995 $ printf "foo\n" > foo.whole
1034 $ printf "foo\n" > foo.whole
996 $ hg commit -m "edit foo"
1035 $ hg commit -m "edit foo"
997 $ printf "BAR\n" > bar.whole
1036 $ printf "BAR\n" > bar.whole
998 $ hg commit -Aqm "add bar"
1037 $ hg commit -Aqm "add bar"
999
1038
1000 $ hg log --graph --template '{rev} {files}'
1039 $ hg log --graph --template '{rev} {files}'
1001 @ 2 bar.whole
1040 @ 2 bar.whole
1002 |
1041 |
1003 o 1 foo.whole
1042 o 1 foo.whole
1004 |
1043 |
1005 o 0 foo.whole
1044 o 0 foo.whole
1006
1045
1007 $ hg fix -r 0:2
1046 $ hg fix -r 0:2
1008 $ hg log --graph --template '{rev} {files}'
1047 $ hg log --graph --template '{rev} {files}'
1009 o 4 bar.whole
1048 o 4 bar.whole
1010 |
1049 |
1011 o 3
1050 o 3
1012 |
1051 |
1013 | @ 2 bar.whole
1052 | @ 2 bar.whole
1014 | |
1053 | |
1015 | x 1 foo.whole
1054 | x 1 foo.whole
1016 |/
1055 |/
1017 o 0 foo.whole
1056 o 0 foo.whole
1018
1057
1019
1058
1020 $ cd ..
1059 $ cd ..
1021
1060
1022 It's also possible that the child needs absolutely no changes, but we still
1061 It's also possible that the child needs absolutely no changes, but we still
1023 need to replace it to update its parent. If we skipped replacing the child
1062 need to replace it to update its parent. If we skipped replacing the child
1024 because it had no file content changes, it would become an orphan for no good
1063 because it had no file content changes, it would become an orphan for no good
1025 reason.
1064 reason.
1026
1065
1027 $ hg init mustreplacechildevenifnop
1066 $ hg init mustreplacechildevenifnop
1028 $ cd mustreplacechildevenifnop
1067 $ cd mustreplacechildevenifnop
1029
1068
1030 $ printf "Foo\n" > foo.whole
1069 $ printf "Foo\n" > foo.whole
1031 $ hg commit -Aqm "add a bad foo"
1070 $ hg commit -Aqm "add a bad foo"
1032 $ printf "FOO\n" > foo.whole
1071 $ printf "FOO\n" > foo.whole
1033 $ hg commit -m "add a good foo"
1072 $ hg commit -m "add a good foo"
1034 $ hg fix -r . -r '.^'
1073 $ hg fix -r . -r '.^'
1035 $ hg log --graph --template '{rev} {desc}'
1074 $ hg log --graph --template '{rev} {desc}'
1036 o 3 add a good foo
1075 o 3 add a good foo
1037 |
1076 |
1038 o 2 add a bad foo
1077 o 2 add a bad foo
1039
1078
1040 @ 1 add a good foo
1079 @ 1 add a good foo
1041 |
1080 |
1042 x 0 add a bad foo
1081 x 0 add a bad foo
1043
1082
1044
1083
1045 $ cd ..
1084 $ cd ..
1046
1085
1047 Similar to the case above, the child revision may become empty as a result of
1086 Similar to the case above, the child revision may become empty as a result of
1048 fixing its parent. We should still create an empty replacement child.
1087 fixing its parent. We should still create an empty replacement child.
1049 TODO: determine how this should interact with ui.allowemptycommit given that
1088 TODO: determine how this should interact with ui.allowemptycommit given that
1050 the empty replacement could have children.
1089 the empty replacement could have children.
1051
1090
1052 $ hg init mustreplacechildevenifempty
1091 $ hg init mustreplacechildevenifempty
1053 $ cd mustreplacechildevenifempty
1092 $ cd mustreplacechildevenifempty
1054
1093
1055 $ printf "foo\n" > foo.whole
1094 $ printf "foo\n" > foo.whole
1056 $ hg commit -Aqm "add foo"
1095 $ hg commit -Aqm "add foo"
1057 $ printf "Foo\n" > foo.whole
1096 $ printf "Foo\n" > foo.whole
1058 $ hg commit -m "edit foo"
1097 $ hg commit -m "edit foo"
1059 $ hg fix -r . -r '.^'
1098 $ hg fix -r . -r '.^'
1060 $ hg log --graph --template '{rev} {desc}\n' --stat
1099 $ hg log --graph --template '{rev} {desc}\n' --stat
1061 o 3 edit foo
1100 o 3 edit foo
1062 |
1101 |
1063 o 2 add foo
1102 o 2 add foo
1064 foo.whole | 1 +
1103 foo.whole | 1 +
1065 1 files changed, 1 insertions(+), 0 deletions(-)
1104 1 files changed, 1 insertions(+), 0 deletions(-)
1066
1105
1067 @ 1 edit foo
1106 @ 1 edit foo
1068 | foo.whole | 2 +-
1107 | foo.whole | 2 +-
1069 | 1 files changed, 1 insertions(+), 1 deletions(-)
1108 | 1 files changed, 1 insertions(+), 1 deletions(-)
1070 |
1109 |
1071 x 0 add foo
1110 x 0 add foo
1072 foo.whole | 1 +
1111 foo.whole | 1 +
1073 1 files changed, 1 insertions(+), 0 deletions(-)
1112 1 files changed, 1 insertions(+), 0 deletions(-)
1074
1113
1075
1114
1076 $ cd ..
1115 $ cd ..
1077
1116
1078 Fixing a secret commit should replace it with another secret commit.
1117 Fixing a secret commit should replace it with another secret commit.
1079
1118
1080 $ hg init fixsecretcommit
1119 $ hg init fixsecretcommit
1081 $ cd fixsecretcommit
1120 $ cd fixsecretcommit
1082
1121
1083 $ printf "foo\n" > foo.whole
1122 $ printf "foo\n" > foo.whole
1084 $ hg commit -Aqm "add foo" --secret
1123 $ hg commit -Aqm "add foo" --secret
1085 $ hg fix -r .
1124 $ hg fix -r .
1086 $ hg log --template '{rev} {phase}\n'
1125 $ hg log --template '{rev} {phase}\n'
1087 1 secret
1126 1 secret
1088 0 secret
1127 0 secret
1089
1128
1090 $ cd ..
1129 $ cd ..
1091
1130
1092 We should also preserve phase when fixing a draft commit while the user has
1131 We should also preserve phase when fixing a draft commit while the user has
1093 their default set to secret.
1132 their default set to secret.
1094
1133
1095 $ hg init respectphasesnewcommit
1134 $ hg init respectphasesnewcommit
1096 $ cd respectphasesnewcommit
1135 $ cd respectphasesnewcommit
1097
1136
1098 $ printf "foo\n" > foo.whole
1137 $ printf "foo\n" > foo.whole
1099 $ hg commit -Aqm "add foo"
1138 $ hg commit -Aqm "add foo"
1100 $ hg --config phases.newcommit=secret fix -r .
1139 $ hg --config phases.newcommit=secret fix -r .
1101 $ hg log --template '{rev} {phase}\n'
1140 $ hg log --template '{rev} {phase}\n'
1102 1 draft
1141 1 draft
1103 0 draft
1142 0 draft
1104
1143
1105 $ cd ..
1144 $ cd ..
1106
1145
1107 Debug output should show what fixer commands are being subprocessed, which is
1146 Debug output should show what fixer commands are being subprocessed, which is
1108 useful for anyone trying to set up a new config.
1147 useful for anyone trying to set up a new config.
1109
1148
1110 $ hg init debugoutput
1149 $ hg init debugoutput
1111 $ cd debugoutput
1150 $ cd debugoutput
1112
1151
1113 $ printf "foo\nbar\nbaz\n" > foo.changed
1152 $ printf "foo\nbar\nbaz\n" > foo.changed
1114 $ hg commit -Aqm "foo"
1153 $ hg commit -Aqm "foo"
1115 $ printf "Foo\nbar\nBaz\n" > foo.changed
1154 $ printf "Foo\nbar\nBaz\n" > foo.changed
1116 $ hg --debug fix --working-dir
1155 $ hg --debug fix --working-dir
1117 subprocess: * $TESTTMP/uppercase.py 1-1 3-3 (glob)
1156 subprocess: * $TESTTMP/uppercase.py 1-1 3-3 (glob)
1118
1157
1119 $ cd ..
1158 $ cd ..
1120
1159
1121 Fixing an obsolete revision can cause divergence, so we abort unless the user
1160 Fixing an obsolete revision can cause divergence, so we abort unless the user
1122 configures to allow it. This is not yet smart enough to know whether there is a
1161 configures to allow it. This is not yet smart enough to know whether there is a
1123 successor, but even then it is not likely intentional or idiomatic to fix an
1162 successor, but even then it is not likely intentional or idiomatic to fix an
1124 obsolete revision.
1163 obsolete revision.
1125
1164
1126 $ hg init abortobsoleterev
1165 $ hg init abortobsoleterev
1127 $ cd abortobsoleterev
1166 $ cd abortobsoleterev
1128
1167
1129 $ printf "foo\n" > foo.changed
1168 $ printf "foo\n" > foo.changed
1130 $ hg commit -Aqm "foo"
1169 $ hg commit -Aqm "foo"
1131 $ hg ci --amend -m rewritten
1170 $ hg ci --amend -m rewritten
1132 $ hg --hidden fix -r 0
1171 $ hg --hidden fix -r 0
1133 abort: fixing obsolete revision could cause divergence
1172 abort: fixing obsolete revision could cause divergence
1134 [255]
1173 [255]
1135
1174
1136 $ hg --hidden fix -r 0 --config experimental.evolution.allowdivergence=true
1175 $ hg --hidden fix -r 0 --config experimental.evolution.allowdivergence=true
1137 2 new content-divergent changesets
1176 2 new content-divergent changesets
1138 $ hg cat -r tip foo.changed
1177 $ hg cat -r tip foo.changed
1139 FOO
1178 FOO
1140
1179
1141 $ cd ..
1180 $ cd ..
1142
1181
1143 Test all of the available substitution values for fixer commands.
1182 Test all of the available substitution values for fixer commands.
1144
1183
1145 $ hg init substitution
1184 $ hg init substitution
1146 $ cd substitution
1185 $ cd substitution
1147
1186
1148 $ mkdir foo
1187 $ mkdir foo
1149 $ printf "hello\ngoodbye\n" > foo/bar
1188 $ printf "hello\ngoodbye\n" > foo/bar
1150 $ hg add
1189 $ hg add
1151 adding foo/bar
1190 adding foo/bar
1152 $ hg --config "fix.fail:command=printf '%s\n' '{rootpath}' '{basename}'" \
1191 $ hg --config "fix.fail:command=printf '%s\n' '{rootpath}' '{basename}'" \
1153 > --config "fix.fail:linerange='{first}' '{last}'" \
1192 > --config "fix.fail:linerange='{first}' '{last}'" \
1154 > --config "fix.fail:pattern=foo/bar" \
1193 > --config "fix.fail:pattern=foo/bar" \
1155 > fix --working-dir
1194 > fix --working-dir
1156 $ cat foo/bar
1195 $ cat foo/bar
1157 foo/bar
1196 foo/bar
1158 bar
1197 bar
1159 1
1198 1
1160 2
1199 2
1161
1200
1162 $ cd ..
1201 $ cd ..
1163
1202
1164 The --base flag should allow picking the revisions to diff against for changed
1203 The --base flag should allow picking the revisions to diff against for changed
1165 files and incremental line formatting.
1204 files and incremental line formatting.
1166
1205
1167 $ hg init baseflag
1206 $ hg init baseflag
1168 $ cd baseflag
1207 $ cd baseflag
1169
1208
1170 $ printf "one\ntwo\n" > foo.changed
1209 $ printf "one\ntwo\n" > foo.changed
1171 $ printf "bar\n" > bar.changed
1210 $ printf "bar\n" > bar.changed
1172 $ hg commit -Aqm "first"
1211 $ hg commit -Aqm "first"
1173 $ printf "one\nTwo\n" > foo.changed
1212 $ printf "one\nTwo\n" > foo.changed
1174 $ hg commit -m "second"
1213 $ hg commit -m "second"
1175 $ hg fix -w --base .
1214 $ hg fix -w --base .
1176 $ hg status
1215 $ hg status
1177 $ hg fix -w --base null
1216 $ hg fix -w --base null
1178 $ cat foo.changed
1217 $ cat foo.changed
1179 ONE
1218 ONE
1180 TWO
1219 TWO
1181 $ cat bar.changed
1220 $ cat bar.changed
1182 BAR
1221 BAR
1183
1222
1184 $ cd ..
1223 $ cd ..
1185
1224
1186 If the user asks to fix the parent of another commit, they are asking to create
1225 If the user asks to fix the parent of another commit, they are asking to create
1187 an orphan. We must respect experimental.evolution.allowunstable.
1226 an orphan. We must respect experimental.evolution.allowunstable.
1188
1227
1189 $ hg init allowunstable
1228 $ hg init allowunstable
1190 $ cd allowunstable
1229 $ cd allowunstable
1191
1230
1192 $ printf "one\n" > foo.whole
1231 $ printf "one\n" > foo.whole
1193 $ hg commit -Aqm "first"
1232 $ hg commit -Aqm "first"
1194 $ printf "two\n" > foo.whole
1233 $ printf "two\n" > foo.whole
1195 $ hg commit -m "second"
1234 $ hg commit -m "second"
1196 $ hg --config experimental.evolution.allowunstable=False fix -r '.^'
1235 $ hg --config experimental.evolution.allowunstable=False fix -r '.^'
1197 abort: cannot fix changeset, as that will orphan 1 descendants
1236 abort: cannot fix changeset, as that will orphan 1 descendants
1198 (see 'hg help evolution.instability')
1237 (see 'hg help evolution.instability')
1199 [10]
1238 [10]
1200 $ hg fix -r '.^'
1239 $ hg fix -r '.^'
1201 1 new orphan changesets
1240 1 new orphan changesets
1202 $ hg cat -r 2 foo.whole
1241 $ hg cat -r 2 foo.whole
1203 ONE
1242 ONE
1204
1243
1205 $ cd ..
1244 $ cd ..
1206
1245
1207 The --base flag affects the set of files being fixed. So while the --whole flag
1246 The --base flag affects the set of files being fixed. So while the --whole flag
1208 makes the base irrelevant for changed line ranges, it still changes the
1247 makes the base irrelevant for changed line ranges, it still changes the
1209 meaning and effect of the command. In this example, no files or lines are fixed
1248 meaning and effect of the command. In this example, no files or lines are fixed
1210 until we specify the base, but then we do fix unchanged lines.
1249 until we specify the base, but then we do fix unchanged lines.
1211
1250
1212 $ hg init basewhole
1251 $ hg init basewhole
1213 $ cd basewhole
1252 $ cd basewhole
1214 $ printf "foo1\n" > foo.changed
1253 $ printf "foo1\n" > foo.changed
1215 $ hg commit -Aqm "first"
1254 $ hg commit -Aqm "first"
1216 $ printf "foo2\n" >> foo.changed
1255 $ printf "foo2\n" >> foo.changed
1217 $ printf "bar\n" > bar.changed
1256 $ printf "bar\n" > bar.changed
1218 $ hg commit -Aqm "second"
1257 $ hg commit -Aqm "second"
1219
1258
1220 $ hg fix --working-dir --whole
1259 $ hg fix --working-dir --whole
1221 $ cat *.changed
1260 $ cat *.changed
1222 bar
1261 bar
1223 foo1
1262 foo1
1224 foo2
1263 foo2
1225
1264
1226 $ hg fix --working-dir --base 0 --whole
1265 $ hg fix --working-dir --base 0 --whole
1227 $ cat *.changed
1266 $ cat *.changed
1228 BAR
1267 BAR
1229 FOO1
1268 FOO1
1230 FOO2
1269 FOO2
1231
1270
1232 $ cd ..
1271 $ cd ..
1233
1272
1234 The execution order of tools can be controlled. This example doesn't work if
1273 The execution order of tools can be controlled. This example doesn't work if
1235 you sort after truncating, but the config defines the correct order while the
1274 you sort after truncating, but the config defines the correct order while the
1236 definitions are out of order (which might imply the incorrect order given the
1275 definitions are out of order (which might imply the incorrect order given the
1237 implementation of fix). The goal is to use multiple tools to select the lowest
1276 implementation of fix). The goal is to use multiple tools to select the lowest
1238 5 numbers in the file.
1277 5 numbers in the file.
1239
1278
1240 $ hg init priorityexample
1279 $ hg init priorityexample
1241 $ cd priorityexample
1280 $ cd priorityexample
1242
1281
1243 $ cat >> .hg/hgrc <<EOF
1282 $ cat >> .hg/hgrc <<EOF
1244 > [fix]
1283 > [fix]
1245 > head:command = head -n 5
1284 > head:command = head -n 5
1246 > head:pattern = numbers.txt
1285 > head:pattern = numbers.txt
1247 > head:priority = 1
1286 > head:priority = 1
1248 > sort:command = sort -n
1287 > sort:command = sort -n
1249 > sort:pattern = numbers.txt
1288 > sort:pattern = numbers.txt
1250 > sort:priority = 2
1289 > sort:priority = 2
1251 > EOF
1290 > EOF
1252
1291
1253 $ printf "8\n2\n3\n6\n7\n4\n9\n5\n1\n0\n" > numbers.txt
1292 $ printf "8\n2\n3\n6\n7\n4\n9\n5\n1\n0\n" > numbers.txt
1254 $ hg add -q
1293 $ hg add -q
1255 $ hg fix -w
1294 $ hg fix -w
1256 $ cat numbers.txt
1295 $ cat numbers.txt
1257 0
1296 0
1258 1
1297 1
1259 2
1298 2
1260 3
1299 3
1261 4
1300 4
1262
1301
1263 And of course we should be able to break this by reversing the execution order.
1302 And of course we should be able to break this by reversing the execution order.
1264 Test negative priorities while we're at it.
1303 Test negative priorities while we're at it.
1265
1304
1266 $ cat >> .hg/hgrc <<EOF
1305 $ cat >> .hg/hgrc <<EOF
1267 > [fix]
1306 > [fix]
1268 > head:priority = -1
1307 > head:priority = -1
1269 > sort:priority = -2
1308 > sort:priority = -2
1270 > EOF
1309 > EOF
1271 $ printf "8\n2\n3\n6\n7\n4\n9\n5\n1\n0\n" > numbers.txt
1310 $ printf "8\n2\n3\n6\n7\n4\n9\n5\n1\n0\n" > numbers.txt
1272 $ hg fix -w
1311 $ hg fix -w
1273 $ cat numbers.txt
1312 $ cat numbers.txt
1274 2
1313 2
1275 3
1314 3
1276 6
1315 6
1277 7
1316 7
1278 8
1317 8
1279
1318
1280 $ cd ..
1319 $ cd ..
1281
1320
1282 It's possible for repeated applications of a fixer tool to create cycles in the
1321 It's possible for repeated applications of a fixer tool to create cycles in the
1283 generated content of a file. For example, two users with different versions of
1322 generated content of a file. For example, two users with different versions of
1284 a code formatter might fight over the formatting when they run hg fix. In the
1323 a code formatter might fight over the formatting when they run hg fix. In the
1285 absence of other changes, this means we could produce commits with the same
1324 absence of other changes, this means we could produce commits with the same
1286 hash in subsequent runs of hg fix. This is a problem unless we support
1325 hash in subsequent runs of hg fix. This is a problem unless we support
1287 obsolescence cycles well. We avoid this by adding an extra field to the
1326 obsolescence cycles well. We avoid this by adding an extra field to the
1288 successor which forces it to have a new hash. That's why this test creates
1327 successor which forces it to have a new hash. That's why this test creates
1289 three revisions instead of two.
1328 three revisions instead of two.
1290
1329
1291 $ hg init cyclictool
1330 $ hg init cyclictool
1292 $ cd cyclictool
1331 $ cd cyclictool
1293
1332
1294 $ cat >> .hg/hgrc <<EOF
1333 $ cat >> .hg/hgrc <<EOF
1295 > [fix]
1334 > [fix]
1296 > swapletters:command = tr ab ba
1335 > swapletters:command = tr ab ba
1297 > swapletters:pattern = foo
1336 > swapletters:pattern = foo
1298 > EOF
1337 > EOF
1299
1338
1300 $ echo ab > foo
1339 $ echo ab > foo
1301 $ hg commit -Aqm foo
1340 $ hg commit -Aqm foo
1302
1341
1303 $ hg fix -r 0
1342 $ hg fix -r 0
1304 $ hg fix -r 1
1343 $ hg fix -r 1
1305
1344
1306 $ hg cat -r 0 foo --hidden
1345 $ hg cat -r 0 foo --hidden
1307 ab
1346 ab
1308 $ hg cat -r 1 foo --hidden
1347 $ hg cat -r 1 foo --hidden
1309 ba
1348 ba
1310 $ hg cat -r 2 foo
1349 $ hg cat -r 2 foo
1311 ab
1350 ab
1312
1351
1313 $ cd ..
1352 $ cd ..
1314
1353
1315 We run fixer tools in the repo root so they can look for config files or other
1354 We run fixer tools in the repo root so they can look for config files or other
1316 important things in the working directory. This does NOT mean we are
1355 important things in the working directory. This does NOT mean we are
1317 reconstructing a working copy of every revision being fixed; we're just giving
1356 reconstructing a working copy of every revision being fixed; we're just giving
1318 the tool knowledge of the repo's location in case it can do something
1357 the tool knowledge of the repo's location in case it can do something
1319 reasonable with that.
1358 reasonable with that.
1320
1359
1321 $ hg init subprocesscwd
1360 $ hg init subprocesscwd
1322 $ cd subprocesscwd
1361 $ cd subprocesscwd
1323
1362
1324 $ cat >> .hg/hgrc <<EOF
1363 $ cat >> .hg/hgrc <<EOF
1325 > [fix]
1364 > [fix]
1326 > printcwd:command = "$PYTHON" -c "import os; print(os.getcwd())"
1365 > printcwd:command = "$PYTHON" -c "import os; print(os.getcwd())"
1327 > printcwd:pattern = relpath:foo/bar
1366 > printcwd:pattern = relpath:foo/bar
1328 > filesetpwd:command = "$PYTHON" -c "import os; print('fs: ' + os.getcwd())"
1367 > filesetpwd:command = "$PYTHON" -c "import os; print('fs: ' + os.getcwd())"
1329 > filesetpwd:pattern = set:**quux
1368 > filesetpwd:pattern = set:**quux
1330 > EOF
1369 > EOF
1331
1370
1332 $ mkdir foo
1371 $ mkdir foo
1333 $ printf "bar\n" > foo/bar
1372 $ printf "bar\n" > foo/bar
1334 $ printf "quux\n" > quux
1373 $ printf "quux\n" > quux
1335 $ hg commit -Aqm blah
1374 $ hg commit -Aqm blah
1336
1375
1337 $ hg fix -w -r . foo/bar
1376 $ hg fix -w -r . foo/bar
1338 $ hg cat -r tip foo/bar
1377 $ hg cat -r tip foo/bar
1339 $TESTTMP/subprocesscwd
1378 $TESTTMP/subprocesscwd
1340 $ cat foo/bar
1379 $ cat foo/bar
1341 $TESTTMP/subprocesscwd
1380 $TESTTMP/subprocesscwd
1342
1381
1343 $ cd foo
1382 $ cd foo
1344
1383
1345 $ hg fix -w -r . bar
1384 $ hg fix -w -r . bar
1346 $ hg cat -r tip bar ../quux
1385 $ hg cat -r tip bar ../quux
1347 $TESTTMP/subprocesscwd
1386 $TESTTMP/subprocesscwd
1348 quux
1387 quux
1349 $ cat bar ../quux
1388 $ cat bar ../quux
1350 $TESTTMP/subprocesscwd
1389 $TESTTMP/subprocesscwd
1351 quux
1390 quux
1352 $ echo modified > bar
1391 $ echo modified > bar
1353 $ hg fix -w bar
1392 $ hg fix -w bar
1354 $ cat bar
1393 $ cat bar
1355 $TESTTMP/subprocesscwd
1394 $TESTTMP/subprocesscwd
1356
1395
1357 Apparently fixing p1() and its descendants doesn't include wdir() unless
1396 Apparently fixing p1() and its descendants doesn't include wdir() unless
1358 explicitly stated.
1397 explicitly stated.
1359
1398
1360 $ hg fix -r '.::'
1399 $ hg fix -r '.::'
1361 $ hg cat -r . ../quux
1400 $ hg cat -r . ../quux
1362 quux
1401 quux
1363 $ hg cat -r tip ../quux
1402 $ hg cat -r tip ../quux
1364 fs: $TESTTMP/subprocesscwd
1403 fs: $TESTTMP/subprocesscwd
1365 $ cat ../quux
1404 $ cat ../quux
1366 quux
1405 quux
1367
1406
1368 Clean files are not fixed unless explicitly named
1407 Clean files are not fixed unless explicitly named
1369 $ echo 'dirty' > ../quux
1408 $ echo 'dirty' > ../quux
1370
1409
1371 $ hg fix --working-dir
1410 $ hg fix --working-dir
1372 $ cat ../quux
1411 $ cat ../quux
1373 fs: $TESTTMP/subprocesscwd
1412 fs: $TESTTMP/subprocesscwd
1374
1413
1375 $ cd ../..
1414 $ cd ../..
1376
1415
1377 Tools configured without a pattern are ignored. It would be too dangerous to
1416 Tools configured without a pattern are ignored. It would be too dangerous to
1378 run them on all files, because this might happen while testing a configuration
1417 run them on all files, because this might happen while testing a configuration
1379 that also deletes all of the file content. There is no reasonable subset of the
1418 that also deletes all of the file content. There is no reasonable subset of the
1380 files to use as a default. Users should be explicit about what files are
1419 files to use as a default. Users should be explicit about what files are
1381 affected by a tool. This test also confirms that we don't crash when the
1420 affected by a tool. This test also confirms that we don't crash when the
1382 pattern config is missing, and that we only warn about it once.
1421 pattern config is missing, and that we only warn about it once.
1383
1422
1384 $ hg init nopatternconfigured
1423 $ hg init nopatternconfigured
1385 $ cd nopatternconfigured
1424 $ cd nopatternconfigured
1386
1425
1387 $ printf "foo" > foo
1426 $ printf "foo" > foo
1388 $ printf "bar" > bar
1427 $ printf "bar" > bar
1389 $ hg add -q
1428 $ hg add -q
1390 $ hg fix --debug --working-dir --config "fix.nopattern:command=echo fixed"
1429 $ hg fix --debug --working-dir --config "fix.nopattern:command=echo fixed"
1391 fixer tool has no pattern configuration: nopattern
1430 fixer tool has no pattern configuration: nopattern
1392 $ cat foo bar
1431 $ cat foo bar
1393 foobar (no-eol)
1432 foobar (no-eol)
1394 $ hg fix --debug --working-dir --config "fix.nocommand:pattern=foo.bar"
1433 $ hg fix --debug --working-dir --config "fix.nocommand:pattern=foo.bar"
1395 fixer tool has no command configuration: nocommand
1434 fixer tool has no command configuration: nocommand
1396
1435
1397 $ cd ..
1436 $ cd ..
1398
1437
1399 Tools can be disabled. Disabled tools do nothing but print a debug message.
1438 Tools can be disabled. Disabled tools do nothing but print a debug message.
1400
1439
1401 $ hg init disabled
1440 $ hg init disabled
1402 $ cd disabled
1441 $ cd disabled
1403
1442
1404 $ printf "foo\n" > foo
1443 $ printf "foo\n" > foo
1405 $ hg add -q
1444 $ hg add -q
1406 $ hg fix --debug --working-dir --config "fix.disabled:command=echo fixed" \
1445 $ hg fix --debug --working-dir --config "fix.disabled:command=echo fixed" \
1407 > --config "fix.disabled:pattern=foo" \
1446 > --config "fix.disabled:pattern=foo" \
1408 > --config "fix.disabled:enabled=false"
1447 > --config "fix.disabled:enabled=false"
1409 ignoring disabled fixer tool: disabled
1448 ignoring disabled fixer tool: disabled
1410 $ cat foo
1449 $ cat foo
1411 foo
1450 foo
1412
1451
1413 $ cd ..
1452 $ cd ..
1414
1453
1415 Test that we can configure a fixer to affect all files regardless of the cwd.
1454 Test that we can configure a fixer to affect all files regardless of the cwd.
1416 The way we invoke matching must not prohibit this.
1455 The way we invoke matching must not prohibit this.
1417
1456
1418 $ hg init affectallfiles
1457 $ hg init affectallfiles
1419 $ cd affectallfiles
1458 $ cd affectallfiles
1420
1459
1421 $ mkdir foo bar
1460 $ mkdir foo bar
1422 $ printf "foo" > foo/file
1461 $ printf "foo" > foo/file
1423 $ printf "bar" > bar/file
1462 $ printf "bar" > bar/file
1424 $ printf "baz" > baz_file
1463 $ printf "baz" > baz_file
1425 $ hg add -q
1464 $ hg add -q
1426
1465
1427 $ cd bar
1466 $ cd bar
1428 $ hg fix --working-dir --config "fix.cooltool:command=echo fixed" \
1467 $ hg fix --working-dir --config "fix.cooltool:command=echo fixed" \
1429 > --config "fix.cooltool:pattern=glob:**"
1468 > --config "fix.cooltool:pattern=glob:**"
1430 $ cd ..
1469 $ cd ..
1431
1470
1432 $ cat foo/file
1471 $ cat foo/file
1433 fixed
1472 fixed
1434 $ cat bar/file
1473 $ cat bar/file
1435 fixed
1474 fixed
1436 $ cat baz_file
1475 $ cat baz_file
1437 fixed
1476 fixed
1438
1477
1439 $ cd ..
1478 $ cd ..
1440
1479
1441 Tools should be able to run on unchanged files, even if they set :linerange.
1480 Tools should be able to run on unchanged files, even if they set :linerange.
1442 This includes a corner case where deleted chunks of a file are not considered
1481 This includes a corner case where deleted chunks of a file are not considered
1443 changes.
1482 changes.
1444
1483
1445 $ hg init skipclean
1484 $ hg init skipclean
1446 $ cd skipclean
1485 $ cd skipclean
1447
1486
1448 $ printf "a\nb\nc\n" > foo
1487 $ printf "a\nb\nc\n" > foo
1449 $ printf "a\nb\nc\n" > bar
1488 $ printf "a\nb\nc\n" > bar
1450 $ printf "a\nb\nc\n" > baz
1489 $ printf "a\nb\nc\n" > baz
1451 $ hg commit -Aqm "base"
1490 $ hg commit -Aqm "base"
1452
1491
1453 $ printf "a\nc\n" > foo
1492 $ printf "a\nc\n" > foo
1454 $ printf "a\nx\nc\n" > baz
1493 $ printf "a\nx\nc\n" > baz
1455
1494
1456 $ cat >> print.py <<EOF
1495 $ cat >> print.py <<EOF
1457 > import sys
1496 > import sys
1458 > for a in sys.argv[1:]:
1497 > for a in sys.argv[1:]:
1459 > print(a)
1498 > print(a)
1460 > EOF
1499 > EOF
1461
1500
1462 $ hg fix --working-dir foo bar baz \
1501 $ hg fix --working-dir foo bar baz \
1463 > --config "fix.changedlines:command=\"$PYTHON\" print.py \"Line ranges:\"" \
1502 > --config "fix.changedlines:command=\"$PYTHON\" print.py \"Line ranges:\"" \
1464 > --config 'fix.changedlines:linerange="{first} through {last}"' \
1503 > --config 'fix.changedlines:linerange="{first} through {last}"' \
1465 > --config 'fix.changedlines:pattern=glob:**' \
1504 > --config 'fix.changedlines:pattern=glob:**' \
1466 > --config 'fix.changedlines:skipclean=false'
1505 > --config 'fix.changedlines:skipclean=false'
1467
1506
1468 $ cat foo
1507 $ cat foo
1469 Line ranges:
1508 Line ranges:
1470 $ cat bar
1509 $ cat bar
1471 Line ranges:
1510 Line ranges:
1472 $ cat baz
1511 $ cat baz
1473 Line ranges:
1512 Line ranges:
1474 2 through 2
1513 2 through 2
1475
1514
1476 $ cd ..
1515 $ cd ..
1477
1516
1478 Test various cases around merges. We were previously dropping files if they were
1517 Test various cases around merges. We were previously dropping files if they were
1479 created on only the p2 side of the merge, so let's test permutations of:
1518 created on only the p2 side of the merge, so let's test permutations of:
1480 * added, was fixed
1519 * added, was fixed
1481 * added, considered for fixing but was already good
1520 * added, considered for fixing but was already good
1482 * added, not considered for fixing
1521 * added, not considered for fixing
1483 * modified, was fixed
1522 * modified, was fixed
1484 * modified, considered for fixing but was already good
1523 * modified, considered for fixing but was already good
1485 * modified, not considered for fixing
1524 * modified, not considered for fixing
1486
1525
1487 Before the bug was fixed where we would drop files, this test demonstrated the
1526 Before the bug was fixed where we would drop files, this test demonstrated the
1488 following issues:
1527 following issues:
1489 * new_in_r1.ignored, new_in_r1_already_good.changed, and
1528 * new_in_r1.ignored, new_in_r1_already_good.changed, and
1490 > mod_in_r1_already_good.changed were NOT in the manifest for the merge commit
1529 > mod_in_r1_already_good.changed were NOT in the manifest for the merge commit
1491 * mod_in_r1.ignored had its contents from r0, NOT r1.
1530 * mod_in_r1.ignored had its contents from r0, NOT r1.
1492
1531
1493 We're also setting a named branch for every commit to demonstrate that the
1532 We're also setting a named branch for every commit to demonstrate that the
1494 branch is kept intact and there aren't issues updating to another branch in the
1533 branch is kept intact and there aren't issues updating to another branch in the
1495 middle of fix.
1534 middle of fix.
1496
1535
1497 $ hg init merge_keeps_files
1536 $ hg init merge_keeps_files
1498 $ cd merge_keeps_files
1537 $ cd merge_keeps_files
1499 $ for f in r0 mod_in_r1 mod_in_r2 mod_in_merge mod_in_child; do
1538 $ for f in r0 mod_in_r1 mod_in_r2 mod_in_merge mod_in_child; do
1500 > for c in changed whole ignored; do
1539 > for c in changed whole ignored; do
1501 > printf "hello\n" > $f.$c
1540 > printf "hello\n" > $f.$c
1502 > done
1541 > done
1503 > printf "HELLO\n" > "mod_in_${f}_already_good.changed"
1542 > printf "HELLO\n" > "mod_in_${f}_already_good.changed"
1504 > done
1543 > done
1505 $ hg branch -q r0
1544 $ hg branch -q r0
1506 $ hg ci -Aqm 'r0'
1545 $ hg ci -Aqm 'r0'
1507 $ hg phase -p
1546 $ hg phase -p
1508 $ make_test_files() {
1547 $ make_test_files() {
1509 > printf "world\n" >> "mod_in_$1.changed"
1548 > printf "world\n" >> "mod_in_$1.changed"
1510 > printf "world\n" >> "mod_in_$1.whole"
1549 > printf "world\n" >> "mod_in_$1.whole"
1511 > printf "world\n" >> "mod_in_$1.ignored"
1550 > printf "world\n" >> "mod_in_$1.ignored"
1512 > printf "WORLD\n" >> "mod_in_$1_already_good.changed"
1551 > printf "WORLD\n" >> "mod_in_$1_already_good.changed"
1513 > printf "new in $1\n" > "new_in_$1.changed"
1552 > printf "new in $1\n" > "new_in_$1.changed"
1514 > printf "new in $1\n" > "new_in_$1.whole"
1553 > printf "new in $1\n" > "new_in_$1.whole"
1515 > printf "new in $1\n" > "new_in_$1.ignored"
1554 > printf "new in $1\n" > "new_in_$1.ignored"
1516 > printf "ALREADY GOOD, NEW IN THIS REV\n" > "new_in_$1_already_good.changed"
1555 > printf "ALREADY GOOD, NEW IN THIS REV\n" > "new_in_$1_already_good.changed"
1517 > }
1556 > }
1518 $ make_test_commit() {
1557 $ make_test_commit() {
1519 > make_test_files "$1"
1558 > make_test_files "$1"
1520 > hg branch -q "$1"
1559 > hg branch -q "$1"
1521 > hg ci -Aqm "$2"
1560 > hg ci -Aqm "$2"
1522 > }
1561 > }
1523 $ make_test_commit r1 "merge me, pt1"
1562 $ make_test_commit r1 "merge me, pt1"
1524 $ hg co -q ".^"
1563 $ hg co -q ".^"
1525 $ make_test_commit r2 "merge me, pt2"
1564 $ make_test_commit r2 "merge me, pt2"
1526 $ hg merge -qr 1
1565 $ hg merge -qr 1
1527 $ make_test_commit merge "evil merge"
1566 $ make_test_commit merge "evil merge"
1528 $ make_test_commit child "child of merge"
1567 $ make_test_commit child "child of merge"
1529 $ make_test_files wdir
1568 $ make_test_files wdir
1530 $ hg fix -r 'not public()' -w
1569 $ hg fix -r 'not public()' -w
1531 $ hg log -G -T'{rev}:{shortest(node,8)}: branch:{branch} desc:{desc}'
1570 $ hg log -G -T'{rev}:{shortest(node,8)}: branch:{branch} desc:{desc}'
1532 @ 8:c22ce900: branch:child desc:child of merge
1571 @ 8:c22ce900: branch:child desc:child of merge
1533 |
1572 |
1534 o 7:5a30615a: branch:merge desc:evil merge
1573 o 7:5a30615a: branch:merge desc:evil merge
1535 |\
1574 |\
1536 | o 6:4e5acdc4: branch:r2 desc:merge me, pt2
1575 | o 6:4e5acdc4: branch:r2 desc:merge me, pt2
1537 | |
1576 | |
1538 o | 5:eea01878: branch:r1 desc:merge me, pt1
1577 o | 5:eea01878: branch:r1 desc:merge me, pt1
1539 |/
1578 |/
1540 o 0:0c548d87: branch:r0 desc:r0
1579 o 0:0c548d87: branch:r0 desc:r0
1541
1580
1542 $ hg files -r tip
1581 $ hg files -r tip
1543 mod_in_child.changed
1582 mod_in_child.changed
1544 mod_in_child.ignored
1583 mod_in_child.ignored
1545 mod_in_child.whole
1584 mod_in_child.whole
1546 mod_in_child_already_good.changed
1585 mod_in_child_already_good.changed
1547 mod_in_merge.changed
1586 mod_in_merge.changed
1548 mod_in_merge.ignored
1587 mod_in_merge.ignored
1549 mod_in_merge.whole
1588 mod_in_merge.whole
1550 mod_in_merge_already_good.changed
1589 mod_in_merge_already_good.changed
1551 mod_in_mod_in_child_already_good.changed
1590 mod_in_mod_in_child_already_good.changed
1552 mod_in_mod_in_merge_already_good.changed
1591 mod_in_mod_in_merge_already_good.changed
1553 mod_in_mod_in_r1_already_good.changed
1592 mod_in_mod_in_r1_already_good.changed
1554 mod_in_mod_in_r2_already_good.changed
1593 mod_in_mod_in_r2_already_good.changed
1555 mod_in_r0_already_good.changed
1594 mod_in_r0_already_good.changed
1556 mod_in_r1.changed
1595 mod_in_r1.changed
1557 mod_in_r1.ignored
1596 mod_in_r1.ignored
1558 mod_in_r1.whole
1597 mod_in_r1.whole
1559 mod_in_r1_already_good.changed
1598 mod_in_r1_already_good.changed
1560 mod_in_r2.changed
1599 mod_in_r2.changed
1561 mod_in_r2.ignored
1600 mod_in_r2.ignored
1562 mod_in_r2.whole
1601 mod_in_r2.whole
1563 mod_in_r2_already_good.changed
1602 mod_in_r2_already_good.changed
1564 new_in_child.changed
1603 new_in_child.changed
1565 new_in_child.ignored
1604 new_in_child.ignored
1566 new_in_child.whole
1605 new_in_child.whole
1567 new_in_child_already_good.changed
1606 new_in_child_already_good.changed
1568 new_in_merge.changed
1607 new_in_merge.changed
1569 new_in_merge.ignored
1608 new_in_merge.ignored
1570 new_in_merge.whole
1609 new_in_merge.whole
1571 new_in_merge_already_good.changed
1610 new_in_merge_already_good.changed
1572 new_in_r1.changed
1611 new_in_r1.changed
1573 new_in_r1.ignored
1612 new_in_r1.ignored
1574 new_in_r1.whole
1613 new_in_r1.whole
1575 new_in_r1_already_good.changed
1614 new_in_r1_already_good.changed
1576 new_in_r2.changed
1615 new_in_r2.changed
1577 new_in_r2.ignored
1616 new_in_r2.ignored
1578 new_in_r2.whole
1617 new_in_r2.whole
1579 new_in_r2_already_good.changed
1618 new_in_r2_already_good.changed
1580 r0.changed
1619 r0.changed
1581 r0.ignored
1620 r0.ignored
1582 r0.whole
1621 r0.whole
1583 $ for f in "$(hg files -r tip)"; do hg cat -r tip $f -T'{path}:\n{data}\n'; done
1622 $ for f in "$(hg files -r tip)"; do hg cat -r tip $f -T'{path}:\n{data}\n'; done
1584 mod_in_child.changed:
1623 mod_in_child.changed:
1585 hello
1624 hello
1586 WORLD
1625 WORLD
1587
1626
1588 mod_in_child.ignored:
1627 mod_in_child.ignored:
1589 hello
1628 hello
1590 world
1629 world
1591
1630
1592 mod_in_child.whole:
1631 mod_in_child.whole:
1593 HELLO
1632 HELLO
1594 WORLD
1633 WORLD
1595
1634
1596 mod_in_child_already_good.changed:
1635 mod_in_child_already_good.changed:
1597 WORLD
1636 WORLD
1598
1637
1599 mod_in_merge.changed:
1638 mod_in_merge.changed:
1600 hello
1639 hello
1601 WORLD
1640 WORLD
1602
1641
1603 mod_in_merge.ignored:
1642 mod_in_merge.ignored:
1604 hello
1643 hello
1605 world
1644 world
1606
1645
1607 mod_in_merge.whole:
1646 mod_in_merge.whole:
1608 HELLO
1647 HELLO
1609 WORLD
1648 WORLD
1610
1649
1611 mod_in_merge_already_good.changed:
1650 mod_in_merge_already_good.changed:
1612 WORLD
1651 WORLD
1613
1652
1614 mod_in_mod_in_child_already_good.changed:
1653 mod_in_mod_in_child_already_good.changed:
1615 HELLO
1654 HELLO
1616
1655
1617 mod_in_mod_in_merge_already_good.changed:
1656 mod_in_mod_in_merge_already_good.changed:
1618 HELLO
1657 HELLO
1619
1658
1620 mod_in_mod_in_r1_already_good.changed:
1659 mod_in_mod_in_r1_already_good.changed:
1621 HELLO
1660 HELLO
1622
1661
1623 mod_in_mod_in_r2_already_good.changed:
1662 mod_in_mod_in_r2_already_good.changed:
1624 HELLO
1663 HELLO
1625
1664
1626 mod_in_r0_already_good.changed:
1665 mod_in_r0_already_good.changed:
1627 HELLO
1666 HELLO
1628
1667
1629 mod_in_r1.changed:
1668 mod_in_r1.changed:
1630 hello
1669 hello
1631 WORLD
1670 WORLD
1632
1671
1633 mod_in_r1.ignored:
1672 mod_in_r1.ignored:
1634 hello
1673 hello
1635 world
1674 world
1636
1675
1637 mod_in_r1.whole:
1676 mod_in_r1.whole:
1638 HELLO
1677 HELLO
1639 WORLD
1678 WORLD
1640
1679
1641 mod_in_r1_already_good.changed:
1680 mod_in_r1_already_good.changed:
1642 WORLD
1681 WORLD
1643
1682
1644 mod_in_r2.changed:
1683 mod_in_r2.changed:
1645 hello
1684 hello
1646 WORLD
1685 WORLD
1647
1686
1648 mod_in_r2.ignored:
1687 mod_in_r2.ignored:
1649 hello
1688 hello
1650 world
1689 world
1651
1690
1652 mod_in_r2.whole:
1691 mod_in_r2.whole:
1653 HELLO
1692 HELLO
1654 WORLD
1693 WORLD
1655
1694
1656 mod_in_r2_already_good.changed:
1695 mod_in_r2_already_good.changed:
1657 WORLD
1696 WORLD
1658
1697
1659 new_in_child.changed:
1698 new_in_child.changed:
1660 NEW IN CHILD
1699 NEW IN CHILD
1661
1700
1662 new_in_child.ignored:
1701 new_in_child.ignored:
1663 new in child
1702 new in child
1664
1703
1665 new_in_child.whole:
1704 new_in_child.whole:
1666 NEW IN CHILD
1705 NEW IN CHILD
1667
1706
1668 new_in_child_already_good.changed:
1707 new_in_child_already_good.changed:
1669 ALREADY GOOD, NEW IN THIS REV
1708 ALREADY GOOD, NEW IN THIS REV
1670
1709
1671 new_in_merge.changed:
1710 new_in_merge.changed:
1672 NEW IN MERGE
1711 NEW IN MERGE
1673
1712
1674 new_in_merge.ignored:
1713 new_in_merge.ignored:
1675 new in merge
1714 new in merge
1676
1715
1677 new_in_merge.whole:
1716 new_in_merge.whole:
1678 NEW IN MERGE
1717 NEW IN MERGE
1679
1718
1680 new_in_merge_already_good.changed:
1719 new_in_merge_already_good.changed:
1681 ALREADY GOOD, NEW IN THIS REV
1720 ALREADY GOOD, NEW IN THIS REV
1682
1721
1683 new_in_r1.changed:
1722 new_in_r1.changed:
1684 NEW IN R1
1723 NEW IN R1
1685
1724
1686 new_in_r1.ignored:
1725 new_in_r1.ignored:
1687 new in r1
1726 new in r1
1688
1727
1689 new_in_r1.whole:
1728 new_in_r1.whole:
1690 NEW IN R1
1729 NEW IN R1
1691
1730
1692 new_in_r1_already_good.changed:
1731 new_in_r1_already_good.changed:
1693 ALREADY GOOD, NEW IN THIS REV
1732 ALREADY GOOD, NEW IN THIS REV
1694
1733
1695 new_in_r2.changed:
1734 new_in_r2.changed:
1696 NEW IN R2
1735 NEW IN R2
1697
1736
1698 new_in_r2.ignored:
1737 new_in_r2.ignored:
1699 new in r2
1738 new in r2
1700
1739
1701 new_in_r2.whole:
1740 new_in_r2.whole:
1702 NEW IN R2
1741 NEW IN R2
1703
1742
1704 new_in_r2_already_good.changed:
1743 new_in_r2_already_good.changed:
1705 ALREADY GOOD, NEW IN THIS REV
1744 ALREADY GOOD, NEW IN THIS REV
1706
1745
1707 r0.changed:
1746 r0.changed:
1708 hello
1747 hello
1709
1748
1710 r0.ignored:
1749 r0.ignored:
1711 hello
1750 hello
1712
1751
1713 r0.whole:
1752 r0.whole:
1714 hello
1753 hello
1715
1754
General Comments 0
You need to be logged in to leave comments. Login now