diff --git a/hgext/formatsource.py b/hgext/formatsource.py new file mode 100644 --- /dev/null +++ b/hgext/formatsource.py @@ -0,0 +1,331 @@ +# Copyright 2017 Octobus +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. +"""help dealing with code source reformating + +The extension provides a way to run code-formatting tools in a way that avoids +conflicts related to this formatting when merging/rebasing code across the +reformatting. + +A new `format-source` command is provided, to apply code formatting tool on +some specific files. This information is recorded into the repository and +reused when merging. The client doing the merge needs the extension for this +logic to kick in. + +Code formatting tools have to be registered in the configuration. The tool +"name" will be used to identify a specific command accross all repositories. +It is mapped to a command line that must output the formatted content on its +standard output. + +For each tool a list of files affecting the result of the formatting can be +configured with the "configpaths" suboption, which is read and registered at +"hg format-source" time. Any change in those files should trigger +reformatting. + +Example:: + + [format-source] + json = python -m json.tool + clang = clang-format -style=Mozilla + clang:configpaths = .clang-format, .clang-format-ignore + +We do not support specifying the mapping of tool name to tool command in the +repository itself for security reasons. + +The code formatting information is tracked in a .hg-format-source file at the +root of the repository. + +Warning: There is no special logic handling renames so moving files to a +directory not covered by the patterns used for the initial formatting will +likely fail. +""" + +from __future__ import absolute_import + +import json +import tempfile + +from mercurial.i18n import _ + +from mercurial import ( + cmdutil, + commands, + encoding, + error, + extensions, + filemerge, + match, + merge, + registrar, + scmutil, + util, +) + +cmdtable = {} +command = registrar.command(cmdtable) + +configtable = {} +configitem = registrar.configitem(configtable) +configitem('format-source', '.*', default=None, generic=True) + +file_storage_path = '.hg-format-source' + +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for +# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should +# be specifying the version(s) of Mercurial they are tested with, or +# leave the attribute unspecified. +testedwith = 'ships-with-hg-core' + +@command('format-source', + [] + commands.walkopts + commands.commitopts + commands.commitopts2, + _('TOOL FILES+')) +def cmd_format_source(ui, repo, tool, *pats, **opts): + """format source file using a registered tools + + This command run TOOL on FILES and record this information in a commit to + help with future merge. + + The actual command run for TOOL needs to be registered in the config. See + :hg:`help -e formatsource` for details. + """ + if repo.getcwd(): + msg = _("format-source must be run from repository root") + hint = _("cd %s") % repo.root + raise error.Abort(msg, hint=hint) + + if not pats: + raise error.Abort(_('no files specified')) + + # XXX We support glob pattern only for now, the recursive behavior of + # various others is a bit wonky. + for pattern in pats: + if not pattern.startswith('glob:'): + msg = _("format-source only supports explicit 'glob' patterns " + "for now ('%s')") + msg %= pattern + hint = _('maybe try with "glob:%s"') % pattern + raise error.Abort(msg, hint=hint) + + # lock the repo to make sure no content is changed + with repo.wlock(): + # formating tool + if ' ' in tool: + raise error.Abort(_("tool name cannot contains space:" + " '%s'") % tool) + shell_tool = repo.ui.config('format-source', tool) + tool_config_files = repo.ui.configlist('format-source', + '%s:configpaths' % tool) + if not shell_tool: + msg = _("unknow format tool: %s (no 'format-source.%s' config)") + raise error.Abort(msg % (tool, tool)) + cmdutil.bailifchanged(repo) + cmdutil.checkunfinished(repo, commit=True) + wctx = repo[None] + # files to be formatted + matcher = scmutil.match(wctx, pats, opts) + # perform actual formatting + for filepath in wctx.matches(matcher): + flags = wctx.flags(filepath) + newcontent = run_tools(ui, repo.root, tool, shell_tool, + filepath, filepath) + # XXX we could do the whole commit in memory + with repo.wvfs(filepath, 'wb') as formatted_file: + formatted_file.write(newcontent) + wctx.filectx(filepath).setflags('l' in flags, 'x' in flags) + + # update the storage to mark formated file as formatted + with repo.wvfs(file_storage_path, mode='ab') as storage: + for pattern in pats: + # XXX if pattern was relative, we need to reroot it from the + # repository root. For now we constrainted the command to run + # at the root of the repository. + data = {'tool': encoding.unifromlocal(tool), + 'pattern': encoding.unifromlocal(pattern)} + if tool_config_files: + data['configpaths'] = [encoding.unifromlocal(path) + for path in tool_config_files] + entry = json.dumps(data, sort_keys=True) + assert '\n' not in entry + storage.write('%s\n' % entry) + + if file_storage_path not in wctx: + storage_matcher = scmutil.match(wctx, ['path:' + file_storage_path]) + cmdutil.add(ui, repo, storage_matcher, '', True) + + # commit the whole + with repo.lock(): + commit_patterns = ['path:' + file_storage_path] + commit_patterns.extend(pats) + return commands._docommit(ui, repo, *commit_patterns, **opts) + +def run_tools(ui, root, tool, cmd, filepath, filename): + """Run the a formatter tool on a specific file""" + env = encoding.environ.copy() + env['HG_FILENAME'] = filename + # XXX escape special character in filepath + format_cmd = "%s %s" % (cmd, filepath) + ui.debug('running %s\n' % format_cmd) + ui.pushbuffer(subproc=True) + try: + ui.system(format_cmd, + environ=env, + cwd=root, + onerr=error.Abort, + errprefix=tool) + finally: + newcontent = ui.popbuffer() + return newcontent + +def touched(repo, old_ctx, new_ctx, paths): + matcher = rootedmatch(repo, new_ctx, paths) + if any(path in new_ctx for path in paths): + status = old_ctx.status(other=new_ctx, match=matcher) + return bool(status.modified or status.added) + return False + +def formatted(repo, old_ctx, new_ctx): + """retrieve the list of formatted patterns between and + + return a {'tool': [patterns]} mapping + """ + new_formatting = {} + if touched(repo, old_ctx, new_ctx, [file_storage_path]): + # quick and dirty line diffing + # (the file is append only by contract) + + new_lines = set(new_ctx[file_storage_path].data().splitlines()) + old_lines = set() + if file_storage_path in old_ctx: + old_lines = set(old_ctx[file_storage_path].data().splitlines()) + new_lines -= old_lines + for line in new_lines: + entry = json.loads(line) + def getkey(key): + return encoding.unitolocal(entry[key]) + new_formatting.setdefault(getkey('tool'), + set()).add(getkey('pattern')) + if file_storage_path in old_ctx: + for line in old_ctx[file_storage_path].data().splitlines(): + entry = json.loads(line) + if not entry.get('configpaths'): + continue + configpaths = [encoding.unitolocal(path) \ + for path in entry['configpaths']] + def getkey(key): + return encoding.unitolocal(entry[key]) + if touched(repo, old_ctx, new_ctx, configpaths): + new_formatting.setdefault(getkey('tool'), + set()).add(getkey('pattern')) + return new_formatting + +def allformatted(repo, local, other, ancestor): + """return a mapping of formatting needed for all involved changeset + """ + + cachekey = (local.node, other.node(), ancestor.node()) + cached = getattr(repo, '_formatting_cache', {}).get(cachekey) + + if cached is not None: + return cached + + local_formating = formatted(repo, ancestor, local) + other_formating = formatted(repo, ancestor, other) + full_formating = local_formating.copy() + for key, value in other_formating.iteritems(): + if key in local_formating: + value = value | local_formating[key] + full_formating[key] = value + + all = [ + (local, local_formating), + (other, other_formating), + (ancestor, full_formating) + ] + for ctx, formatting in all: + for tool, patterns in formatting.iteritems(): + formatting[tool] = rootedmatch(repo, ctx, patterns) + + final = tuple(formatting for __, formatting in all) + getattr(repo, '_formatting_cache', {})[cachekey] = cached + + return final + +def rootedmatch(repo, ctx, patterns): + """match patterns agains the root of a repository""" + # rework of basectx.match to ignore current working directory + + # Only a case insensitive filesystem needs magic to translate user input + # to actual case in the filesystem. + icasefs = not util.fscasesensitive(repo.root) + if util.safehasattr(match, 'icasefsmatcher'): #< hg 4.3 + if icasefs: + return match.icasefsmatcher(repo.root, repo.root, patterns, + default='glob', auditor=repo.auditor, + ctx=ctx) + else: + return match.match(repo.root, repo.root, patterns, default='glob', + auditor=repo.auditor, ctx=ctx) + else: + return match.match(repo.root, repo.root, patterns, default='glob', + auditor=repo.auditor, ctx=ctx, icasefs=icasefs) + +def apply_formating(repo, formatting, fctx): + """apply formatting to a file context (if applicable)""" + data = None + for tool, matcher in sorted(formatting.items()): + # matches? + if matcher(fctx.path()): + if data is None: + data = fctx.data() + shell_tool = repo.ui.config('format-source', tool) + if not shell_tool: + msg = _("format-source, no command defined for '%s'," + " skipping formating: '%s'\n") + msg %= (tool, fctx.path()) + repo.ui.warn(msg) + continue + with tempfile.NamedTemporaryFile(mode='wb') as f: + f.write(data) + f.flush() + data = run_tools(repo.ui, repo.root, tool, shell_tool, + f.name, fctx.path()) + if data is not None: + fctx.data = lambda: data + + +def wrap_filemerge(origfunc, premerge, repo, wctx, mynode, orig, fcd, fco, + fca, *args, **kwargs): + """wrap the file merge logic to apply formatting on files that needs them""" + _update_filemerge_content(repo, fcd, fco, fca) + return origfunc(premerge, repo, wctx, mynode, orig, fcd, fco, fca, + *args, **kwargs) + +def _update_filemerge_content(repo, fcd, fco, fca): + if fcd.isabsent() or fco.isabsent() or fca.isabsent(): + return + local = fcd._changectx + other = fco._changectx + ances = fca._changectx + all = allformatted(repo, local, other, ances) + local_formating, other_formating, full_formating = all + apply_formating(repo, local_formating, fco) + apply_formating(repo, other_formating, fcd) + apply_formating(repo, full_formating, fca) + + if 'data' in vars(fcd): # XXX hacky way to check if data overwritten + file_path = repo.wvfs.join(fcd.path()) + with open(file_path, 'wb') as local_file: + local_file.write(fcd.data()) + +def wrap_update(orig, repo, *args, **kwargs): + """install the formatting cache""" + repo._formatting_cache = {} + try: + return orig(repo, *args, **kwargs) + finally: + del repo._formatting_cache + +def uisetup(self): + extensions.wrapfunction(filemerge, '_filemerge', wrap_filemerge) + extensions.wrapfunction(merge, 'update', wrap_update) diff --git a/tests/test-format-config.t b/tests/test-format-config.t new file mode 100644 --- /dev/null +++ b/tests/test-format-config.t @@ -0,0 +1,288 @@ + +Basic init + + $ code_root=`dirname $TESTDIR` + + $ cat << EOF >> $HGRCPATH + > [extensions] + > formatsource= + > rebase = + > strip = + > [format-source] + > json = $PYTHON $TESTDIR/testlib/json-pretty.py + > json:configpaths = .json-indent + > [default] + > format-source=--date '0 0' + > EOF + $ HGMERGE=:merge3 + + $ hg init test_repo + $ cd test_repo + +Commit various json file + + $ mkdir dir-1 + $ cat << EOF > dir-1/file-1.json + > {"key1": [42,53,78], "key2": [9,3,8,1], "key3": ["London", "Paris", "Tokyo"]} + > EOF + $ cat << EOF > dir-1/file-2.json + > {"key1": 1, "key2": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "key3": [54]} + > EOF + $ hg add . + adding dir-1/file-1.json + adding dir-1/file-2.json + $ hg commit --message 'initial commit' + +format them (in multiple steps) + + $ hg format-source --date '0 0' json glob:*/file-1.json -m 'format without config' + $ hg export + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID fb63bdd6edbf60d86f7aeaf0d806ecf6555c02eb + # Parent 103bbf4a41e9e9010c27ab49c158f99b176d4f3e + format without config + + diff -r 103bbf4a41e9 -r fb63bdd6edbf .hg-format-source + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +{"configpaths": [".json-indent"], "pattern": "glob:*/file-1.json", "tool": "json"} + diff -r 103bbf4a41e9 -r fb63bdd6edbf dir-1/file-1.json + --- a/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,18 @@ + -{"key1": [42,53,78], "key2": [9,3,8,1], "key3": ["London", "Paris", "Tokyo"]} + +{ + + "key1": [ + + 42, + + 53, + + 78 + + ], + + "key2": [ + + 9, + + 3, + + 8, + + 1 + + ], + + "key3": [ + + "London", + + "Paris", + + "Tokyo" + + ] + +} + $ echo 2 > .json-indent + $ hg add .json-indent + $ $PYTHON $TESTDIR/testlib/json-pretty.py < dir-1/file-1.json > tmp + $ mv tmp dir-1/file-1.json + $ hg diff + diff -r fb63bdd6edbf .json-indent + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/.json-indent Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +2 + diff -r fb63bdd6edbf dir-1/file-1.json + --- a/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + @@ -1,18 +1,18 @@ + { + - "key1": [ + - 42, + - 53, + - 78 + - ], + - "key2": [ + - 9, + - 3, + - 8, + - 1 + - ], + - "key3": [ + - "London", + - "Paris", + - "Tokyo" + - ] + + "key1": [ + + 42, + + 53, + + 78 + + ], + + "key2": [ + + 9, + + 3, + + 8, + + 1 + + ], + + "key3": [ + + "London", + + "Paris", + + "Tokyo" + + ] + } + $ hg commit -m 'reformat with indent=2' + $ echo 1 > .json-indent + $ $PYTHON $TESTDIR/testlib/json-pretty.py < dir-1/file-1.json > tmp + $ mv tmp dir-1/file-1.json + $ hg diff + diff -r bacb7be97453 .json-indent + --- a/.json-indent Thu Jan 01 00:00:00 1970 +0000 + +++ b/.json-indent Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,1 @@ + -2 + +1 + diff -r bacb7be97453 dir-1/file-1.json + --- a/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + @@ -1,18 +1,18 @@ + { + - "key1": [ + - 42, + - 53, + - 78 + - ], + - "key2": [ + - 9, + - 3, + - 8, + - 1 + - ], + - "key3": [ + - "London", + - "Paris", + - "Tokyo" + - ] + + "key1": [ + + 42, + + 53, + + 78 + + ], + + "key2": [ + + 9, + + 3, + + 8, + + 1 + + ], + + "key3": [ + + "London", + + "Paris", + + "Tokyo" + + ] + } + $ hg commit -m 'reformat with indent=1' + +Add changes on another branch + + $ hg up 0 + 1 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ cat << EOF > dir-1/file-1.json + > {"key1": [42,53,78,66], "key2": [9,3,8,1], "key3": ["London", "Paris", "Tokyo"]} + > EOF + $ cat << EOF > dir-1/file-2.json + > {"key1": 1, "key2": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "key3": [54, 55]} + > EOF + $ hg commit -m 'some editions' + created new head + +Merge with "format without config" + + $ hg log -G + @ changeset: 4:360af76de133 + | tag: tip + | parent: 0:103bbf4a41e9 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: some editions + | + | o changeset: 3:fa670ec0f89c + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: reformat with indent=1 + | | + | o changeset: 2:bacb7be97453 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: reformat with indent=2 + | | + | o changeset: 1:fb63bdd6edbf + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: format without config + | + o changeset: 0:103bbf4a41e9 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: initial commit + + $ f --md5 dir-1/file-2.json + dir-1/file-2.json: md5=19be5044f37a12515b1bf92e2becf702 + $ hg merge 1 + merging dir-1/file-1.json + 1 files updated, 1 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ f --md5 dir-1/file-2.json + dir-1/file-2.json: md5=19be5044f37a12515b1bf92e2becf702 + $ hg diff -r 'p2()' dir-1/file-1.json + diff -r fb63bdd6edbf dir-1/file-1.json + --- a/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + @@ -2,7 +2,8 @@ + "key1": [ + 42, + 53, + - 78 + + 78, + + 66 + ], + "key2": [ + 9, + $ hg commit -m 'merge #1' + +Merge with "format with indent=2" + + $ hg merge 2 + merging dir-1/file-1.json + 1 files updated, 1 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ f --md5 dir-1/file-2.json + dir-1/file-2.json: md5=19be5044f37a12515b1bf92e2becf702 + $ hg diff -r 'p2()' dir-1/file-1.json + diff -r bacb7be97453 dir-1/file-1.json + --- a/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + @@ -2,7 +2,8 @@ + "key1": [ + 42, + 53, + - 78 + + 78, + + 66 + ], + "key2": [ + 9, + $ hg commit -m 'merge #2' + +Merge with indent=1 + + $ hg merge 3 + merging dir-1/file-1.json + 1 files updated, 1 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ f --md5 dir-1/file-2.json + dir-1/file-2.json: md5=19be5044f37a12515b1bf92e2becf702 + $ hg diff -r 'p2()' dir-1/file-1.json + diff -r fa670ec0f89c dir-1/file-1.json + --- a/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + @@ -2,7 +2,8 @@ + "key1": [ + 42, + 53, + - 78 + + 78, + + 66 + ], + "key2": [ + 9, + $ hg commit -m 'merge #3' diff --git a/tests/test-format-source.t b/tests/test-format-source.t new file mode 100644 --- /dev/null +++ b/tests/test-format-source.t @@ -0,0 +1,1376 @@ + +Basic init + + $ code_root=`dirname $TESTDIR` + + $ cat << EOF >> $HGRCPATH + > [extensions] + > formatsource= + > rebase = + > strip = + > [format-source] + > json = $PYTHON -m json.tool + > [default] + > format-source=--date '0 0' + > [ui] + > merge = internal:merge3 + > EOF + + $ hg init test_repo + $ cd test_repo + +Commit various json file + + $ cat << EOF > root-file.json + > {"key1": 1, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste"]} + > EOF + $ mkdir dir-1 + $ cat << EOF > dir-1/file-1.json + > {"key1": [42,53,78], "key2": [9,3,8,1], "key3": ["London", "Paris", "Tokyo"]} + > EOF + $ cat << EOF > dir-1/file-2.json + > {"key1": 1, "key2": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "key3": [54]} + > EOF + $ mkdir dir-2 + $ cat << EOF > dir-2/file-1.json + > {"key1": [44,50,82], "key2": [4, 8, -4, 5], "key3": ["Asia", "Europe"]} + > EOF + $ cat << EOF > dir-2/file-2.json + > {"key1": 6, + > "key2": [3, 5, -2, 3, 4, 11, 10, -4, 8, 9], + > "key3": [898, 32543, 2342]} + > EOF + $ cat << EOF > dir-2/file-3.json + > {"key1": "hello", "key2": [3, 1, 8, -1, 19, 2], "key3": "babar"} + > EOF + $ hg add . + adding dir-1/file-1.json + adding dir-1/file-2.json + adding dir-2/file-1.json + adding dir-2/file-2.json + adding dir-2/file-3.json + adding root-file.json + $ hg commit --message 'initial commit' + +format them (in multiple steps) + + $ hg format-source --date '0 0' json glob:root-file.json -m 'format the root-file' + $ hg export + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 4d5d2129291fb884920bc2dce4a5da973634a6e9 + # Parent f168d21ce7659a51518009b8aab78a222af2ddf8 + format the root-file + + diff -r f168d21ce765 -r 4d5d2129291f .hg-format-source + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +{"pattern": "glob:root-file.json", "tool": "json"} + diff -r f168d21ce765 -r 4d5d2129291f root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,14 @@ + -{"key1": 1, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste"]} + +{ + + "key1": 1, + + "key2": [ + + 5, + + 6, + + 7, + + 8 + + ], + + "key3": [ + + "arthur", + + "babar", + + "celeste" + + ] + +} + $ hg format-source --date '0 0' json 'glob:dir-1/**' -m 'format dir1 as a whole' + $ hg export + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 279459a606de25c75ca2e2984a607ddf4a88ac29 + # Parent 4d5d2129291fb884920bc2dce4a5da973634a6e9 + format dir1 as a whole + + diff -r 4d5d2129291f -r 279459a606de .hg-format-source + --- a/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,2 @@ + {"pattern": "glob:root-file.json", "tool": "json"} + +{"pattern": "glob:dir-1/**", "tool": "json"} + diff -r 4d5d2129291f -r 279459a606de dir-1/file-1.json + --- a/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,18 @@ + -{"key1": [42,53,78], "key2": [9,3,8,1], "key3": ["London", "Paris", "Tokyo"]} + +{ + + "key1": [ + + 42, + + 53, + + 78 + + ], + + "key2": [ + + 9, + + 3, + + 8, + + 1 + + ], + + "key3": [ + + "London", + + "Paris", + + "Tokyo" + + ] + +} + diff -r 4d5d2129291f -r 279459a606de dir-1/file-2.json + --- a/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,18 @@ + -{"key1": 1, "key2": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "key3": [54]} + +{ + + "key1": 1, + + "key2": [ + + 0, + + 1, + + 2, + + 3, + + 4, + + 5, + + 6, + + 7, + + 8, + + 9 + + ], + + "key3": [ + + 54 + + ] + +} + $ hg format-source --date '0 0' json glob:dir-2/file-1.json glob:dir-2/file-3.json -m 'format some dir2 with explicite pattern' + $ hg export + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID d29e366c2a4b03cc2c09c0581fed00147b5bbebc + # Parent 279459a606de25c75ca2e2984a607ddf4a88ac29 + format some dir2 with explicite pattern + + diff -r 279459a606de -r d29e366c2a4b .hg-format-source + --- a/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + @@ -1,2 +1,4 @@ + {"pattern": "glob:root-file.json", "tool": "json"} + {"pattern": "glob:dir-1/**", "tool": "json"} + +{"pattern": "glob:dir-2/file-1.json", "tool": "json"} + +{"pattern": "glob:dir-2/file-3.json", "tool": "json"} + diff -r 279459a606de -r d29e366c2a4b dir-2/file-1.json + --- a/dir-2/file-1.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-2/file-1.json Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,17 @@ + -{"key1": [44,50,82], "key2": [4, 8, -4, 5], "key3": ["Asia", "Europe"]} + +{ + + "key1": [ + + 44, + + 50, + + 82 + + ], + + "key2": [ + + 4, + + 8, + + -4, + + 5 + + ], + + "key3": [ + + "Asia", + + "Europe" + + ] + +} + diff -r 279459a606de -r d29e366c2a4b dir-2/file-3.json + --- a/dir-2/file-3.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-2/file-3.json Thu Jan 01 00:00:00 1970 +0000 + @@ -1,1 +1,12 @@ + -{"key1": "hello", "key2": [3, 1, 8, -1, 19, 2], "key3": "babar"} + +{ + + "key1": "hello", + + "key2": [ + + 3, + + 1, + + 8, + + -1, + + 19, + + 2 + + ], + + "key3": "babar" + +} + $ hg log -G + @ changeset: 3:d29e366c2a4b + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: format some dir2 with explicite pattern + | + o changeset: 2:279459a606de + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: format dir1 as a whole + | + o changeset: 1:4d5d2129291f + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: format the root-file + | + o changeset: 0:f168d21ce765 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: initial commit + + +Test merging +============ + +Simple case, the root file + + $ hg up 0 + 5 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ cat << EOF > root-file.json + > {"key1": 1, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste", "pomme"]} + > EOF + $ hg branch B_A + marked working directory as branch B_A + (branches are permanent and global, did you want a bookmark?) + $ hg ci -m 'update root-file' + $ hg merge 1 + merging root-file.json + 1 files updated, 1 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg diff -r 'p1()' + diff -r 86b2ca2ae552 .hg-format-source + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hg-format-source * (glob) + @@ -0,0 +1,1 @@ + +{"pattern": "glob:root-file.json", "tool": "json"} + diff -r 86b2ca2ae552 root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -1,1 +1,15 @@ + -{"key1": 1, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste", "pomme"]} + +{ + + "key1": 1, + + "key2": [ + + 5, + + 6, + + 7, + + 8 + + ], + + "key3": [ + + "arthur", + + "babar", + + "celeste", + + "pomme" + + ] + +} + $ hg diff -r 'p2()' + diff -r 4d5d2129291f root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -9,6 +9,7 @@ + "key3": [ + "arthur", + "babar", + - "celeste" + + "celeste", + + "pomme" + ] + } + +Same from the other direction + + $ hg up -C 1 + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg merge B_A + merging root-file.json + 0 files updated, 1 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg diff -r 'p1()' + diff -r 4d5d2129291f root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -9,6 +9,7 @@ + "key3": [ + "arthur", + "babar", + - "celeste" + + "celeste", + + "pomme" + ] + } + $ hg diff -r 'p2()' + diff -r 86b2ca2ae552 .hg-format-source + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hg-format-source * (glob) + @@ -0,0 +1,1 @@ + +{"pattern": "glob:root-file.json", "tool": "json"} + diff -r 86b2ca2ae552 root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -1,1 +1,15 @@ + -{"key1": 1, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste", "pomme"]} + +{ + + "key1": 1, + + "key2": [ + + 5, + + 6, + + 7, + + 8 + + ], + + "key3": [ + + "arthur", + + "babar", + + "celeste", + + "pomme" + + ] + +} + +Merging change on both side: + + $ hg up -C 1 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg branch B_B + marked working directory as branch B_B + + $ echo '{"key1": 4, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste"]}' | $PYTHON -m json.tool > root-file.json + $ hg ci -m 'update key 1' + $ hg merge B_A + merging root-file.json + 0 files updated, 1 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg diff -r 'p1()' + diff -r 217d6c23789b root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -9,6 +9,7 @@ + "key3": [ + "arthur", + "babar", + - "celeste" + + "celeste", + + "pomme" + ] + } + $ hg diff -r 'p2()' + diff -r 86b2ca2ae552 .hg-format-source + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hg-format-source * (glob) + @@ -0,0 +1,1 @@ + +{"pattern": "glob:root-file.json", "tool": "json"} + diff -r 86b2ca2ae552 root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -1,1 +1,15 @@ + -{"key1": 1, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste", "pomme"]} + +{ + + "key1": 4, + + "key2": [ + + 5, + + 6, + + 7, + + 8 + + ], + + "key3": [ + + "arthur", + + "babar", + + "celeste", + + "pomme" + + ] + +} + +Merging with conflict + + $ hg up -C B_B + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ echo '{"key1": 4, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste", "Flore"]}' | $PYTHON -m json.tool > root-file.json + $ hg ci -m 'conflicting update' + $ hg merge B_A + merging root-file.json + warning: conflicts while merging root-file.json! (edit, then use 'hg resolve --mark') + 0 files updated, 0 files merged, 0 files removed, 1 files unresolved + use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon + [1] + $ hg resolve -l + U root-file.json + $ hg diff -r 'p1()' + diff -r 654f4c32c118 root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -9,7 +9,14 @@ + "key3": [ + "arthur", + "babar", + +<<<<<<< working copy: 654f4c32c118 B_B - test: conflicting update + "celeste", + "Flore" + +||||||| base + + "celeste" + +======= + + "celeste", + + "pomme" + +>>>>>>> merge rev: 86b2ca2ae552 B_A - test: update root-file + ] + } + $ hg diff -r 'p2()' + diff -r 86b2ca2ae552 .hg-format-source + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hg-format-source * (glob) + @@ -0,0 +1,1 @@ + +{"pattern": "glob:root-file.json", "tool": "json"} + diff -r 86b2ca2ae552 root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -1,1 +1,22 @@ + -{"key1": 1, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste", "pomme"]} + +{ + + "key1": 4, + + "key2": [ + + 5, + + 6, + + 7, + + 8 + + ], + + "key3": [ + + "arthur", + + "babar", + +<<<<<<< working copy: 654f4c32c118 B_B - test: conflicting update + + "celeste", + + "Flore" + +||||||| base + + "celeste" + +======= + + "celeste", + + "pomme" + +>>>>>>> merge rev: 86b2ca2ae552 B_A - test: update root-file + + ] + +} + +Test merge with no reformating needed +------------------------------------- + + $ hg up -C B_B + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg merge default + 5 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg diff -r 'p2()' + diff -r d29e366c2a4b root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -1,5 +1,5 @@ + { + - "key1": 1, + + "key1": 4, + "key2": [ + 5, + 6, + @@ -9,6 +9,7 @@ + "key3": [ + "arthur", + "babar", + - "celeste" + + "celeste", + + "Flore" + ] + } + +Test rebase +----------- + + $ hg up -C B_A + 5 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg rebase -d default + rebasing 4:86b2ca2ae552 "update root-file" + merging root-file.json + saved backup bundle to $TESTTMP/test_repo/.hg/strip-backup/86b2ca2ae552-2a92ee50-*.hg (glob) + $ hg export + # HG changeset patch + # User test + # Date 0 0 + # Thu Jan 01 00:00:00 1970 +0000 + # Node ID 180a5b3a41142f9e2f40e773ed299bc996442266 + # Parent d29e366c2a4b03cc2c09c0581fed00147b5bbebc + update root-file + + diff -r d29e366c2a4b -r 180a5b3a4114 root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json Thu Jan 01 00:00:00 1970 +0000 + @@ -9,6 +9,7 @@ + "key3": [ + "arthur", + "babar", + - "celeste" + + "celeste", + + "pomme" + ] + } + $ hg log -G + @ changeset: 6:180a5b3a4114 + | tag: tip + | parent: 3:d29e366c2a4b + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: update root-file + | + | o changeset: 5:654f4c32c118 + | | branch: B_B + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: conflicting update + | | + | o changeset: 4:217d6c23789b + | | branch: B_B + | | parent: 1:4d5d2129291f + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: update key 1 + | | + o | changeset: 3:d29e366c2a4b + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: format some dir2 with explicite pattern + | | + o | changeset: 2:279459a606de + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: format dir1 as a whole + | + o changeset: 1:4d5d2129291f + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: format the root-file + | + o changeset: 0:f168d21ce765 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: initial commit + + +Test update +----------- + + $ hg up -C 0 + 5 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ cat << EOF > root-file.json + > {"key1": 1, "key2": [4,6,7,8], "key3": ["arthur", "babar", "celeste"]} + > EOF + $ hg diff + diff -r f168d21ce765 root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -1,1 +1,1 @@ + -{"key1": 1, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste"]} + +{"key1": 1, "key2": [4,6,7,8], "key3": ["arthur", "babar", "celeste"]} + $ hg up --merge + merging root-file.json + 5 files updated, 1 files merged, 0 files removed, 0 files unresolved + $ hg diff + diff -r 180a5b3a4114 root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -1,7 +1,7 @@ + { + "key1": 1, + "key2": [ + - 5, + + 4, + 6, + 7, + 8 + + +Test multiple change to multiple file accross multiple revisions +---------------------------------------------------------------- + +modify one side + + $ hg up -C default + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ echo '{"key1": [42,53,76], "key2": [9,3,8,1], "key3": ["London", "Paris", "Tokyo"]}' | $PYTHON -m json.tool > dir-1/file-1.json + $ hg diff + diff -r 180a5b3a4114 dir-1/file-1.json + --- a/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-1.json * (glob) + @@ -2,7 +2,7 @@ + "key1": [ + 42, + 53, + - 78 + + 76 + ], + "key2": [ + 9, + $ hg ci -m 'update d1f1' + $ echo '{"key1": 4, "key2": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "key3": [54]}' | $PYTHON -m json.tool > dir-1/file-2.json + $ echo '{"key1": [44,50,86], "key2": [4, 8, -4, 5], "key3": ["Asia", "Europe", "Oceania"]}' | $PYTHON -m json.tool > dir-2/file-1.json + $ hg diff + diff -r 0daf45f1c8ad dir-1/file-2.json + --- a/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-2.json * (glob) + @@ -1,5 +1,5 @@ + { + - "key1": 1, + + "key1": 4, + "key2": [ + 0, + 1, + diff -r 0daf45f1c8ad dir-2/file-1.json + --- a/dir-2/file-1.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-2/file-1.json * (glob) + @@ -2,7 +2,7 @@ + "key1": [ + 44, + 50, + - 82 + + 86 + ], + "key2": [ + 4, + @@ -12,6 +12,7 @@ + ], + "key3": [ + "Asia", + - "Europe" + + "Europe", + + "Oceania" + ] + } + $ hg ci -m 'update d1f2 and d2f1' + +modify the other side + + $ hg up -C B_B + 6 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ echo '{"key1": 1337, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste"]}' | $PYTHON -m json.tool > root-file.json + $ echo '{"key1": 4, "key2": [0, 1, 3, 3, 7, 5, 9, 7, 8, 9], "key3": [54]}' > dir-1/file-2.json + $ hg diff + diff -r 654f4c32c118 dir-1/file-2.json + --- a/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-2.json * (glob) + @@ -1,1 +1,1 @@ + -{"key1": 1, "key2": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "key3": [54]} + +{"key1": 4, "key2": [0, 1, 3, 3, 7, 5, 9, 7, 8, 9], "key3": [54]} + diff -r 654f4c32c118 root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -1,5 +1,5 @@ + { + - "key1": 4, + + "key1": 1337, + "key2": [ + 5, + 6, + @@ -9,7 +9,6 @@ + "key3": [ + "arthur", + "babar", + - "celeste", + - "Flore" + + "celeste" + ] + } + $ hg ci -m 'update root-file and d1f2' + $ cat << EOF > dir-2/file-2.json + > {"key1": 6, + > "key2": [3, 5, -2, 3, 4, 11, 10, -4, 8, 9], + > "key3": [898, 32543, 2336]} + > EOF + $ echo '{"key1": "hello", "key2": [6, 1, 8, -1, 19, 2], "key3": "Babar"}' > dir-2/file-3.json + $ hg diff + diff -r b3816cb249a4 dir-2/file-2.json + --- a/dir-2/file-2.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-2/file-2.json * (glob) + @@ -1,3 +1,3 @@ + {"key1": 6, + "key2": [3, 5, -2, 3, 4, 11, 10, -4, 8, 9], + -"key3": [898, 32543, 2342]} + +"key3": [898, 32543, 2336]} + diff -r b3816cb249a4 dir-2/file-3.json + --- a/dir-2/file-3.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-2/file-3.json * (glob) + @@ -1,1 +1,1 @@ + -{"key1": "hello", "key2": [3, 1, 8, -1, 19, 2], "key3": "babar"} + +{"key1": "hello", "key2": [6, 1, 8, -1, 19, 2], "key3": "Babar"} + $ hg ci -m 'update d2f2 and d2f3' + +Check output before leaping + + $ hg log -Gp -r 'sort(all(), "topo")' + @ changeset: 10:db645e78c8a3 + | branch: B_B + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: update d2f2 and d2f3 + | + | diff -r b3816cb249a4 -r db645e78c8a3 dir-2/file-2.json + | --- a/dir-2/file-2.json Thu Jan 01 00:00:00 1970 +0000 + | +++ b/dir-2/file-2.json Thu Jan 01 00:00:00 1970 +0000 + | @@ -1,3 +1,3 @@ + | {"key1": 6, + | "key2": [3, 5, -2, 3, 4, 11, 10, -4, 8, 9], + | -"key3": [898, 32543, 2342]} + | +"key3": [898, 32543, 2336]} + | diff -r b3816cb249a4 -r db645e78c8a3 dir-2/file-3.json + | --- a/dir-2/file-3.json Thu Jan 01 00:00:00 1970 +0000 + | +++ b/dir-2/file-3.json Thu Jan 01 00:00:00 1970 +0000 + | @@ -1,1 +1,1 @@ + | -{"key1": "hello", "key2": [3, 1, 8, -1, 19, 2], "key3": "babar"} + | +{"key1": "hello", "key2": [6, 1, 8, -1, 19, 2], "key3": "Babar"} + | + o changeset: 9:b3816cb249a4 + | branch: B_B + | parent: 5:654f4c32c118 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: update root-file and d1f2 + | + | diff -r 654f4c32c118 -r b3816cb249a4 dir-1/file-2.json + | --- a/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + | +++ b/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + | @@ -1,1 +1,1 @@ + | -{"key1": 1, "key2": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "key3": [54]} + | +{"key1": 4, "key2": [0, 1, 3, 3, 7, 5, 9, 7, 8, 9], "key3": [54]} + | diff -r 654f4c32c118 -r b3816cb249a4 root-file.json + | --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + | +++ b/root-file.json Thu Jan 01 00:00:00 1970 +0000 + | @@ -1,5 +1,5 @@ + | { + | - "key1": 4, + | + "key1": 1337, + | "key2": [ + | 5, + | 6, + | @@ -9,7 +9,6 @@ + | "key3": [ + | "arthur", + | "babar", + | - "celeste", + | - "Flore" + | + "celeste" + | ] + | } + | + o changeset: 5:654f4c32c118 + | branch: B_B + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: conflicting update + | + | diff -r 217d6c23789b -r 654f4c32c118 root-file.json + | --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + | +++ b/root-file.json Thu Jan 01 00:00:00 1970 +0000 + | @@ -9,6 +9,7 @@ + | "key3": [ + | "arthur", + | "babar", + | - "celeste" + | + "celeste", + | + "Flore" + | ] + | } + | + o changeset: 4:217d6c23789b + | branch: B_B + | parent: 1:4d5d2129291f + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: update key 1 + | + | diff -r 4d5d2129291f -r 217d6c23789b root-file.json + | --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + | +++ b/root-file.json Thu Jan 01 00:00:00 1970 +0000 + | @@ -1,5 +1,5 @@ + | { + | - "key1": 1, + | + "key1": 4, + | "key2": [ + | 5, + | 6, + | + | o changeset: 8:73175e0546e6 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: update d1f2 and d2f1 + | | + | | diff -r 0daf45f1c8ad -r 73175e0546e6 dir-1/file-2.json + | | --- a/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + | | +++ b/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + | | @@ -1,5 +1,5 @@ + | | { + | | - "key1": 1, + | | + "key1": 4, + | | "key2": [ + | | 0, + | | 1, + | | diff -r 0daf45f1c8ad -r 73175e0546e6 dir-2/file-1.json + | | --- a/dir-2/file-1.json Thu Jan 01 00:00:00 1970 +0000 + | | +++ b/dir-2/file-1.json Thu Jan 01 00:00:00 1970 +0000 + | | @@ -2,7 +2,7 @@ + | | "key1": [ + | | 44, + | | 50, + | | - 82 + | | + 86 + | | ], + | | "key2": [ + | | 4, + | | @@ -12,6 +12,7 @@ + | | ], + | | "key3": [ + | | "Asia", + | | - "Europe" + | | + "Europe", + | | + "Oceania" + | | ] + | | } + | | + | o changeset: 7:0daf45f1c8ad + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: update d1f1 + | | + | | diff -r 180a5b3a4114 -r 0daf45f1c8ad dir-1/file-1.json + | | --- a/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + | | +++ b/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + | | @@ -2,7 +2,7 @@ + | | "key1": [ + | | 42, + | | 53, + | | - 78 + | | + 76 + | | ], + | | "key2": [ + | | 9, + | | + | o changeset: 6:180a5b3a4114 + | | parent: 3:d29e366c2a4b + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: update root-file + | | + | | diff -r d29e366c2a4b -r 180a5b3a4114 root-file.json + | | --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + | | +++ b/root-file.json Thu Jan 01 00:00:00 1970 +0000 + | | @@ -9,6 +9,7 @@ + | | "key3": [ + | | "arthur", + | | "babar", + | | - "celeste" + | | + "celeste", + | | + "pomme" + | | ] + | | } + | | + | o changeset: 3:d29e366c2a4b + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: format some dir2 with explicite pattern + | | + | | diff -r 279459a606de -r d29e366c2a4b .hg-format-source + | | --- a/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + | | +++ b/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + | | @@ -1,2 +1,4 @@ + | | {"pattern": "glob:root-file.json", "tool": "json"} + | | {"pattern": "glob:dir-1/**", "tool": "json"} + | | +{"pattern": "glob:dir-2/file-1.json", "tool": "json"} + | | +{"pattern": "glob:dir-2/file-3.json", "tool": "json"} + | | diff -r 279459a606de -r d29e366c2a4b dir-2/file-1.json + | | --- a/dir-2/file-1.json Thu Jan 01 00:00:00 1970 +0000 + | | +++ b/dir-2/file-1.json Thu Jan 01 00:00:00 1970 +0000 + | | @@ -1,1 +1,17 @@ + | | -{"key1": [44,50,82], "key2": [4, 8, -4, 5], "key3": ["Asia", "Europe"]} + | | +{ + | | + "key1": [ + | | + 44, + | | + 50, + | | + 82 + | | + ], + | | + "key2": [ + | | + 4, + | | + 8, + | | + -4, + | | + 5 + | | + ], + | | + "key3": [ + | | + "Asia", + | | + "Europe" + | | + ] + | | +} + | | diff -r 279459a606de -r d29e366c2a4b dir-2/file-3.json + | | --- a/dir-2/file-3.json Thu Jan 01 00:00:00 1970 +0000 + | | +++ b/dir-2/file-3.json Thu Jan 01 00:00:00 1970 +0000 + | | @@ -1,1 +1,12 @@ + | | -{"key1": "hello", "key2": [3, 1, 8, -1, 19, 2], "key3": "babar"} + | | +{ + | | + "key1": "hello", + | | + "key2": [ + | | + 3, + | | + 1, + | | + 8, + | | + -1, + | | + 19, + | | + 2 + | | + ], + | | + "key3": "babar" + | | +} + | | + | o changeset: 2:279459a606de + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: format dir1 as a whole + | + | diff -r 4d5d2129291f -r 279459a606de .hg-format-source + | --- a/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + | +++ b/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + | @@ -1,1 +1,2 @@ + | {"pattern": "glob:root-file.json", "tool": "json"} + | +{"pattern": "glob:dir-1/**", "tool": "json"} + | diff -r 4d5d2129291f -r 279459a606de dir-1/file-1.json + | --- a/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + | +++ b/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + | @@ -1,1 +1,18 @@ + | -{"key1": [42,53,78], "key2": [9,3,8,1], "key3": ["London", "Paris", "Tokyo"]} + | +{ + | + "key1": [ + | + 42, + | + 53, + | + 78 + | + ], + | + "key2": [ + | + 9, + | + 3, + | + 8, + | + 1 + | + ], + | + "key3": [ + | + "London", + | + "Paris", + | + "Tokyo" + | + ] + | +} + | diff -r 4d5d2129291f -r 279459a606de dir-1/file-2.json + | --- a/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + | +++ b/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + | @@ -1,1 +1,18 @@ + | -{"key1": 1, "key2": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "key3": [54]} + | +{ + | + "key1": 1, + | + "key2": [ + | + 0, + | + 1, + | + 2, + | + 3, + | + 4, + | + 5, + | + 6, + | + 7, + | + 8, + | + 9 + | + ], + | + "key3": [ + | + 54 + | + ] + | +} + | + o changeset: 1:4d5d2129291f + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: format the root-file + | + | diff -r f168d21ce765 -r 4d5d2129291f .hg-format-source + | --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + | +++ b/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + | @@ -0,0 +1,1 @@ + | +{"pattern": "glob:root-file.json", "tool": "json"} + | diff -r f168d21ce765 -r 4d5d2129291f root-file.json + | --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + | +++ b/root-file.json Thu Jan 01 00:00:00 1970 +0000 + | @@ -1,1 +1,14 @@ + | -{"key1": 1, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste"]} + | +{ + | + "key1": 1, + | + "key2": [ + | + 5, + | + 6, + | + 7, + | + 8 + | + ], + | + "key3": [ + | + "arthur", + | + "babar", + | + "celeste" + | + ] + | +} + | + o changeset: 0:f168d21ce765 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: initial commit + + diff -r 000000000000 -r f168d21ce765 dir-1/file-1.json + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +{"key1": [42,53,78], "key2": [9,3,8,1], "key3": ["London", "Paris", "Tokyo"]} + diff -r 000000000000 -r f168d21ce765 dir-1/file-2.json + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +{"key1": 1, "key2": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "key3": [54]} + diff -r 000000000000 -r f168d21ce765 dir-2/file-1.json + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-2/file-1.json Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +{"key1": [44,50,82], "key2": [4, 8, -4, 5], "key3": ["Asia", "Europe"]} + diff -r 000000000000 -r f168d21ce765 dir-2/file-2.json + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-2/file-2.json Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,3 @@ + +{"key1": 6, + +"key2": [3, 5, -2, 3, 4, 11, 10, -4, 8, 9], + +"key3": [898, 32543, 2342]} + diff -r 000000000000 -r f168d21ce765 dir-2/file-3.json + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-2/file-3.json Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +{"key1": "hello", "key2": [3, 1, 8, -1, 19, 2], "key3": "babar"} + diff -r 000000000000 -r f168d21ce765 root-file.json + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json Thu Jan 01 00:00:00 1970 +0000 + @@ -0,0 +1,1 @@ + +{"key1": 1, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste"]} + + +Actual merge + + $ hg diff -r 1 -r B_B .hg-format-source + $ hg diff -r 1 -r default .hg-format-source + diff -r 4d5d2129291f -r 73175e0546e6 .hg-format-source + --- a/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hg-format-source Thu Jan 01 00:00:00 1970 +0000* (glob) + @@ -1,1 +1,4 @@ + {"pattern": "glob:root-file.json", "tool": "json"} + +{"pattern": "glob:dir-1/**", "tool": "json"} + +{"pattern": "glob:dir-2/file-1.json", "tool": "json"} + +{"pattern": "glob:dir-2/file-3.json", "tool": "json"} + $ hg status + $ hg up -C B_B + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg merge default + merging dir-1/file-2.json + merging dir-2/file-3.json + merging root-file.json + 3 files updated, 3 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg log -GT compact + @ 10[tip] db645e78c8a3 1970-01-01 00:00 +0000 test + | update d2f2 and d2f3 + | + o 9:5 b3816cb249a4 1970-01-01 00:00 +0000 test + | update root-file and d1f2 + | + | @ 8 73175e0546e6 1970-01-01 00:00 +0000 test + | | update d1f2 and d2f1 + | | + | o 7 0daf45f1c8ad 1970-01-01 00:00 +0000 test + | | update d1f1 + | | + | o 6:3 180a5b3a4114 1970-01-01 00:00 +0000 test + | | update root-file + | | + o | 5 654f4c32c118 1970-01-01 00:00 +0000 test + | | conflicting update + | | + o | 4:1 217d6c23789b 1970-01-01 00:00 +0000 test + | | update key 1 + | | + | o 3 d29e366c2a4b 1970-01-01 00:00 +0000 test + | | format some dir2 with explicite pattern + | | + | o 2 279459a606de 1970-01-01 00:00 +0000 test + |/ format dir1 as a whole + | + o 1 4d5d2129291f 1970-01-01 00:00 +0000 test + | format the root-file + | + o 0 f168d21ce765 1970-01-01 00:00 +0000 test + initial commit + + $ hg diff -r 'p1()' + diff -r db645e78c8a3 .hg-format-source + --- a/.hg-format-source Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hg-format-source * (glob) + @@ -1,1 +1,4 @@ + {"pattern": "glob:root-file.json", "tool": "json"} + +{"pattern": "glob:dir-1/**", "tool": "json"} + +{"pattern": "glob:dir-2/file-1.json", "tool": "json"} + +{"pattern": "glob:dir-2/file-3.json", "tool": "json"} + diff -r db645e78c8a3 dir-1/file-1.json + --- a/dir-1/file-1.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-1.json * (glob) + @@ -1,1 +1,18 @@ + -{"key1": [42,53,78], "key2": [9,3,8,1], "key3": ["London", "Paris", "Tokyo"]} + +{ + + "key1": [ + + 42, + + 53, + + 76 + + ], + + "key2": [ + + 9, + + 3, + + 8, + + 1 + + ], + + "key3": [ + + "London", + + "Paris", + + "Tokyo" + + ] + +} + diff -r db645e78c8a3 dir-1/file-2.json + --- a/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-2.json * (glob) + @@ -1,1 +1,18 @@ + -{"key1": 4, "key2": [0, 1, 3, 3, 7, 5, 9, 7, 8, 9], "key3": [54]} + +{ + + "key1": 4, + + "key2": [ + + 0, + + 1, + + 3, + + 3, + + 7, + + 5, + + 9, + + 7, + + 8, + + 9 + + ], + + "key3": [ + + 54 + + ] + +} + diff -r db645e78c8a3 dir-2/file-1.json + --- a/dir-2/file-1.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-2/file-1.json * (glob) + @@ -1,1 +1,18 @@ + -{"key1": [44,50,82], "key2": [4, 8, -4, 5], "key3": ["Asia", "Europe"]} + +{ + + "key1": [ + + 44, + + 50, + + 86 + + ], + + "key2": [ + + 4, + + 8, + + -4, + + 5 + + ], + + "key3": [ + + "Asia", + + "Europe", + + "Oceania" + + ] + +} + diff -r db645e78c8a3 dir-2/file-3.json + --- a/dir-2/file-3.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-2/file-3.json * (glob) + @@ -1,1 +1,12 @@ + -{"key1": "hello", "key2": [6, 1, 8, -1, 19, 2], "key3": "Babar"} + +{ + + "key1": "hello", + + "key2": [ + + 6, + + 1, + + 8, + + -1, + + 19, + + 2 + + ], + + "key3": "Babar" + +} + diff -r db645e78c8a3 root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -9,6 +9,7 @@ + "key3": [ + "arthur", + "babar", + - "celeste" + + "celeste", + + "pomme" + ] + } + $ hg diff -r 'p2()' + diff -r 73175e0546e6 dir-1/file-2.json + --- a/dir-1/file-2.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-1/file-2.json * (glob) + @@ -3,11 +3,11 @@ + "key2": [ + 0, + 1, + - 2, + + 3, + 3, + - 4, + + 7, + 5, + - 6, + + 9, + 7, + 8, + 9 + diff -r 73175e0546e6 dir-2/file-2.json + --- a/dir-2/file-2.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-2/file-2.json * (glob) + @@ -1,3 +1,3 @@ + {"key1": 6, + "key2": [3, 5, -2, 3, 4, 11, 10, -4, 8, 9], + -"key3": [898, 32543, 2342]} + +"key3": [898, 32543, 2336]} + diff -r 73175e0546e6 dir-2/file-3.json + --- a/dir-2/file-3.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/dir-2/file-3.json * (glob) + @@ -1,12 +1,12 @@ + { + "key1": "hello", + "key2": [ + - 3, + + 6, + 1, + 8, + -1, + 19, + 2 + ], + - "key3": "babar" + + "key3": "Babar" + } + diff -r 73175e0546e6 root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -1,5 +1,5 @@ + { + - "key1": 1, + + "key1": 1337, + "key2": [ + 5, + 6, + +Merge from a sub directory +========================== + + $ hg up -C . + 6 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd dir-1 + $ hg merge default + merging dir-1/file-2.json + merging dir-2/file-3.json + merging root-file.json + 3 files updated, 3 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg commit -qm 'Merge commit' + $ cd .. + +Preserve flags +============== + + $ mkdir flags + $ cat << EOF > flags/executable.json + > {} + > EOF + $ chmod +x flags/executable.json + $ hg add -q flags/* + $ hg ci -qm "Add flags subdir" + $ hg format-source json glob:flags/* -m 'Executable file formatted' + $ ls -l flags/* | cut -b 1-10 + -rwxr-xr-x + +Test error case +=============== + +Current limitation +------------------ + +not at repository root + + $ cd dir-1 + $ hg format-source json glob:file-2.json + abort: format-source must be run from repository root + (cd $TESTTMP/test_repo) + [255] + +Not a glob: + + $ cd .. + $ hg format-source json dir-1:file-2.json + abort: format-source only supports explicit 'glob' patterns for now ('dir-1:file-2.json') + (maybe try with "glob:dir-1:file-2.json") + [255] + +legitimate error +---------------- + +no pattern + + $ hg format-source json + abort: no files specified + [255] + +unknown tool + + $ hg format-source babar-tool glob:file-2.json + abort: unknow format tool: babar-tool (no 'format-source.babar-tool' config) + [255] + +space in tool + + $ hg format-source 'babar tooling' 'glob:**' + abort: tool name cannot contains space: 'babar tooling' + [255] + +Uncommited changes + + $ echo a >> root-file.json + $ hg format-source json 'glob:root-file.json' + abort: uncommitted changes + [255] + +Merge with an undefined tool + + $ hg up -qC 0 + $ cat << EOF > root-file.json + > {"key1": 1, "key2": [5,6,7,8], "key3": ["Arthur", "babar", "celeste"]} + > EOF + $ hg diff + diff -r f168d21ce765 root-file.json + --- a/root-file.json Thu Jan 01 00:00:00 1970 +0000 + +++ b/root-file.json * (glob) + @@ -1,1 +1,1 @@ + -{"key1": 1, "key2": [5,6,7,8], "key3": ["arthur", "babar", "celeste"]} + +{"key1": 1, "key2": [5,6,7,8], "key3": ["Arthur", "babar", "celeste"]} + $ hg up --merge default --config 'format-source.json=' + format-source, no command defined for 'json', skipping formating: 'root-file.json' + format-source, no command defined for 'json', skipping formating: 'root-file.json' + merging root-file.json + format-source, no command defined for 'json', skipping formating: 'root-file.json' + format-source, no command defined for 'json', skipping formating: 'root-file.json' + warning: conflicts while merging root-file.json! (edit, then use 'hg resolve --mark') + 5 files updated, 0 files merged, 0 files removed, 1 files unresolved + use 'hg resolve' to retry unresolved file merges + [1] diff --git a/tests/test-help.t b/tests/test-help.t --- a/tests/test-help.t +++ b/tests/test-help.t @@ -360,6 +360,7 @@ eol automatically manage newlines in repository files extdiff command to allow external programs to compare revisions factotum http authentication with factotum + formatsource help dealing with code source reformating githelp try mapping git commands to Mercurial commands gpg commands to sign and verify changesets hgk browse the repository in a graphical way diff --git a/tests/testlib/json-pretty.py b/tests/testlib/json-pretty.py new file mode 100644 --- /dev/null +++ b/tests/testlib/json-pretty.py @@ -0,0 +1,47 @@ +r"""Command-line tool to validate and pretty-print JSON + +Usage:: + + $ echo '{"json":"obj"}' | python -m json.tool + { + "json": "obj" + } + $ echo '{ 1.2:3.4}' | python -m json.tool + Expecting property name enclosed in double quotes: line 1 column 3 (char 2) + +""" +from __future__ import absolute_import +import json +import os +import sys + +def main(): + if len(sys.argv) == 1: + infile = sys.stdin + outfile = sys.stdout + elif len(sys.argv) == 2: + infile = open(sys.argv[1], 'rb') + outfile = sys.stdout + elif len(sys.argv) == 3: + infile = open(sys.argv[1], 'rb') + outfile = open(sys.argv[2], 'wb') + else: + raise SystemExit(sys.argv[0] + " [infile [outfile]]") + with infile: + try: + obj = json.load(infile) + except ValueError as e: + raise SystemExit(e) + if os.path.exists('.json-indent'): + with open('.json-indent') as fp: + indent = int(fp.read()) + else: + indent = 4 + with outfile: + json.dump(obj, outfile, sort_keys=True, + indent=indent, separators=(',', ': ')) + outfile.write('\n') + + +if __name__ == '__main__': + main()