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