6 # of the GNU General Public License, incorporated herein by reference. |
6 # of the GNU General Public License, incorporated herein by reference. |
7 |
7 |
8 from node import * |
8 from node import * |
9 from i18n import gettext as _ |
9 from i18n import gettext as _ |
10 from demandload import * |
10 from demandload import * |
11 demandload(globals(), "util os tempfile") |
11 demandload(globals(), "errno util os tempfile") |
12 |
12 |
13 def fmerge(f, local, other, ancestor): |
13 def fmerge(f, local, other, ancestor): |
14 """merge executable flags""" |
14 """merge executable flags""" |
15 a, b, c = ancestor.execf(f), local.execf(f), other.execf(f) |
15 a, b, c = ancestor.execf(f), local.execf(f), other.execf(f) |
16 return ((a^b) | (a^c)) ^ a |
16 return ((a^b) | (a^c)) ^ a |
86 modified, added, removed, deleted, unknown = repo.status()[:5] |
86 modified, added, removed, deleted, unknown = repo.status()[:5] |
87 if branchmerge and not forcemerge: |
87 if branchmerge and not forcemerge: |
88 if modified or added or removed: |
88 if modified or added or removed: |
89 raise util.Abort(_("outstanding uncommitted changes")) |
89 raise util.Abort(_("outstanding uncommitted changes")) |
90 |
90 |
91 m1n = repo.changelog.read(p1)[0] |
91 m1 = repo.changectx(p1).manifest().copy() |
92 m2n = repo.changelog.read(p2)[0] |
92 m2 = repo.changectx(p2).manifest().copy() |
93 man = repo.manifest.ancestor(m1n, m2n) |
93 ma = repo.changectx(pa).manifest() |
94 m1 = repo.manifest.read(m1n).copy() |
|
95 m2 = repo.manifest.read(m2n).copy() |
|
96 ma = repo.manifest.read(man) |
|
97 |
94 |
98 if not force: |
95 if not force: |
99 for f in unknown: |
96 for f in unknown: |
100 if f in m2: |
97 if f in m2: |
101 if repo.file(f).cmp(m2[f], repo.wread(f)): |
98 if repo.file(f).cmp(m2[f], repo.wread(f)): |
106 # we care about merging |
103 # we care about merging |
107 repo.ui.note(_("resolving manifests\n")) |
104 repo.ui.note(_("resolving manifests\n")) |
108 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s linear %s\n") % |
105 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s linear %s\n") % |
109 (overwrite, branchmerge, bool(partial), linear_path)) |
106 (overwrite, branchmerge, bool(partial), linear_path)) |
110 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % |
107 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % |
111 (short(man), short(m1n), short(m2n))) |
108 (short(p1), short(p2), short(pa))) |
112 |
109 |
113 action = {} |
110 action = {} |
114 forget = [] |
111 forget = [] |
115 |
112 |
116 # update m1 from working dir |
113 # update m1 from working dir |
117 umap = dict.fromkeys(unknown) |
114 umap = dict.fromkeys(unknown) |
118 |
115 |
119 for f in added + modified + unknown: |
116 for i,l in (("a", added), ("m", modified), ("u", unknown)): |
120 m1[f] = m1.get(f, nullid) + "+" |
117 for f in l: |
121 m1.set(f, util.is_exec(repo.wjoin(f), m1.execf(f))) |
118 m1[f] = m1.get(f, nullid) + i |
|
119 m1.set(f, util.is_exec(repo.wjoin(f), m1.execf(f))) |
122 |
120 |
123 for f in deleted + removed: |
121 for f in deleted + removed: |
124 del m1[f] |
122 del m1[f] |
125 |
123 |
126 # If we're jumping between revisions (as opposed to merging), |
124 # If we're jumping between revisions (as opposed to merging), |
155 # or are we going back in time and clean? |
153 # or are we going back in time and clean? |
156 elif overwrite or m2[f] != a or (backwards and not n[20:]): |
154 elif overwrite or m2[f] != a or (backwards and not n[20:]): |
157 repo.ui.debug(_(" remote %s is newer, get\n") % f) |
155 repo.ui.debug(_(" remote %s is newer, get\n") % f) |
158 action[f] = (m2.execf(f), m2[f], None) |
156 action[f] = (m2.execf(f), m2[f], None) |
159 queued = 1 |
157 queued = 1 |
160 elif f in umap or f in added: |
158 elif n[20:] in ("u","a"): |
161 # this unknown file is the same as the checkout |
159 # this unknown file is the same as the checkout |
162 # we need to reset the dirstate if the file was added |
160 # we need to reset the dirstate if the file was added |
163 action[f] = (m2.execf(f), m2[f], None) |
161 action[f] = (m2.execf(f), m2[f], None) |
164 |
162 |
165 # do we still need to look at mode bits? |
163 # do we still need to look at mode bits? |
166 if not queued and m1.execf(f) != m2.execf(f): |
164 if not queued and m1.execf(f) != m2.execf(f): |
167 if overwrite: |
165 if overwrite: |
168 repo.ui.debug(_(" updating permissions for %s\n") % f) |
166 repo.ui.debug(_(" updating permissions for %s\n") % f) |
169 util.set_exec(repo.wjoin(f), m2.execf(f)) |
167 util.set_exec(repo.wjoin(f), m2.execf(f)) |
170 else: |
168 else: |
171 if fmerge(f, m1, m2, ma) != m1.execf(f): |
169 mode = fmerge(f, m1, m2, ma) |
|
170 if mode != m1.execf(f): |
172 repo.ui.debug(_(" updating permissions for %s\n") |
171 repo.ui.debug(_(" updating permissions for %s\n") |
173 % f) |
172 % f) |
174 util.set_exec(repo.wjoin(f), mode) |
173 util.set_exec(repo.wjoin(f), mode) |
175 del m2[f] |
174 del m2[f] |
176 elif f in ma: |
175 elif f in ma: |
185 else: |
184 else: |
186 repo.ui.debug(_("other deleted %s\n") % f) |
185 repo.ui.debug(_("other deleted %s\n") % f) |
187 action[f] = (None, None, None) |
186 action[f] = (None, None, None) |
188 else: |
187 else: |
189 # file is created on branch or in working directory |
188 # file is created on branch or in working directory |
190 if overwrite and f not in umap: |
189 if overwrite and n[20:] != "u": |
191 repo.ui.debug(_("remote deleted %s, clobbering\n") % f) |
190 repo.ui.debug(_("remote deleted %s, clobbering\n") % f) |
192 action[f] = (None, None, None) |
191 action[f] = (None, None, None) |
193 elif not n[20:]: # same as parent |
192 elif not n[20:]: # same as parent |
194 if backwards: |
193 if backwards: |
195 repo.ui.debug(_("remote deleted %s\n") % f) |
194 repo.ui.debug(_("remote deleted %s\n") % f) |
234 else: xxp2 = xp2 |
233 else: xxp2 = xp2 |
235 |
234 |
236 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2) |
235 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2) |
237 |
236 |
238 # update files |
237 # update files |
239 unresolved = [] |
238 updated, merged, removed, unresolved = 0, 0, 0, 0 |
240 updated, merged, removed = 0, 0, 0 |
|
241 files = action.keys() |
239 files = action.keys() |
242 files.sort() |
240 files.sort() |
243 for f in files: |
241 for f in files: |
244 flag, my, other = action[f] |
242 flag, my, other = action[f] |
245 if f[0] == "/": |
243 if f[0] == "/": |
255 (f, inst.strerror)) |
253 (f, inst.strerror)) |
256 removed +=1 |
254 removed +=1 |
257 elif other: |
255 elif other: |
258 repo.ui.status(_("merging %s\n") % f) |
256 repo.ui.status(_("merging %s\n") % f) |
259 if merge3(repo, f, my, other, xp1, xp2): |
257 if merge3(repo, f, my, other, xp1, xp2): |
260 unresolved.append(f) |
258 unresolved += 1 |
261 util.set_exec(repo.wjoin(f), flag) |
259 util.set_exec(repo.wjoin(f), flag) |
262 merged += 1 |
260 merged += 1 |
263 else: |
261 else: |
264 repo.ui.note(_("getting %s\n") % f) |
262 repo.ui.note(_("getting %s\n") % f) |
265 t = repo.file(f).read(my) |
263 t = repo.file(f).read(my) |
300 f_len = fl.size(fl.rev(other)) |
298 f_len = fl.size(fl.rev(other)) |
301 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1) |
299 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1) |
302 |
300 |
303 if show_stats: |
301 if show_stats: |
304 stats = ((updated, _("updated")), |
302 stats = ((updated, _("updated")), |
305 (merged - len(unresolved), _("merged")), |
303 (merged - unresolved, _("merged")), |
306 (removed, _("removed")), |
304 (removed, _("removed")), |
307 (len(unresolved), _("unresolved"))) |
305 (unresolved, _("unresolved"))) |
308 note = ", ".join([_("%d files %s") % s for s in stats]) |
306 note = ", ".join([_("%d files %s") % s for s in stats]) |
309 repo.ui.status("%s\n" % note) |
307 repo.ui.status("%s\n" % note) |
310 if not partial: |
308 if not partial: |
311 if branchmerge: |
309 if branchmerge: |
312 if unresolved: |
310 if unresolved: |
320 repo.ui.status(_("(branch merge, don't forget to commit)\n")) |
318 repo.ui.status(_("(branch merge, don't forget to commit)\n")) |
321 elif unresolved: |
319 elif unresolved: |
322 repo.ui.status(_("There are unresolved merges with" |
320 repo.ui.status(_("There are unresolved merges with" |
323 " locally modified files.\n")) |
321 " locally modified files.\n")) |
324 |
322 |
325 repo.hook('update', parent1=xp1, parent2=xxp2, error=len(unresolved)) |
323 repo.hook('update', parent1=xp1, parent2=xxp2, error=unresolved) |
326 return len(unresolved) |
324 return unresolved |
327 |
325 |