Patchwork [7,of,7,checkunknown] merge: add options to warn or ignore on colliding unknown files

login
register
mail settings
Submitter Siddharth Agarwal
Date Jan. 2, 2016, 11:25 a.m.
Message ID <a14ba255c9bb6fd11e93.1451733911@dev666.prn1.facebook.com>
Download mbox | patch
Permalink /patch/12475/
State Accepted
Delegated to: Martin von Zweigbergk
Headers show

Comments

Siddharth Agarwal - Jan. 2, 2016, 11:25 a.m.
# HG changeset patch
# User Siddharth Agarwal <sid0@fb.com>
# Date 1451733112 28800
#      Sat Jan 02 03:11:52 2016 -0800
# Node ID a14ba255c9bb6fd11e93ec3ecaaa88ff077d0284
# Parent  d2b847835e055f08e538ecf3ad336b7a1cb57e30
# Available At http://42.netv6.net/sid0-wip/hg/
#              hg pull http://42.netv6.net/sid0-wip/hg/ -r a14ba255c9bb
merge: add options to warn or ignore on colliding unknown files

A 'colliding unknown file' is a file that meets all of the following
conditions:

- is untracked or ignored on disk
- is present in the changeset being merged or updated to
- has different contents

Previously, we would always abort whenever we saw such files. With this config
option we can choose to warn and back the unknown files up instead, or even
forgo the warning entirely and silently back the unknown files up.

Common use cases for this configuration include a large scale transition of
formerly ignored unknown files to tracked files. In some cases the files can be
given new names, but in other cases, external "convention over configuration"
constraints have determined that the file must retain the same name as before.
Martin von Zweigbergk - Jan. 6, 2016, 6:47 a.m.
Pushed to the clowncopter, thanks!

On Sat, Jan 2, 2016 at 3:29 AM Siddharth Agarwal <sid0@fb.com> wrote:

