Show More
@@ -1,116 +1,118 b'' | |||||
1 | # If you want to change PREFIX, do not just edit it below. The changed |
|
1 | # If you want to change PREFIX, do not just edit it below. The changed | |
2 | # value wont get passed on to recursive make calls. You should instead |
|
2 | # value wont get passed on to recursive make calls. You should instead | |
3 | # override the variable on the command like: |
|
3 | # override the variable on the command like: | |
4 | # |
|
4 | # | |
5 | # % make PREFIX=/opt/ install |
|
5 | # % make PREFIX=/opt/ install | |
6 |
|
6 | |||
7 | PREFIX=/usr/local |
|
7 | PREFIX=/usr/local | |
8 | export PREFIX |
|
8 | export PREFIX | |
9 | PYTHON=python |
|
9 | PYTHON=python | |
10 | PURE= |
|
10 | PURE= | |
11 | PYFILES:=$(shell find mercurial hgext doc -name '*.py') |
|
11 | PYFILES:=$(shell find mercurial hgext doc -name '*.py') | |
12 | DOCFILES=mercurial/help/*.txt |
|
12 | DOCFILES=mercurial/help/*.txt | |
13 |
|
13 | |||
14 | help: |
|
14 | help: | |
15 | @echo 'Commonly used make targets:' |
|
15 | @echo 'Commonly used make targets:' | |
16 | @echo ' all - build program and documentation' |
|
16 | @echo ' all - build program and documentation' | |
17 | @echo ' install - install program and man pages to PREFIX ($(PREFIX))' |
|
17 | @echo ' install - install program and man pages to PREFIX ($(PREFIX))' | |
18 | @echo ' install-home - install with setup.py install --home=HOME ($(HOME))' |
|
18 | @echo ' install-home - install with setup.py install --home=HOME ($(HOME))' | |
19 | @echo ' local - build for inplace usage' |
|
19 | @echo ' local - build for inplace usage' | |
20 | @echo ' tests - run all tests in the automatic test suite' |
|
20 | @echo ' tests - run all tests in the automatic test suite' | |
21 | @echo ' test-foo - run only specified tests (e.g. test-merge1.t)' |
|
21 | @echo ' test-foo - run only specified tests (e.g. test-merge1.t)' | |
22 | @echo ' dist - run all tests and create a source tarball in dist/' |
|
22 | @echo ' dist - run all tests and create a source tarball in dist/' | |
23 | @echo ' clean - remove files created by other targets' |
|
23 | @echo ' clean - remove files created by other targets' | |
24 | @echo ' (except installed files or dist source tarball)' |
|
24 | @echo ' (except installed files or dist source tarball)' | |
25 | @echo ' update-pot - update i18n/hg.pot' |
|
25 | @echo ' update-pot - update i18n/hg.pot' | |
26 | @echo |
|
26 | @echo | |
27 | @echo 'Example for a system-wide installation under /usr/local:' |
|
27 | @echo 'Example for a system-wide installation under /usr/local:' | |
28 | @echo ' make all && su -c "make install" && hg version' |
|
28 | @echo ' make all && su -c "make install" && hg version' | |
29 | @echo |
|
29 | @echo | |
30 | @echo 'Example for a local installation (usable in this directory):' |
|
30 | @echo 'Example for a local installation (usable in this directory):' | |
31 | @echo ' make local && ./hg version' |
|
31 | @echo ' make local && ./hg version' | |
32 |
|
32 | |||
33 | all: build doc |
|
33 | all: build doc | |
34 |
|
34 | |||
35 | local: |
|
35 | local: | |
36 | $(PYTHON) setup.py $(PURE) build_py -c -d . build_ext -i build_mo |
|
36 | $(PYTHON) setup.py $(PURE) build_py -c -d . build_ext -i build_mo | |
37 | $(PYTHON) hg version |
|
37 | $(PYTHON) hg version | |
38 |
|
38 | |||
39 | build: |
|
39 | build: | |
40 | $(PYTHON) setup.py $(PURE) build |
|
40 | $(PYTHON) setup.py $(PURE) build | |
41 |
|
41 | |||
42 | doc: |
|
42 | doc: | |
43 | $(MAKE) -C doc |
|
43 | $(MAKE) -C doc | |
44 |
|
44 | |||
45 | clean: |
|
45 | clean: | |
46 | -$(PYTHON) setup.py clean --all # ignore errors from this command |
|
46 | -$(PYTHON) setup.py clean --all # ignore errors from this command | |
47 | find . \( -name '*.py[cdo]' -o -name '*.so' \) -exec rm -f '{}' ';' |
|
47 | find . \( -name '*.py[cdo]' -o -name '*.so' \) -exec rm -f '{}' ';' | |
48 | rm -f $(addprefix mercurial/,$(notdir $(wildcard mercurial/pure/*.py))) |
|
48 | rm -f $(addprefix mercurial/,$(notdir $(wildcard mercurial/pure/*.py))) | |
49 | rm -f MANIFEST MANIFEST.in tests/*.err |
|
49 | rm -f MANIFEST MANIFEST.in tests/*.err | |
50 | rm -rf build mercurial/locale |
|
50 | rm -rf build mercurial/locale | |
51 | $(MAKE) -C doc clean |
|
51 | $(MAKE) -C doc clean | |
52 |
|
52 | |||
53 | install: install-bin install-doc |
|
53 | install: install-bin install-doc | |
54 |
|
54 | |||
55 | install-bin: build |
|
55 | install-bin: build | |
56 | $(PYTHON) setup.py $(PURE) install --root="$(DESTDIR)/" --prefix="$(PREFIX)" --force |
|
56 | $(PYTHON) setup.py $(PURE) install --root="$(DESTDIR)/" --prefix="$(PREFIX)" --force | |
57 |
|
57 | |||
58 | install-doc: doc |
|
58 | install-doc: doc | |
59 | cd doc && $(MAKE) $(MFLAGS) install |
|
59 | cd doc && $(MAKE) $(MFLAGS) install | |
60 |
|
60 | |||
61 | install-home: install-home-bin install-home-doc |
|
61 | install-home: install-home-bin install-home-doc | |
62 |
|
62 | |||
63 | install-home-bin: build |
|
63 | install-home-bin: build | |
64 | $(PYTHON) setup.py $(PURE) install --home="$(HOME)" --force |
|
64 | $(PYTHON) setup.py $(PURE) install --home="$(HOME)" --force | |
65 |
|
65 | |||
66 | install-home-doc: doc |
|
66 | install-home-doc: doc | |
67 | cd doc && $(MAKE) $(MFLAGS) PREFIX="$(HOME)" install |
|
67 | cd doc && $(MAKE) $(MFLAGS) PREFIX="$(HOME)" install | |
68 |
|
68 | |||
69 | MANIFEST-doc: |
|
69 | MANIFEST-doc: | |
70 | $(MAKE) -C doc MANIFEST |
|
70 | $(MAKE) -C doc MANIFEST | |
71 |
|
71 | |||
72 | MANIFEST.in: MANIFEST-doc |
|
72 | MANIFEST.in: MANIFEST-doc | |
73 | hg manifest | sed -e 's/^/include /' > MANIFEST.in |
|
73 | hg manifest | sed -e 's/^/include /' > MANIFEST.in | |
74 | echo include mercurial/__version__.py >> MANIFEST.in |
|
74 | echo include mercurial/__version__.py >> MANIFEST.in | |
75 | sed -e 's/^/include /' < doc/MANIFEST >> MANIFEST.in |
|
75 | sed -e 's/^/include /' < doc/MANIFEST >> MANIFEST.in | |
76 |
|
76 | |||
77 | dist: tests dist-notests |
|
77 | dist: tests dist-notests | |
78 |
|
78 | |||
79 | dist-notests: doc MANIFEST.in |
|
79 | dist-notests: doc MANIFEST.in | |
80 | TAR_OPTIONS="--owner=root --group=root --mode=u+w,go-w,a+rX-s" $(PYTHON) setup.py -q sdist |
|
80 | TAR_OPTIONS="--owner=root --group=root --mode=u+w,go-w,a+rX-s" $(PYTHON) setup.py -q sdist | |
81 |
|
81 | |||
82 | check: tests |
|
82 | check: tests | |
83 |
|
83 | |||
84 | tests: |
|
84 | tests: | |
85 | cd tests && $(PYTHON) run-tests.py $(TESTFLAGS) |
|
85 | cd tests && $(PYTHON) run-tests.py $(TESTFLAGS) | |
86 |
|
86 | |||
87 | test-%: |
|
87 | test-%: | |
88 | cd tests && $(PYTHON) run-tests.py $(TESTFLAGS) $@ |
|
88 | cd tests && $(PYTHON) run-tests.py $(TESTFLAGS) $@ | |
89 |
|
89 | |||
90 | update-pot: i18n/hg.pot |
|
90 | update-pot: i18n/hg.pot | |
91 |
|
91 | |||
92 | i18n/hg.pot: $(PYFILES) $(DOCFILES) |
|
92 | i18n/hg.pot: $(PYFILES) $(DOCFILES) | |
93 | $(PYTHON) i18n/hggettext mercurial/commands.py \ |
|
93 | $(PYTHON) i18n/hggettext mercurial/commands.py \ | |
94 |
hgext/*.py hgext/*/__init__.py |
|
94 | hgext/*.py hgext/*/__init__.py \ | |
|
95 | mercurial/fileset.py mercurial/revset.py \ | |||
95 | mercurial/templatefilters.py mercurial/templatekw.py \ |
|
96 | mercurial/templatefilters.py mercurial/templatekw.py \ | |
|
97 | mercurial/filemerge.py \ | |||
96 | $(DOCFILES) > i18n/hg.pot |
|
98 | $(DOCFILES) > i18n/hg.pot | |
97 | # All strings marked for translation in Mercurial contain |
|
99 | # All strings marked for translation in Mercurial contain | |
98 | # ASCII characters only. But some files contain string |
|
100 | # ASCII characters only. But some files contain string | |
99 | # literals like this '\037\213'. xgettext thinks it has to |
|
101 | # literals like this '\037\213'. xgettext thinks it has to | |
100 | # parse them even though they are not marked for translation. |
|
102 | # parse them even though they are not marked for translation. | |
101 | # Extracting with an explicit encoding of ISO-8859-1 will make |
|
103 | # Extracting with an explicit encoding of ISO-8859-1 will make | |
102 | # xgettext "parse" and ignore them. |
|
104 | # xgettext "parse" and ignore them. | |
103 | echo $(PYFILES) | xargs \ |
|
105 | echo $(PYFILES) | xargs \ | |
104 | xgettext --package-name "Mercurial" \ |
|
106 | xgettext --package-name "Mercurial" \ | |
105 | --msgid-bugs-address "<mercurial-devel@selenic.com>" \ |
|
107 | --msgid-bugs-address "<mercurial-devel@selenic.com>" \ | |
106 | --copyright-holder "Matt Mackall <mpm@selenic.com> and others" \ |
|
108 | --copyright-holder "Matt Mackall <mpm@selenic.com> and others" \ | |
107 | --from-code ISO-8859-1 --join --sort-by-file --add-comments=i18n: \ |
|
109 | --from-code ISO-8859-1 --join --sort-by-file --add-comments=i18n: \ | |
108 | -d hg -p i18n -o hg.pot |
|
110 | -d hg -p i18n -o hg.pot | |
109 | $(PYTHON) i18n/posplit i18n/hg.pot |
|
111 | $(PYTHON) i18n/posplit i18n/hg.pot | |
110 |
|
112 | |||
111 | %.po: i18n/hg.pot |
|
113 | %.po: i18n/hg.pot | |
112 | msgmerge --no-location --update $@ $^ |
|
114 | msgmerge --no-location --update $@ $^ | |
113 |
|
115 | |||
114 | .PHONY: help all local build doc clean install install-bin install-doc \ |
|
116 | .PHONY: help all local build doc clean install install-bin install-doc \ | |
115 | install-home install-home-bin install-home-doc dist dist-notests tests \ |
|
117 | install-home install-home-bin install-home-doc dist dist-notests tests \ | |
116 | update-pot |
|
118 | update-pot |
@@ -1,342 +1,367 b'' | |||||
1 | # filemerge.py - file-level merge handling for Mercurial |
|
1 | # filemerge.py - file-level merge handling for Mercurial | |
2 | # |
|
2 | # | |
3 | # Copyright 2006, 2007, 2008 Matt Mackall <mpm@selenic.com> |
|
3 | # Copyright 2006, 2007, 2008 Matt Mackall <mpm@selenic.com> | |
4 | # |
|
4 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
5 | # This software may be used and distributed according to the terms of the | |
6 | # GNU General Public License version 2 or any later version. |
|
6 | # GNU General Public License version 2 or any later version. | |
7 |
|
7 | |||
8 | from node import short |
|
8 | from node import short | |
9 | from i18n import _ |
|
9 | from i18n import _ | |
10 | import util, simplemerge, match, error |
|
10 | import util, simplemerge, match, error | |
11 | import os, tempfile, re, filecmp |
|
11 | import os, tempfile, re, filecmp | |
12 |
|
12 | |||
13 | def _toolstr(ui, tool, part, default=""): |
|
13 | def _toolstr(ui, tool, part, default=""): | |
14 | return ui.config("merge-tools", tool + "." + part, default) |
|
14 | return ui.config("merge-tools", tool + "." + part, default) | |
15 |
|
15 | |||
16 | def _toolbool(ui, tool, part, default=False): |
|
16 | def _toolbool(ui, tool, part, default=False): | |
17 | return ui.configbool("merge-tools", tool + "." + part, default) |
|
17 | return ui.configbool("merge-tools", tool + "." + part, default) | |
18 |
|
18 | |||
19 | def _toollist(ui, tool, part, default=[]): |
|
19 | def _toollist(ui, tool, part, default=[]): | |
20 | return ui.configlist("merge-tools", tool + "." + part, default) |
|
20 | return ui.configlist("merge-tools", tool + "." + part, default) | |
21 |
|
21 | |||
22 |
|
|
22 | internals = {} | |
23 |
|
23 | |||
24 | def internaltool(name, trymerge, onfailure=None): |
|
24 | def internaltool(name, trymerge, onfailure=None): | |
25 | '''return a decorator for populating internal merge tool table''' |
|
25 | '''return a decorator for populating internal merge tool table''' | |
26 | def decorator(func): |
|
26 | def decorator(func): | |
27 |
|
|
27 | internals[name] = func | |
28 | func.trymerge = trymerge |
|
28 | func.trymerge = trymerge | |
29 | func.onfailure = onfailure |
|
29 | func.onfailure = onfailure | |
30 | return func |
|
30 | return func | |
31 | return decorator |
|
31 | return decorator | |
32 |
|
32 | |||
33 | def _findtool(ui, tool): |
|
33 | def _findtool(ui, tool): | |
34 |
if tool in |
|
34 | if tool in internals: | |
35 | return tool |
|
35 | return tool | |
36 | for kn in ("regkey", "regkeyalt"): |
|
36 | for kn in ("regkey", "regkeyalt"): | |
37 | k = _toolstr(ui, tool, kn) |
|
37 | k = _toolstr(ui, tool, kn) | |
38 | if not k: |
|
38 | if not k: | |
39 | continue |
|
39 | continue | |
40 | p = util.lookupreg(k, _toolstr(ui, tool, "regname")) |
|
40 | p = util.lookupreg(k, _toolstr(ui, tool, "regname")) | |
41 | if p: |
|
41 | if p: | |
42 | p = util.findexe(p + _toolstr(ui, tool, "regappend")) |
|
42 | p = util.findexe(p + _toolstr(ui, tool, "regappend")) | |
43 | if p: |
|
43 | if p: | |
44 | return p |
|
44 | return p | |
45 | exe = _toolstr(ui, tool, "executable", tool) |
|
45 | exe = _toolstr(ui, tool, "executable", tool) | |
46 | return util.findexe(util.expandpath(exe)) |
|
46 | return util.findexe(util.expandpath(exe)) | |
47 |
|
47 | |||
48 | def _picktool(repo, ui, path, binary, symlink): |
|
48 | def _picktool(repo, ui, path, binary, symlink): | |
49 | def check(tool, pat, symlink, binary): |
|
49 | def check(tool, pat, symlink, binary): | |
50 | tmsg = tool |
|
50 | tmsg = tool | |
51 | if pat: |
|
51 | if pat: | |
52 | tmsg += " specified for " + pat |
|
52 | tmsg += " specified for " + pat | |
53 | if not _findtool(ui, tool): |
|
53 | if not _findtool(ui, tool): | |
54 | if pat: # explicitly requested tool deserves a warning |
|
54 | if pat: # explicitly requested tool deserves a warning | |
55 | ui.warn(_("couldn't find merge tool %s\n") % tmsg) |
|
55 | ui.warn(_("couldn't find merge tool %s\n") % tmsg) | |
56 | else: # configured but non-existing tools are more silent |
|
56 | else: # configured but non-existing tools are more silent | |
57 | ui.note(_("couldn't find merge tool %s\n") % tmsg) |
|
57 | ui.note(_("couldn't find merge tool %s\n") % tmsg) | |
58 | elif symlink and not _toolbool(ui, tool, "symlink"): |
|
58 | elif symlink and not _toolbool(ui, tool, "symlink"): | |
59 | ui.warn(_("tool %s can't handle symlinks\n") % tmsg) |
|
59 | ui.warn(_("tool %s can't handle symlinks\n") % tmsg) | |
60 | elif binary and not _toolbool(ui, tool, "binary"): |
|
60 | elif binary and not _toolbool(ui, tool, "binary"): | |
61 | ui.warn(_("tool %s can't handle binary\n") % tmsg) |
|
61 | ui.warn(_("tool %s can't handle binary\n") % tmsg) | |
62 | elif not util.gui() and _toolbool(ui, tool, "gui"): |
|
62 | elif not util.gui() and _toolbool(ui, tool, "gui"): | |
63 | ui.warn(_("tool %s requires a GUI\n") % tmsg) |
|
63 | ui.warn(_("tool %s requires a GUI\n") % tmsg) | |
64 | else: |
|
64 | else: | |
65 | return True |
|
65 | return True | |
66 | return False |
|
66 | return False | |
67 |
|
67 | |||
68 | # forcemerge comes from command line arguments, highest priority |
|
68 | # forcemerge comes from command line arguments, highest priority | |
69 | force = ui.config('ui', 'forcemerge') |
|
69 | force = ui.config('ui', 'forcemerge') | |
70 | if force: |
|
70 | if force: | |
71 | toolpath = _findtool(ui, force) |
|
71 | toolpath = _findtool(ui, force) | |
72 | if toolpath: |
|
72 | if toolpath: | |
73 | return (force, '"' + toolpath + '"') |
|
73 | return (force, '"' + toolpath + '"') | |
74 | else: |
|
74 | else: | |
75 | # mimic HGMERGE if given tool not found |
|
75 | # mimic HGMERGE if given tool not found | |
76 | return (force, force) |
|
76 | return (force, force) | |
77 |
|
77 | |||
78 | # HGMERGE takes next precedence |
|
78 | # HGMERGE takes next precedence | |
79 | hgmerge = os.environ.get("HGMERGE") |
|
79 | hgmerge = os.environ.get("HGMERGE") | |
80 | if hgmerge: |
|
80 | if hgmerge: | |
81 | return (hgmerge, hgmerge) |
|
81 | return (hgmerge, hgmerge) | |
82 |
|
82 | |||
83 | # then patterns |
|
83 | # then patterns | |
84 | for pat, tool in ui.configitems("merge-patterns"): |
|
84 | for pat, tool in ui.configitems("merge-patterns"): | |
85 | mf = match.match(repo.root, '', [pat]) |
|
85 | mf = match.match(repo.root, '', [pat]) | |
86 | if mf(path) and check(tool, pat, symlink, False): |
|
86 | if mf(path) and check(tool, pat, symlink, False): | |
87 | toolpath = _findtool(ui, tool) |
|
87 | toolpath = _findtool(ui, tool) | |
88 | return (tool, '"' + toolpath + '"') |
|
88 | return (tool, '"' + toolpath + '"') | |
89 |
|
89 | |||
90 | # then merge tools |
|
90 | # then merge tools | |
91 | tools = {} |
|
91 | tools = {} | |
92 | for k, v in ui.configitems("merge-tools"): |
|
92 | for k, v in ui.configitems("merge-tools"): | |
93 | t = k.split('.')[0] |
|
93 | t = k.split('.')[0] | |
94 | if t not in tools: |
|
94 | if t not in tools: | |
95 | tools[t] = int(_toolstr(ui, t, "priority", "0")) |
|
95 | tools[t] = int(_toolstr(ui, t, "priority", "0")) | |
96 | names = tools.keys() |
|
96 | names = tools.keys() | |
97 | tools = sorted([(-p, t) for t, p in tools.items()]) |
|
97 | tools = sorted([(-p, t) for t, p in tools.items()]) | |
98 | uimerge = ui.config("ui", "merge") |
|
98 | uimerge = ui.config("ui", "merge") | |
99 | if uimerge: |
|
99 | if uimerge: | |
100 | if uimerge not in names: |
|
100 | if uimerge not in names: | |
101 | return (uimerge, uimerge) |
|
101 | return (uimerge, uimerge) | |
102 | tools.insert(0, (None, uimerge)) # highest priority |
|
102 | tools.insert(0, (None, uimerge)) # highest priority | |
103 | tools.append((None, "hgmerge")) # the old default, if found |
|
103 | tools.append((None, "hgmerge")) # the old default, if found | |
104 | for p, t in tools: |
|
104 | for p, t in tools: | |
105 | if check(t, None, symlink, binary): |
|
105 | if check(t, None, symlink, binary): | |
106 | toolpath = _findtool(ui, t) |
|
106 | toolpath = _findtool(ui, t) | |
107 | return (t, '"' + toolpath + '"') |
|
107 | return (t, '"' + toolpath + '"') | |
108 | # internal merge as last resort |
|
108 | # internal merge as last resort | |
109 | return (not (symlink or binary) and "internal:merge" or None, None) |
|
109 | return (not (symlink or binary) and "internal:merge" or None, None) | |
110 |
|
110 | |||
111 | def _eoltype(data): |
|
111 | def _eoltype(data): | |
112 | "Guess the EOL type of a file" |
|
112 | "Guess the EOL type of a file" | |
113 | if '\0' in data: # binary |
|
113 | if '\0' in data: # binary | |
114 | return None |
|
114 | return None | |
115 | if '\r\n' in data: # Windows |
|
115 | if '\r\n' in data: # Windows | |
116 | return '\r\n' |
|
116 | return '\r\n' | |
117 | if '\r' in data: # Old Mac |
|
117 | if '\r' in data: # Old Mac | |
118 | return '\r' |
|
118 | return '\r' | |
119 | if '\n' in data: # UNIX |
|
119 | if '\n' in data: # UNIX | |
120 | return '\n' |
|
120 | return '\n' | |
121 | return None # unknown |
|
121 | return None # unknown | |
122 |
|
122 | |||
123 | def _matcheol(file, origfile): |
|
123 | def _matcheol(file, origfile): | |
124 | "Convert EOL markers in a file to match origfile" |
|
124 | "Convert EOL markers in a file to match origfile" | |
125 | tostyle = _eoltype(util.readfile(origfile)) |
|
125 | tostyle = _eoltype(util.readfile(origfile)) | |
126 | if tostyle: |
|
126 | if tostyle: | |
127 | data = util.readfile(file) |
|
127 | data = util.readfile(file) | |
128 | style = _eoltype(data) |
|
128 | style = _eoltype(data) | |
129 | if style: |
|
129 | if style: | |
130 | newdata = data.replace(style, tostyle) |
|
130 | newdata = data.replace(style, tostyle) | |
131 | if newdata != data: |
|
131 | if newdata != data: | |
132 | util.writefile(file, newdata) |
|
132 | util.writefile(file, newdata) | |
133 |
|
133 | |||
134 | @internaltool('internal:prompt', False) |
|
134 | @internaltool('internal:prompt', False) | |
135 | def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf): |
|
135 | def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf): | |
|
136 | """``internal:prompt`` | |||
|
137 | Asks the user which of the local or the other version to keep as | |||
|
138 | the merged version.""" | |||
136 | ui = repo.ui |
|
139 | ui = repo.ui | |
137 | fd = fcd.path() |
|
140 | fd = fcd.path() | |
138 |
|
141 | |||
139 | if ui.promptchoice(_(" no tool found to merge %s\n" |
|
142 | if ui.promptchoice(_(" no tool found to merge %s\n" | |
140 | "keep (l)ocal or take (o)ther?") % fd, |
|
143 | "keep (l)ocal or take (o)ther?") % fd, | |
141 | (_("&Local"), _("&Other")), 0): |
|
144 | (_("&Local"), _("&Other")), 0): | |
142 | return _iother(repo, mynode, orig, fcd, fco, fca, toolconf) |
|
145 | return _iother(repo, mynode, orig, fcd, fco, fca, toolconf) | |
143 | else: |
|
146 | else: | |
144 | return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf) |
|
147 | return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf) | |
145 |
|
148 | |||
146 | @internaltool('internal:local', False) |
|
149 | @internaltool('internal:local', False) | |
147 | def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf): |
|
150 | def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf): | |
|
151 | """``internal:local`` | |||
|
152 | Uses the local version of files as the merged version.""" | |||
148 | return 0 |
|
153 | return 0 | |
149 |
|
154 | |||
150 | @internaltool('internal:other', False) |
|
155 | @internaltool('internal:other', False) | |
151 | def _iother(repo, mynode, orig, fcd, fco, fca, toolconf): |
|
156 | def _iother(repo, mynode, orig, fcd, fco, fca, toolconf): | |
|
157 | """``internal:other`` | |||
|
158 | Uses the other version of files as the merged version.""" | |||
152 | repo.wwrite(fcd.path(), fco.data(), fco.flags()) |
|
159 | repo.wwrite(fcd.path(), fco.data(), fco.flags()) | |
153 | return 0 |
|
160 | return 0 | |
154 |
|
161 | |||
155 | @internaltool('internal:fail', False) |
|
162 | @internaltool('internal:fail', False) | |
156 | def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf): |
|
163 | def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf): | |
|
164 | """``internal:fail`` | |||
|
165 | Rather than attempting to merge files that were modified on both | |||
|
166 | branches, it marks them as unresolved. The resolve command must be | |||
|
167 | used to resolve these conflicts.""" | |||
157 | return 1 |
|
168 | return 1 | |
158 |
|
169 | |||
159 | def _premerge(repo, toolconf, files): |
|
170 | def _premerge(repo, toolconf, files): | |
160 | tool, toolpath, binary, symlink = toolconf |
|
171 | tool, toolpath, binary, symlink = toolconf | |
161 | a, b, c, back = files |
|
172 | a, b, c, back = files | |
162 |
|
173 | |||
163 | ui = repo.ui |
|
174 | ui = repo.ui | |
164 |
|
175 | |||
165 | # do we attempt to simplemerge first? |
|
176 | # do we attempt to simplemerge first? | |
166 | try: |
|
177 | try: | |
167 | premerge = _toolbool(ui, tool, "premerge", not (binary or symlink)) |
|
178 | premerge = _toolbool(ui, tool, "premerge", not (binary or symlink)) | |
168 | except error.ConfigError: |
|
179 | except error.ConfigError: | |
169 | premerge = _toolstr(ui, tool, "premerge").lower() |
|
180 | premerge = _toolstr(ui, tool, "premerge").lower() | |
170 | valid = 'keep'.split() |
|
181 | valid = 'keep'.split() | |
171 | if premerge not in valid: |
|
182 | if premerge not in valid: | |
172 | _valid = ', '.join(["'" + v + "'" for v in valid]) |
|
183 | _valid = ', '.join(["'" + v + "'" for v in valid]) | |
173 | raise error.ConfigError(_("%s.premerge not valid " |
|
184 | raise error.ConfigError(_("%s.premerge not valid " | |
174 | "('%s' is neither boolean nor %s)") % |
|
185 | "('%s' is neither boolean nor %s)") % | |
175 | (tool, premerge, _valid)) |
|
186 | (tool, premerge, _valid)) | |
176 |
|
187 | |||
177 | if premerge: |
|
188 | if premerge: | |
178 | r = simplemerge.simplemerge(ui, a, b, c, quiet=True) |
|
189 | r = simplemerge.simplemerge(ui, a, b, c, quiet=True) | |
179 | if not r: |
|
190 | if not r: | |
180 | ui.debug(" premerge successful\n") |
|
191 | ui.debug(" premerge successful\n") | |
181 | return 0 |
|
192 | return 0 | |
182 | if premerge != 'keep': |
|
193 | if premerge != 'keep': | |
183 | util.copyfile(back, a) # restore from backup and try again |
|
194 | util.copyfile(back, a) # restore from backup and try again | |
184 | return 1 # continue merging |
|
195 | return 1 # continue merging | |
185 |
|
196 | |||
186 | @internaltool('internal:merge', True, |
|
197 | @internaltool('internal:merge', True, | |
187 | _("merging %s incomplete! " |
|
198 | _("merging %s incomplete! " | |
188 | "(edit conflicts, then use 'hg resolve --mark')\n")) |
|
199 | "(edit conflicts, then use 'hg resolve --mark')\n")) | |
189 | def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files): |
|
200 | def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files): | |
|
201 | """``internal:merge`` | |||
|
202 | Uses the internal non-interactive simple merge algorithm for merging | |||
|
203 | files. It will fail if there are any conflicts and leave markers in | |||
|
204 | the partially merged file.""" | |||
190 | r = _premerge(repo, toolconf, files) |
|
205 | r = _premerge(repo, toolconf, files) | |
191 | if r: |
|
206 | if r: | |
192 | a, b, c, back = files |
|
207 | a, b, c, back = files | |
193 |
|
208 | |||
194 | ui = repo.ui |
|
209 | ui = repo.ui | |
195 |
|
210 | |||
196 | r = simplemerge.simplemerge(ui, a, b, c, label=['local', 'other']) |
|
211 | r = simplemerge.simplemerge(ui, a, b, c, label=['local', 'other']) | |
197 | return True, r |
|
212 | return True, r | |
198 | return False, 0 |
|
213 | return False, 0 | |
199 |
|
214 | |||
200 | @internaltool('internal:dump', True) |
|
215 | @internaltool('internal:dump', True) | |
201 | def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files): |
|
216 | def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files): | |
|
217 | """``internal:dump`` | |||
|
218 | Creates three versions of the files to merge, containing the | |||
|
219 | contents of local, other and base. These files can then be used to | |||
|
220 | perform a merge manually. If the file to be merged is named | |||
|
221 | ``a.txt``, these files will accordingly be named ``a.txt.local``, | |||
|
222 | ``a.txt.other`` and ``a.txt.base`` and they will be placed in the | |||
|
223 | same directory as ``a.txt``.""" | |||
202 | r = _premerge(repo, toolconf, files) |
|
224 | r = _premerge(repo, toolconf, files) | |
203 | if r: |
|
225 | if r: | |
204 | a, b, c, back = files |
|
226 | a, b, c, back = files | |
205 |
|
227 | |||
206 | fd = fcd.path() |
|
228 | fd = fcd.path() | |
207 |
|
229 | |||
208 | util.copyfile(a, a + ".local") |
|
230 | util.copyfile(a, a + ".local") | |
209 | repo.wwrite(fd + ".other", fco.data(), fco.flags()) |
|
231 | repo.wwrite(fd + ".other", fco.data(), fco.flags()) | |
210 | repo.wwrite(fd + ".base", fca.data(), fca.flags()) |
|
232 | repo.wwrite(fd + ".base", fca.data(), fca.flags()) | |
211 | return False, r |
|
233 | return False, r | |
212 |
|
234 | |||
213 | def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files): |
|
235 | def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files): | |
214 | r = _premerge(repo, toolconf, files) |
|
236 | r = _premerge(repo, toolconf, files) | |
215 | if r: |
|
237 | if r: | |
216 | tool, toolpath, binary, symlink = toolconf |
|
238 | tool, toolpath, binary, symlink = toolconf | |
217 | a, b, c, back = files |
|
239 | a, b, c, back = files | |
218 | out = "" |
|
240 | out = "" | |
219 | env = dict(HG_FILE=fcd.path(), |
|
241 | env = dict(HG_FILE=fcd.path(), | |
220 | HG_MY_NODE=short(mynode), |
|
242 | HG_MY_NODE=short(mynode), | |
221 | HG_OTHER_NODE=str(fco.changectx()), |
|
243 | HG_OTHER_NODE=str(fco.changectx()), | |
222 | HG_BASE_NODE=str(fca.changectx()), |
|
244 | HG_BASE_NODE=str(fca.changectx()), | |
223 | HG_MY_ISLINK='l' in fcd.flags(), |
|
245 | HG_MY_ISLINK='l' in fcd.flags(), | |
224 | HG_OTHER_ISLINK='l' in fco.flags(), |
|
246 | HG_OTHER_ISLINK='l' in fco.flags(), | |
225 | HG_BASE_ISLINK='l' in fca.flags()) |
|
247 | HG_BASE_ISLINK='l' in fca.flags()) | |
226 |
|
248 | |||
227 | ui = repo.ui |
|
249 | ui = repo.ui | |
228 |
|
250 | |||
229 | args = _toolstr(ui, tool, "args", '$local $base $other') |
|
251 | args = _toolstr(ui, tool, "args", '$local $base $other') | |
230 | if "$output" in args: |
|
252 | if "$output" in args: | |
231 | out, a = a, back # read input from backup, write to original |
|
253 | out, a = a, back # read input from backup, write to original | |
232 | replace = dict(local=a, base=b, other=c, output=out) |
|
254 | replace = dict(local=a, base=b, other=c, output=out) | |
233 | args = util.interpolate(r'\$', replace, args, |
|
255 | args = util.interpolate(r'\$', replace, args, | |
234 | lambda s: '"%s"' % util.localpath(s)) |
|
256 | lambda s: '"%s"' % util.localpath(s)) | |
235 | r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env, |
|
257 | r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env, | |
236 | out=ui.fout) |
|
258 | out=ui.fout) | |
237 | return True, r |
|
259 | return True, r | |
238 | return False, 0 |
|
260 | return False, 0 | |
239 |
|
261 | |||
240 | def filemerge(repo, mynode, orig, fcd, fco, fca): |
|
262 | def filemerge(repo, mynode, orig, fcd, fco, fca): | |
241 | """perform a 3-way merge in the working directory |
|
263 | """perform a 3-way merge in the working directory | |
242 |
|
264 | |||
243 | mynode = parent node before merge |
|
265 | mynode = parent node before merge | |
244 | orig = original local filename before merge |
|
266 | orig = original local filename before merge | |
245 | fco = other file context |
|
267 | fco = other file context | |
246 | fca = ancestor file context |
|
268 | fca = ancestor file context | |
247 | fcd = local file context for current/destination file |
|
269 | fcd = local file context for current/destination file | |
248 | """ |
|
270 | """ | |
249 |
|
271 | |||
250 | def temp(prefix, ctx): |
|
272 | def temp(prefix, ctx): | |
251 | pre = "%s~%s." % (os.path.basename(ctx.path()), prefix) |
|
273 | pre = "%s~%s." % (os.path.basename(ctx.path()), prefix) | |
252 | (fd, name) = tempfile.mkstemp(prefix=pre) |
|
274 | (fd, name) = tempfile.mkstemp(prefix=pre) | |
253 | data = repo.wwritedata(ctx.path(), ctx.data()) |
|
275 | data = repo.wwritedata(ctx.path(), ctx.data()) | |
254 | f = os.fdopen(fd, "wb") |
|
276 | f = os.fdopen(fd, "wb") | |
255 | f.write(data) |
|
277 | f.write(data) | |
256 | f.close() |
|
278 | f.close() | |
257 | return name |
|
279 | return name | |
258 |
|
280 | |||
259 | if not fco.cmp(fcd): # files identical? |
|
281 | if not fco.cmp(fcd): # files identical? | |
260 | return None |
|
282 | return None | |
261 |
|
283 | |||
262 | ui = repo.ui |
|
284 | ui = repo.ui | |
263 | fd = fcd.path() |
|
285 | fd = fcd.path() | |
264 | binary = fcd.isbinary() or fco.isbinary() or fca.isbinary() |
|
286 | binary = fcd.isbinary() or fco.isbinary() or fca.isbinary() | |
265 | symlink = 'l' in fcd.flags() + fco.flags() |
|
287 | symlink = 'l' in fcd.flags() + fco.flags() | |
266 | tool, toolpath = _picktool(repo, ui, fd, binary, symlink) |
|
288 | tool, toolpath = _picktool(repo, ui, fd, binary, symlink) | |
267 | ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" % |
|
289 | ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" % | |
268 | (tool, fd, binary, symlink)) |
|
290 | (tool, fd, binary, symlink)) | |
269 |
|
291 | |||
270 |
if tool in |
|
292 | if tool in internals: | |
271 |
func = |
|
293 | func = internals[tool] | |
272 | trymerge = func.trymerge |
|
294 | trymerge = func.trymerge | |
273 | onfailure = func.onfailure |
|
295 | onfailure = func.onfailure | |
274 | else: |
|
296 | else: | |
275 | func = _xmerge |
|
297 | func = _xmerge | |
276 | trymerge = True |
|
298 | trymerge = True | |
277 | onfailure = _("merging %s failed!\n") |
|
299 | onfailure = _("merging %s failed!\n") | |
278 |
|
300 | |||
279 | toolconf = tool, toolpath, binary, symlink |
|
301 | toolconf = tool, toolpath, binary, symlink | |
280 |
|
302 | |||
281 | if not trymerge: |
|
303 | if not trymerge: | |
282 | return func(repo, mynode, orig, fcd, fco, fca, toolconf) |
|
304 | return func(repo, mynode, orig, fcd, fco, fca, toolconf) | |
283 |
|
305 | |||
284 | a = repo.wjoin(fd) |
|
306 | a = repo.wjoin(fd) | |
285 | b = temp("base", fca) |
|
307 | b = temp("base", fca) | |
286 | c = temp("other", fco) |
|
308 | c = temp("other", fco) | |
287 | back = a + ".orig" |
|
309 | back = a + ".orig" | |
288 | util.copyfile(a, back) |
|
310 | util.copyfile(a, back) | |
289 |
|
311 | |||
290 | if orig != fco.path(): |
|
312 | if orig != fco.path(): | |
291 | ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd)) |
|
313 | ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd)) | |
292 | else: |
|
314 | else: | |
293 | ui.status(_("merging %s\n") % fd) |
|
315 | ui.status(_("merging %s\n") % fd) | |
294 |
|
316 | |||
295 | ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca)) |
|
317 | ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca)) | |
296 |
|
318 | |||
297 | needcheck, r = func(repo, mynode, orig, fcd, fco, fca, toolconf, |
|
319 | needcheck, r = func(repo, mynode, orig, fcd, fco, fca, toolconf, | |
298 | (a, b, c, back)) |
|
320 | (a, b, c, back)) | |
299 | if not needcheck: |
|
321 | if not needcheck: | |
300 | if r: |
|
322 | if r: | |
301 | if onfailure: |
|
323 | if onfailure: | |
302 | ui.warn(onfailure % fd) |
|
324 | ui.warn(onfailure % fd) | |
303 | else: |
|
325 | else: | |
304 | os.unlink(back) |
|
326 | os.unlink(back) | |
305 |
|
327 | |||
306 | os.unlink(b) |
|
328 | os.unlink(b) | |
307 | os.unlink(c) |
|
329 | os.unlink(c) | |
308 | return r |
|
330 | return r | |
309 |
|
331 | |||
310 | if not r and (_toolbool(ui, tool, "checkconflicts") or |
|
332 | if not r and (_toolbool(ui, tool, "checkconflicts") or | |
311 | 'conflicts' in _toollist(ui, tool, "check")): |
|
333 | 'conflicts' in _toollist(ui, tool, "check")): | |
312 | if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(), |
|
334 | if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(), | |
313 | re.MULTILINE): |
|
335 | re.MULTILINE): | |
314 | r = 1 |
|
336 | r = 1 | |
315 |
|
337 | |||
316 | checked = False |
|
338 | checked = False | |
317 | if 'prompt' in _toollist(ui, tool, "check"): |
|
339 | if 'prompt' in _toollist(ui, tool, "check"): | |
318 | checked = True |
|
340 | checked = True | |
319 | if ui.promptchoice(_("was merge of '%s' successful (yn)?") % fd, |
|
341 | if ui.promptchoice(_("was merge of '%s' successful (yn)?") % fd, | |
320 | (_("&Yes"), _("&No")), 1): |
|
342 | (_("&Yes"), _("&No")), 1): | |
321 | r = 1 |
|
343 | r = 1 | |
322 |
|
344 | |||
323 | if not r and not checked and (_toolbool(ui, tool, "checkchanged") or |
|
345 | if not r and not checked and (_toolbool(ui, tool, "checkchanged") or | |
324 | 'changed' in _toollist(ui, tool, "check")): |
|
346 | 'changed' in _toollist(ui, tool, "check")): | |
325 | if filecmp.cmp(a, back): |
|
347 | if filecmp.cmp(a, back): | |
326 | if ui.promptchoice(_(" output file %s appears unchanged\n" |
|
348 | if ui.promptchoice(_(" output file %s appears unchanged\n" | |
327 | "was merge successful (yn)?") % fd, |
|
349 | "was merge successful (yn)?") % fd, | |
328 | (_("&Yes"), _("&No")), 1): |
|
350 | (_("&Yes"), _("&No")), 1): | |
329 | r = 1 |
|
351 | r = 1 | |
330 |
|
352 | |||
331 | if _toolbool(ui, tool, "fixeol"): |
|
353 | if _toolbool(ui, tool, "fixeol"): | |
332 | _matcheol(a, back) |
|
354 | _matcheol(a, back) | |
333 |
|
355 | |||
334 | if r: |
|
356 | if r: | |
335 | if onfailure: |
|
357 | if onfailure: | |
336 | ui.warn(onfailure % fd) |
|
358 | ui.warn(onfailure % fd) | |
337 | else: |
|
359 | else: | |
338 | os.unlink(back) |
|
360 | os.unlink(back) | |
339 |
|
361 | |||
340 | os.unlink(b) |
|
362 | os.unlink(b) | |
341 | os.unlink(c) |
|
363 | os.unlink(c) | |
342 | return r |
|
364 | return r | |
|
365 | ||||
|
366 | # tell hggettext to extract docstrings from these functions: | |||
|
367 | i18nfunctions = internals.values() |
@@ -1,110 +1,111 b'' | |||||
1 | # help.py - help data for mercurial |
|
1 | # help.py - help data for mercurial | |
2 | # |
|
2 | # | |
3 | # Copyright 2006 Matt Mackall <mpm@selenic.com> |
|
3 | # Copyright 2006 Matt Mackall <mpm@selenic.com> | |
4 | # |
|
4 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
5 | # This software may be used and distributed according to the terms of the | |
6 | # GNU General Public License version 2 or any later version. |
|
6 | # GNU General Public License version 2 or any later version. | |
7 |
|
7 | |||
8 | from i18n import gettext, _ |
|
8 | from i18n import gettext, _ | |
9 | import sys, os |
|
9 | import sys, os | |
10 | import extensions, revset, fileset, templatekw, templatefilters |
|
10 | import extensions, revset, fileset, templatekw, templatefilters, filemerge | |
11 | import util |
|
11 | import util | |
12 |
|
12 | |||
13 | def listexts(header, exts, indent=1): |
|
13 | def listexts(header, exts, indent=1): | |
14 | '''return a text listing of the given extensions''' |
|
14 | '''return a text listing of the given extensions''' | |
15 | if not exts: |
|
15 | if not exts: | |
16 | return '' |
|
16 | return '' | |
17 | maxlength = max(len(e) for e in exts) |
|
17 | maxlength = max(len(e) for e in exts) | |
18 | result = '\n%s\n\n' % header |
|
18 | result = '\n%s\n\n' % header | |
19 | for name, desc in sorted(exts.iteritems()): |
|
19 | for name, desc in sorted(exts.iteritems()): | |
20 | result += '%s%-*s %s\n' % (' ' * indent, maxlength + 2, |
|
20 | result += '%s%-*s %s\n' % (' ' * indent, maxlength + 2, | |
21 | ':%s:' % name, desc) |
|
21 | ':%s:' % name, desc) | |
22 | return result |
|
22 | return result | |
23 |
|
23 | |||
24 | def extshelp(): |
|
24 | def extshelp(): | |
25 | doc = loaddoc('extensions')() |
|
25 | doc = loaddoc('extensions')() | |
26 | doc += listexts(_('enabled extensions:'), extensions.enabled()) |
|
26 | doc += listexts(_('enabled extensions:'), extensions.enabled()) | |
27 | doc += listexts(_('disabled extensions:'), extensions.disabled()) |
|
27 | doc += listexts(_('disabled extensions:'), extensions.disabled()) | |
28 | return doc |
|
28 | return doc | |
29 |
|
29 | |||
30 | def loaddoc(topic): |
|
30 | def loaddoc(topic): | |
31 | """Return a delayed loader for help/topic.txt.""" |
|
31 | """Return a delayed loader for help/topic.txt.""" | |
32 |
|
32 | |||
33 | def loader(): |
|
33 | def loader(): | |
34 | if util.mainfrozen(): |
|
34 | if util.mainfrozen(): | |
35 | module = sys.executable |
|
35 | module = sys.executable | |
36 | else: |
|
36 | else: | |
37 | module = __file__ |
|
37 | module = __file__ | |
38 | base = os.path.dirname(module) |
|
38 | base = os.path.dirname(module) | |
39 |
|
39 | |||
40 | for dir in ('.', '..'): |
|
40 | for dir in ('.', '..'): | |
41 | docdir = os.path.join(base, dir, 'help') |
|
41 | docdir = os.path.join(base, dir, 'help') | |
42 | if os.path.isdir(docdir): |
|
42 | if os.path.isdir(docdir): | |
43 | break |
|
43 | break | |
44 |
|
44 | |||
45 | path = os.path.join(docdir, topic + ".txt") |
|
45 | path = os.path.join(docdir, topic + ".txt") | |
46 | doc = gettext(util.readfile(path)) |
|
46 | doc = gettext(util.readfile(path)) | |
47 | for rewriter in helphooks.get(topic, []): |
|
47 | for rewriter in helphooks.get(topic, []): | |
48 | doc = rewriter(topic, doc) |
|
48 | doc = rewriter(topic, doc) | |
49 | return doc |
|
49 | return doc | |
50 |
|
50 | |||
51 | return loader |
|
51 | return loader | |
52 |
|
52 | |||
53 | helptable = sorted([ |
|
53 | helptable = sorted([ | |
54 | (["config", "hgrc"], _("Configuration Files"), loaddoc('config')), |
|
54 | (["config", "hgrc"], _("Configuration Files"), loaddoc('config')), | |
55 | (["dates"], _("Date Formats"), loaddoc('dates')), |
|
55 | (["dates"], _("Date Formats"), loaddoc('dates')), | |
56 | (["patterns"], _("File Name Patterns"), loaddoc('patterns')), |
|
56 | (["patterns"], _("File Name Patterns"), loaddoc('patterns')), | |
57 | (['environment', 'env'], _('Environment Variables'), |
|
57 | (['environment', 'env'], _('Environment Variables'), | |
58 | loaddoc('environment')), |
|
58 | loaddoc('environment')), | |
59 | (['revs', 'revisions'], _('Specifying Single Revisions'), |
|
59 | (['revs', 'revisions'], _('Specifying Single Revisions'), | |
60 | loaddoc('revisions')), |
|
60 | loaddoc('revisions')), | |
61 | (['mrevs', 'multirevs'], _('Specifying Multiple Revisions'), |
|
61 | (['mrevs', 'multirevs'], _('Specifying Multiple Revisions'), | |
62 | loaddoc('multirevs')), |
|
62 | loaddoc('multirevs')), | |
63 | (['revset', 'revsets'], _("Specifying Revision Sets"), loaddoc('revsets')), |
|
63 | (['revset', 'revsets'], _("Specifying Revision Sets"), loaddoc('revsets')), | |
64 | (['fileset', 'filesets'], _("Specifying File Sets"), loaddoc('filesets')), |
|
64 | (['fileset', 'filesets'], _("Specifying File Sets"), loaddoc('filesets')), | |
65 | (['diffs'], _('Diff Formats'), loaddoc('diffs')), |
|
65 | (['diffs'], _('Diff Formats'), loaddoc('diffs')), | |
66 | (['merge-tools'], _('Merge Tools'), loaddoc('merge-tools')), |
|
66 | (['merge-tools'], _('Merge Tools'), loaddoc('merge-tools')), | |
67 | (['templating', 'templates'], _('Template Usage'), |
|
67 | (['templating', 'templates'], _('Template Usage'), | |
68 | loaddoc('templates')), |
|
68 | loaddoc('templates')), | |
69 | (['urls'], _('URL Paths'), loaddoc('urls')), |
|
69 | (['urls'], _('URL Paths'), loaddoc('urls')), | |
70 | (["extensions"], _("Using additional features"), extshelp), |
|
70 | (["extensions"], _("Using additional features"), extshelp), | |
71 | (["subrepo", "subrepos"], _("Subrepositories"), loaddoc('subrepos')), |
|
71 | (["subrepo", "subrepos"], _("Subrepositories"), loaddoc('subrepos')), | |
72 | (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')), |
|
72 | (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')), | |
73 | (["glossary"], _("Glossary"), loaddoc('glossary')), |
|
73 | (["glossary"], _("Glossary"), loaddoc('glossary')), | |
74 | (["hgignore", "ignore"], _("syntax for Mercurial ignore files"), |
|
74 | (["hgignore", "ignore"], _("syntax for Mercurial ignore files"), | |
75 | loaddoc('hgignore')), |
|
75 | loaddoc('hgignore')), | |
76 | (["phases"], _("Working with Phases"), loaddoc('phases')), |
|
76 | (["phases"], _("Working with Phases"), loaddoc('phases')), | |
77 | ]) |
|
77 | ]) | |
78 |
|
78 | |||
79 | # Map topics to lists of callable taking the current topic help and |
|
79 | # Map topics to lists of callable taking the current topic help and | |
80 | # returning the updated version |
|
80 | # returning the updated version | |
81 | helphooks = {} |
|
81 | helphooks = {} | |
82 |
|
82 | |||
83 | def addtopichook(topic, rewriter): |
|
83 | def addtopichook(topic, rewriter): | |
84 | helphooks.setdefault(topic, []).append(rewriter) |
|
84 | helphooks.setdefault(topic, []).append(rewriter) | |
85 |
|
85 | |||
86 | def makeitemsdoc(topic, doc, marker, items): |
|
86 | def makeitemsdoc(topic, doc, marker, items): | |
87 | """Extract docstring from the items key to function mapping, build a |
|
87 | """Extract docstring from the items key to function mapping, build a | |
88 | .single documentation block and use it to overwrite the marker in doc |
|
88 | .single documentation block and use it to overwrite the marker in doc | |
89 | """ |
|
89 | """ | |
90 | entries = [] |
|
90 | entries = [] | |
91 | for name in sorted(items): |
|
91 | for name in sorted(items): | |
92 | text = (items[name].__doc__ or '').rstrip() |
|
92 | text = (items[name].__doc__ or '').rstrip() | |
93 | if not text: |
|
93 | if not text: | |
94 | continue |
|
94 | continue | |
95 | text = gettext(text) |
|
95 | text = gettext(text) | |
96 | lines = text.splitlines() |
|
96 | lines = text.splitlines() | |
97 | lines[1:] = [(' ' + l.strip()) for l in lines[1:]] |
|
97 | lines[1:] = [(' ' + l.strip()) for l in lines[1:]] | |
98 | entries.append('\n'.join(lines)) |
|
98 | entries.append('\n'.join(lines)) | |
99 | entries = '\n\n'.join(entries) |
|
99 | entries = '\n\n'.join(entries) | |
100 | return doc.replace(marker, entries) |
|
100 | return doc.replace(marker, entries) | |
101 |
|
101 | |||
102 | def addtopicsymbols(topic, marker, symbols): |
|
102 | def addtopicsymbols(topic, marker, symbols): | |
103 | def add(topic, doc): |
|
103 | def add(topic, doc): | |
104 | return makeitemsdoc(topic, doc, marker, symbols) |
|
104 | return makeitemsdoc(topic, doc, marker, symbols) | |
105 | addtopichook(topic, add) |
|
105 | addtopichook(topic, add) | |
106 |
|
106 | |||
107 | addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols) |
|
107 | addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols) | |
|
108 | addtopicsymbols('merge-tools', '.. internaltoolsmarker', filemerge.internals) | |||
108 | addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols) |
|
109 | addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols) | |
109 | addtopicsymbols('templates', '.. keywordsmarker', templatekw.keywords) |
|
110 | addtopicsymbols('templates', '.. keywordsmarker', templatekw.keywords) | |
110 | addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters) |
|
111 | addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters) |
@@ -1,110 +1,84 b'' | |||||
1 | To merge files Mercurial uses merge tools. |
|
1 | To merge files Mercurial uses merge tools. | |
2 |
|
2 | |||
3 | A merge tool combines two different versions of a file into a merged |
|
3 | A merge tool combines two different versions of a file into a merged | |
4 | file. Merge tools are given the two files and the greatest common |
|
4 | file. Merge tools are given the two files and the greatest common | |
5 | ancestor of the two file versions, so they can determine the changes |
|
5 | ancestor of the two file versions, so they can determine the changes | |
6 | made on both branches. |
|
6 | made on both branches. | |
7 |
|
7 | |||
8 | Merge tools are used both for :hg:`resolve`, :hg:`merge`, :hg:`update`, |
|
8 | Merge tools are used both for :hg:`resolve`, :hg:`merge`, :hg:`update`, | |
9 | :hg:`backout` and in several extensions. |
|
9 | :hg:`backout` and in several extensions. | |
10 |
|
10 | |||
11 | Usually, the merge tool tries to automatically reconcile the files by |
|
11 | Usually, the merge tool tries to automatically reconcile the files by | |
12 | combining all non-overlapping changes that occurred separately in |
|
12 | combining all non-overlapping changes that occurred separately in | |
13 | the two different evolutions of the same initial base file. Furthermore, some |
|
13 | the two different evolutions of the same initial base file. Furthermore, some | |
14 | interactive merge programs make it easier to manually resolve |
|
14 | interactive merge programs make it easier to manually resolve | |
15 | conflicting merges, either in a graphical way, or by inserting some |
|
15 | conflicting merges, either in a graphical way, or by inserting some | |
16 | conflict markers. Mercurial does not include any interactive merge |
|
16 | conflict markers. Mercurial does not include any interactive merge | |
17 | programs but relies on external tools for that. |
|
17 | programs but relies on external tools for that. | |
18 |
|
18 | |||
19 | Available merge tools |
|
19 | Available merge tools | |
20 | """"""""""""""""""""" |
|
20 | """"""""""""""""""""" | |
21 |
|
21 | |||
22 | External merge tools and their properties are configured in the |
|
22 | External merge tools and their properties are configured in the | |
23 | merge-tools configuration section - see hgrc(5) - but they can often just |
|
23 | merge-tools configuration section - see hgrc(5) - but they can often just | |
24 | be named by their executable. |
|
24 | be named by their executable. | |
25 |
|
25 | |||
26 | A merge tool is generally usable if its executable can be found on the |
|
26 | A merge tool is generally usable if its executable can be found on the | |
27 | system and if it can handle the merge. The executable is found if it |
|
27 | system and if it can handle the merge. The executable is found if it | |
28 | is an absolute or relative executable path or the name of an |
|
28 | is an absolute or relative executable path or the name of an | |
29 | application in the executable search path. The tool is assumed to be |
|
29 | application in the executable search path. The tool is assumed to be | |
30 | able to handle the merge if it can handle symlinks if the file is a |
|
30 | able to handle the merge if it can handle symlinks if the file is a | |
31 | symlink, if it can handle binary files if the file is binary, and if a |
|
31 | symlink, if it can handle binary files if the file is binary, and if a | |
32 | GUI is available if the tool requires a GUI. |
|
32 | GUI is available if the tool requires a GUI. | |
33 |
|
33 | |||
34 | There are some internal merge tools which can be used. The internal |
|
34 | There are some internal merge tools which can be used. The internal | |
35 | merge tools are: |
|
35 | merge tools are: | |
36 |
|
36 | |||
37 | ``internal:merge`` |
|
37 | .. internaltoolsmarker | |
38 | Uses the internal non-interactive simple merge algorithm for merging |
|
|||
39 | files. It will fail if there are any conflicts and leave markers in |
|
|||
40 | the partially merged file. |
|
|||
41 |
|
||||
42 | ``internal:fail`` |
|
|||
43 | Rather than attempting to merge files that were modified on both |
|
|||
44 | branches, it marks them as unresolved. The resolve command must be |
|
|||
45 | used to resolve these conflicts. |
|
|||
46 |
|
||||
47 | ``internal:local`` |
|
|||
48 | Uses the local version of files as the merged version. |
|
|||
49 |
|
||||
50 | ``internal:other`` |
|
|||
51 | Uses the other version of files as the merged version. |
|
|||
52 |
|
||||
53 | ``internal:prompt`` |
|
|||
54 | Asks the user which of the local or the other version to keep as |
|
|||
55 | the merged version. |
|
|||
56 |
|
||||
57 | ``internal:dump`` |
|
|||
58 | Creates three versions of the files to merge, containing the |
|
|||
59 | contents of local, other and base. These files can then be used to |
|
|||
60 | perform a merge manually. If the file to be merged is named |
|
|||
61 | ``a.txt``, these files will accordingly be named ``a.txt.local``, |
|
|||
62 | ``a.txt.other`` and ``a.txt.base`` and they will be placed in the |
|
|||
63 | same directory as ``a.txt``. |
|
|||
64 |
|
38 | |||
65 | Internal tools are always available and do not require a GUI but will by default |
|
39 | Internal tools are always available and do not require a GUI but will by default | |
66 | not handle symlinks or binary files. |
|
40 | not handle symlinks or binary files. | |
67 |
|
41 | |||
68 | Choosing a merge tool |
|
42 | Choosing a merge tool | |
69 | """"""""""""""""""""" |
|
43 | """"""""""""""""""""" | |
70 |
|
44 | |||
71 | Mercurial uses these rules when deciding which merge tool to use: |
|
45 | Mercurial uses these rules when deciding which merge tool to use: | |
72 |
|
46 | |||
73 | 1. If a tool has been specified with the --tool option to merge or resolve, it |
|
47 | 1. If a tool has been specified with the --tool option to merge or resolve, it | |
74 | is used. If it is the name of a tool in the merge-tools configuration, its |
|
48 | is used. If it is the name of a tool in the merge-tools configuration, its | |
75 | configuration is used. Otherwise the specified tool must be executable by |
|
49 | configuration is used. Otherwise the specified tool must be executable by | |
76 | the shell. |
|
50 | the shell. | |
77 |
|
51 | |||
78 | 2. If the ``HGMERGE`` environment variable is present, its value is used and |
|
52 | 2. If the ``HGMERGE`` environment variable is present, its value is used and | |
79 | must be executable by the shell. |
|
53 | must be executable by the shell. | |
80 |
|
54 | |||
81 | 3. If the filename of the file to be merged matches any of the patterns in the |
|
55 | 3. If the filename of the file to be merged matches any of the patterns in the | |
82 | merge-patterns configuration section, the first usable merge tool |
|
56 | merge-patterns configuration section, the first usable merge tool | |
83 | corresponding to a matching pattern is used. Here, binary capabilities of the |
|
57 | corresponding to a matching pattern is used. Here, binary capabilities of the | |
84 | merge tool are not considered. |
|
58 | merge tool are not considered. | |
85 |
|
59 | |||
86 | 4. If ui.merge is set it will be considered next. If the value is not the name |
|
60 | 4. If ui.merge is set it will be considered next. If the value is not the name | |
87 | of a configured tool, the specified value is used and must be executable by |
|
61 | of a configured tool, the specified value is used and must be executable by | |
88 | the shell. Otherwise the named tool is used if it is usable. |
|
62 | the shell. Otherwise the named tool is used if it is usable. | |
89 |
|
63 | |||
90 | 5. If any usable merge tools are present in the merge-tools configuration |
|
64 | 5. If any usable merge tools are present in the merge-tools configuration | |
91 | section, the one with the highest priority is used. |
|
65 | section, the one with the highest priority is used. | |
92 |
|
66 | |||
93 | 6. If a program named ``hgmerge`` can be found on the system, it is used - but |
|
67 | 6. If a program named ``hgmerge`` can be found on the system, it is used - but | |
94 | it will by default not be used for symlinks and binary files. |
|
68 | it will by default not be used for symlinks and binary files. | |
95 |
|
69 | |||
96 | 7. If the file to be merged is not binary and is not a symlink, then |
|
70 | 7. If the file to be merged is not binary and is not a symlink, then | |
97 | ``internal:merge`` is used. |
|
71 | ``internal:merge`` is used. | |
98 |
|
72 | |||
99 | 8. The merge of the file fails and must be resolved before commit. |
|
73 | 8. The merge of the file fails and must be resolved before commit. | |
100 |
|
74 | |||
101 | .. note:: |
|
75 | .. note:: | |
102 | After selecting a merge program, Mercurial will by default attempt |
|
76 | After selecting a merge program, Mercurial will by default attempt | |
103 | to merge the files using a simple merge algorithm first. Only if it doesn't |
|
77 | to merge the files using a simple merge algorithm first. Only if it doesn't | |
104 | succeed because of conflicting changes Mercurial will actually execute the |
|
78 | succeed because of conflicting changes Mercurial will actually execute the | |
105 | merge program. Whether to use the simple merge algorithm first can be |
|
79 | merge program. Whether to use the simple merge algorithm first can be | |
106 | controlled by the premerge setting of the merge tool. Premerge is enabled by |
|
80 | controlled by the premerge setting of the merge tool. Premerge is enabled by | |
107 | default unless the file is binary or a symlink. |
|
81 | default unless the file is binary or a symlink. | |
108 |
|
82 | |||
109 | See the merge-tools and ui sections of hgrc(5) for details on the |
|
83 | See the merge-tools and ui sections of hgrc(5) for details on the | |
110 | configuration of merge tools. |
|
84 | configuration of merge tools. |
General Comments 0
You need to be logged in to leave comments.
Login now