merge: split up checks for unknown and ignored files that differ
In some real-world cases it is preferable to allow overwriting ignored files
while continuing to abort on unknown files. This primarily happens when we're
replacing build artifacts (which are ignored) with checked in files, but
continuing to abort on differing files that aren't ignored.
We're redefining merge.checkunknown to only control the behavior for files
that aren't ignored. That's fine because this config was only very recently
introduced and has not made its way into any Mercurial releases yet.
--- a/mercurial/help/config.txt Tue Jan 12 18:17:07 2016 -0800
+++ b/mercurial/help/config.txt Tue Jan 12 18:38:49 2016 -0800
@@ -990,14 +990,20 @@
This section specifies behavior during merges and updates.
-``checkunknown``
- Controls behavior when an unknown file on disk has the same name as a tracked
+``checkignored``
+ Controls behavior when an ignored file on disk has the same name as a tracked
file in the changeset being merged or updated to, and has different
contents. Options are ``abort``, ``warn`` and ``ignore``. With ``abort``,
abort on such files. With ``warn``, warn on such files and back them up as
.orig. With ``ignore``, don't print a warning and back them up as
.orig. (default: ``abort``)
+``checkunknown``
+ Controls behavior when an unknown file that isn't ignored has the same name
+ as a tracked file in the changeset being merged or updated to, and has
+ different contents. Similar to ``merge.checkignored``, except for files that
+ are not ignored. (default: ``abort``)
+
``merge-patterns``
------------------
--- a/mercurial/merge.py Tue Jan 12 18:17:07 2016 -0800
+++ b/mercurial/merge.py Tue Jan 12 18:38:49 2016 -0800
@@ -591,7 +591,8 @@
elif config == 'warn':
warnconflicts.update(conflicts)
- config = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
+ unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
+ ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
for f, (m, args, msg) in actions.iteritems():
if m in ('c', 'dc'):
if _checkunknownfile(repo, wctx, mctx, f):
@@ -600,7 +601,11 @@
if _checkunknownfile(repo, wctx, mctx, f, args[0]):
conflicts.add(f)
- collectconflicts(conflicts, config)
+ ignoredconflicts = set([c for c in conflicts
+ if repo.dirstate._ignore(c)])
+ unknownconflicts = conflicts - ignoredconflicts
+ collectconflicts(ignoredconflicts, ignoredconfig)
+ collectconflicts(unknownconflicts, unknownconfig)
for f in sorted(abortconflicts):
repo.ui.warn(_("%s: untracked file differs\n") % f)
if abortconflicts:
--- a/tests/test-merge1.t Tue Jan 12 18:17:07 2016 -0800
+++ b/tests/test-merge1.t Tue Jan 12 18:38:49 2016 -0800
@@ -152,10 +152,78 @@
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
+merge.checkignored
+ $ hg up --clean 1
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ cat >> .hgignore << EOF
+ > remoteignored
+ > EOF
+ $ echo This is file localignored3 > localignored
+ $ echo This is file remoteignored3 > remoteignored
+ $ hg add .hgignore localignored remoteignored
+ $ hg commit -m "commit #3"
+
+ $ hg up 2
+ 1 files updated, 0 files merged, 4 files removed, 0 files unresolved
+ $ cat >> .hgignore << EOF
+ > localignored
+ > EOF
+ $ hg add .hgignore
+ $ hg commit -m "commit #4"
+
+remote .hgignore shouldn't be used for determining whether a file is ignored
+ $ echo This is file remoteignored4 > remoteignored
+ $ hg merge 3 --config merge.checkignored=ignore --config merge.checkunknown=abort
+ remoteignored: untracked file differs
+ abort: untracked files in working directory differ from files in requested revision
+ [255]
+ $ hg merge 3 --config merge.checkignored=abort --config merge.checkunknown=ignore
+ merging .hgignore
+ merging for .hgignore
+ 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ cat remoteignored
+ This is file remoteignored3
+ $ cat remoteignored.orig
+ This is file remoteignored4
+ $ rm remoteignored.orig
+
+local .hgignore should be used for that
+ $ hg up --clean 4
+ 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ echo This is file localignored4 > localignored
+also test other conflicting files to see we output the full set of warnings
+ $ echo This is file b2 > b
+ $ hg merge 3 --config merge.checkignored=abort --config merge.checkunknown=abort
+ b: untracked file differs
+ localignored: untracked file differs
+ abort: untracked files in working directory differ from files in requested revision
+ [255]
+ $ hg merge 3 --config merge.checkignored=abort --config merge.checkunknown=ignore
+ localignored: untracked file differs
+ abort: untracked files in working directory differ from files in requested revision
+ [255]
+ $ hg merge 3 --config merge.checkignored=warn --config merge.checkunknown=abort
+ b: untracked file differs
+ abort: untracked files in working directory differ from files in requested revision
+ [255]
+ $ hg merge 3 --config merge.checkignored=warn --config merge.checkunknown=warn
+ b: replacing untracked file
+ localignored: replacing untracked file
+ merging .hgignore
+ merging for .hgignore
+ 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ cat localignored
+ This is file localignored3
+ $ cat localignored.orig
+ This is file localignored4
+ $ rm localignored.orig
+
$ cat b.orig
This is file b2
$ hg up --clean 2
- 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
$ mv b.orig b
this merge of b should work