> # HG changeset patch
> # User Siddharth Agarwal <sid0@fb.com>
> # Date 1451733112 28800
> #      Sat Jan 02 03:11:52 2016 -0800
> # Node ID a14ba255c9bb6fd11e93ec3ecaaa88ff077d0284
> # Parent  d2b847835e055f08e538ecf3ad336b7a1cb57e30
> # Available At http://42.netv6.net/sid0-wip/hg/
> #              hg pull http://42.netv6.net/sid0-wip/hg/ -r a14ba255c9bb
> merge: add options to warn or ignore on colliding unknown files
>
> A 'colliding unknown file' is a file that meets all of the following
> conditions:
>
> - is untracked or ignored on disk
> - is present in the changeset being merged or updated to
> - has different contents
>
> Previously, we would always abort whenever we saw such files. With this
> config
> option we can choose to warn and back the unknown files up instead, or even
> forgo the warning entirely and silently back the unknown files up.
>
> Common use cases for this configuration include a large scale transition of
> formerly ignored unknown files to tracked files. In some cases the files
> can be
> given new names, but in other cases, external "convention over
> configuration"
> constraints have determined that the file must retain the same name as
> before.
>
> diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt
> --- a/mercurial/help/config.txt
> +++ b/mercurial/help/config.txt
> @@ -987,6 +987,19 @@ proxy.
>      Optional. Always use the proxy, even for localhost and any entries
>      in ``http_proxy.no``. (default: False)
>
> +``merge``
> +---------
> +
> +This section specifies behavior during merges and updates.
> +
> +``checkunknown``
> +   Controls behavior when an unknown 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``)
> +
>  ``merge-patterns``
>  ------------------
>
> diff --git a/mercurial/merge.py b/mercurial/merge.py
> --- a/mercurial/merge.py
> +++ b/mercurial/merge.py
> @@ -26,6 +26,7 @@ from . import (
>      error,
>      filemerge,
>      obsolete,
> +    scmutil,
>      subrepo,
>      util,
>      worker,
> @@ -572,6 +573,14 @@ def _checkunknownfiles(repo, wctx, mctx,
>      """
>      conflicts = set()
>      if not force:
> +        config = repo.ui.config('merge', 'checkunknown', default='abort')
> +        valid = ['abort', 'ignore', 'warn']
> +        if config not in valid:
> +            validstr = ', '.join(["'" + v + "'" for v in valid])
> +            raise error.ConfigError(_("merge.checkunknown not valid "
> +                                      "('%s' is none of %s)")
> +                                    % (config, validstr))
> +
>          for f, (m, args, msg) in actions.iteritems():
>              if m in ('c', 'dc'):
>                  if _checkunknownfile(repo, wctx, mctx, f):
> @@ -580,16 +589,21 @@ def _checkunknownfiles(repo, wctx, mctx,
>                  if _checkunknownfile(repo, wctx, mctx, f, args[0]):
>                      conflicts.add(f)
>
> -        for f in sorted(conflicts):
> -            repo.ui.warn(_("%s: untracked file differs\n") % f)
> -        if conflicts:
> -            raise error.Abort(_("untracked files in working directory
> differ "
> -                                "from files in requested revision"))
> +        if config == 'abort':
> +            for f in sorted(conflicts):
> +                repo.ui.warn(_("%s: untracked file differs\n") % f)
> +            if conflicts:
> +                raise error.Abort(_("untracked files in working directory
> "
> +                                    "differ from files in requested
> revision"))
> +        elif config == 'warn':
> +            for f in sorted(conflicts):
> +                repo.ui.warn(_("%s: replacing untracked file\n") % f)
>
>      for f, (m, args, msg) in actions.iteritems():
> +        backup = f in conflicts
>          if m == 'c':
>              flags, = args
> -            actions[f] = ('g', (flags, False), msg)
> +            actions[f] = ('g', (flags, backup), msg)
>          elif m == 'cm':
>              fl2, anc = args
>              different = _checkunknownfile(repo, wctx, mctx, f)
> @@ -597,7 +611,7 @@ def _checkunknownfiles(repo, wctx, mctx,
>                  actions[f] = ('m', (f, f, None, False, anc),
>                                "remote differs from untracked local")
>              else:
> -                actions[f] = ('g', (fl2, False), "remote created")
> +                actions[f] = ('g', (fl2, backup), "remote created")
>
>  def _forgetremoved(wctx, mctx, branchmerge):
>      """
> diff --git a/tests/test-merge1.t b/tests/test-merge1.t
> --- a/tests/test-merge1.t
> +++ b/tests/test-merge1.t
> @@ -124,7 +124,43 @@ symlinks shouldn't be followed
>    $ echo This is file b2 > b
>  #endif
>
> -merge of b expected
> +bad config
> +  $ hg merge 1 --config merge.checkunknown=x
> +  abort: merge.checkunknown not valid ('x' is none of 'abort', 'ignore',
> 'warn')
> +  [255]
> +this merge should fail
> +  $ hg merge 1 --config merge.checkunknown=abort
> +  b: untracked file differs
> +  abort: untracked files in working directory differ from files in
> requested revision
> +  [255]
> +
> +this merge should warn
> +  $ hg merge 1 --config merge.checkunknown=warn
> +  b: replacing untracked file
> +  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +  (branch merge, don't forget to commit)
> +  $ cat b.orig
> +  This is file b2
> +  $ hg up --clean 2
> +  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
> +  $ mv b.orig b
> +
> +this merge should silently ignore
> +  $ cat b
> +  This is file b2
> +  $ hg merge 1 --config merge.checkunknown=ignore
> +  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> +  (branch merge, don't forget to commit)
> +
> +  $ cat b.orig
> +  This is file b2
> +  $ hg up --clean 2
> +  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
> +  $ mv b.orig b
> +
> +this merge of b should work
> +  $ cat b
> +  This is file b2
>    $ hg merge -f 1
>    merging b
>    merging for b
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> https://selenic.com/mailman/listinfo/mercurial-devel
>

Patch

diff --git a/mercurial/help/config.txt b/mercurial/help/config.txt
--- a/mercurial/help/config.txt
+++ b/mercurial/help/config.txt
@@ -987,6 +987,19 @@  proxy.
     Optional. Always use the proxy, even for localhost and any entries
     in ``http_proxy.no``. (default: False)
 
+``merge``
+---------
+
+This section specifies behavior during merges and updates.
+
+``checkunknown``
+   Controls behavior when an unknown 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``)
+
 ``merge-patterns``
 ------------------
 
diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -26,6 +26,7 @@  from . import (
     error,
     filemerge,
     obsolete,
+    scmutil,
     subrepo,
     util,
     worker,
@@ -572,6 +573,14 @@  def _checkunknownfiles(repo, wctx, mctx,
     """
     conflicts = set()
     if not force:
+        config = repo.ui.config('merge', 'checkunknown', default='abort')
+        valid = ['abort', 'ignore', 'warn']
+        if config not in valid:
+            validstr = ', '.join(["'" + v + "'" for v in valid])
+            raise error.ConfigError(_("merge.checkunknown not valid "
+                                      "('%s' is none of %s)")
+                                    % (config, validstr))
+
         for f, (m, args, msg) in actions.iteritems():
             if m in ('c', 'dc'):
                 if _checkunknownfile(repo, wctx, mctx, f):
@@ -580,16 +589,21 @@  def _checkunknownfiles(repo, wctx, mctx,
                 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
                     conflicts.add(f)
 
-        for f in sorted(conflicts):
-            repo.ui.warn(_("%s: untracked file differs\n") % f)
-        if conflicts:
-            raise error.Abort(_("untracked files in working directory differ "
-                                "from files in requested revision"))
+        if config == 'abort':
+            for f in sorted(conflicts):
+                repo.ui.warn(_("%s: untracked file differs\n") % f)
+            if conflicts:
+                raise error.Abort(_("untracked files in working directory "
+                                    "differ from files in requested revision"))
+        elif config == 'warn':
+            for f in sorted(conflicts):
+                repo.ui.warn(_("%s: replacing untracked file\n") % f)
 
     for f, (m, args, msg) in actions.iteritems():
+        backup = f in conflicts
         if m == 'c':
             flags, = args
-            actions[f] = ('g', (flags, False), msg)
+            actions[f] = ('g', (flags, backup), msg)
         elif m == 'cm':
             fl2, anc = args
             different = _checkunknownfile(repo, wctx, mctx, f)
@@ -597,7 +611,7 @@  def _checkunknownfiles(repo, wctx, mctx,
                 actions[f] = ('m', (f, f, None, False, anc),
                               "remote differs from untracked local")
             else:
-                actions[f] = ('g', (fl2, False), "remote created")
+                actions[f] = ('g', (fl2, backup), "remote created")
 
 def _forgetremoved(wctx, mctx, branchmerge):
     """
diff --git a/tests/test-merge1.t b/tests/test-merge1.t
--- a/tests/test-merge1.t
+++ b/tests/test-merge1.t
@@ -124,7 +124,43 @@  symlinks shouldn't be followed
   $ echo This is file b2 > b
 #endif
 
-merge of b expected
+bad config
+  $ hg merge 1 --config merge.checkunknown=x
+  abort: merge.checkunknown not valid ('x' is none of 'abort', 'ignore', 'warn')
+  [255]
+this merge should fail
+  $ hg merge 1 --config merge.checkunknown=abort
+  b: untracked file differs
+  abort: untracked files in working directory differ from files in requested revision
+  [255]
+
+this merge should warn
+  $ hg merge 1 --config merge.checkunknown=warn
+  b: replacing untracked file
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+  $ cat b.orig
+  This is file b2
+  $ hg up --clean 2
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mv b.orig b
+
+this merge should silently ignore
+  $ cat b
+  This is file b2
+  $ hg merge 1 --config merge.checkunknown=ignore
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  (branch merge, don't forget to commit)
+
+  $ cat b.orig
+  This is file b2
+  $ hg up --clean 2
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ mv b.orig b
+
+this merge of b should work
+  $ cat b
+  This is file b2
   $ hg merge -f 1
   merging b
   merging for b