mercurial/merge.py
changeset 3100 09e8aecd8016
parent 3098 fe9b13e35e46
child 3105 ef4e5d05bac4
equal deleted inserted replaced
3099:c27d1e1798a3 3100:09e8aecd8016
     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