mercurial/merge.py
changeset 3107 3bd05ad67f45
parent 3106 4ec28446fca8
child 3108 67874ebbb7dd
equal deleted inserted replaced
3106:4ec28446fca8 3107:3bd05ad67f45
    50 
    50 
    51     os.unlink(b)
    51     os.unlink(b)
    52     os.unlink(c)
    52     os.unlink(c)
    53     return r
    53     return r
    54 
    54 
    55 def manifestmerge(ui, m1, m2, ma, overwrite, backwards):
    55 def checkunknown(repo, m2, status):
       
    56     """
       
    57     check for collisions between unknown files and files in m2
       
    58     """
       
    59     modified, added, removed, deleted, unknown = status[:5]
       
    60     for f in unknown:
       
    61         if f in m2:
       
    62             if repo.file(f).cmp(m2[f], repo.wread(f)):
       
    63                 raise util.Abort(_("'%s' already exists in the working"
       
    64                                    " dir and differs from remote") % f)
       
    65 
       
    66 def workingmanifest(repo, man, status):
       
    67     """
       
    68     Update manifest to correspond to the working directory
       
    69     """
       
    70 
       
    71     modified, added, removed, deleted, unknown = status[:5]
       
    72     for i,l in (("a", added), ("m", modified), ("u", unknown)):
       
    73         for f in l:
       
    74             man[f] = man.get(f, nullid) + i
       
    75             man.set(f, util.is_exec(repo.wjoin(f), man.execf(f)))
       
    76 
       
    77     for f in deleted + removed:
       
    78         del man[f]
       
    79 
       
    80     return man
       
    81 
       
    82 def forgetremoved(m2, status):
       
    83     """
       
    84     Forget removed files
       
    85 
       
    86     If we're jumping between revisions (as opposed to merging), and if
       
    87     neither the working directory nor the target rev has the file,
       
    88     then we need to remove it from the dirstate, to prevent the
       
    89     dirstate from listing the file when it is no longer in the
       
    90     manifest.
       
    91     """
       
    92 
       
    93     modified, added, removed, deleted, unknown = status[:5]
       
    94     action = []
       
    95 
       
    96     for f in deleted + removed:
       
    97         if f not in m2:
       
    98             action.append((f, "f"))
       
    99 
       
   100     return action
       
   101 
       
   102 def manifestmerge(ui, m1, m2, ma, overwrite, backwards, partial):
    56     """
   103     """
    57     Merge manifest m1 with m2 using ancestor ma and generate merge action list
   104     Merge manifest m1 with m2 using ancestor ma and generate merge action list
    58     """
   105     """
    59 
   106 
    60     action = []
   107     action = []
       
   108 
       
   109     # Filter manifests
       
   110     if partial:
       
   111         for f in m1.keys():
       
   112             if not partial(f): del m1[f]
       
   113         for f in m2.keys():
       
   114             if not partial(f): del m2[f]
    61 
   115 
    62     # Compare manifests
   116     # Compare manifests
    63     for f, n in m1.iteritems():
   117     for f, n in m1.iteritems():
    64         if f in m2:
   118         if f in m2:
    65             queued = 0
   119             queued = 0
   174 
   228 
   175     if not linear_path and not (overwrite or branchmerge):
   229     if not linear_path and not (overwrite or branchmerge):
   176         raise util.Abort(_("update spans branches, use 'hg merge' "
   230         raise util.Abort(_("update spans branches, use 'hg merge' "
   177                            "or 'hg update -C' to lose changes"))
   231                            "or 'hg update -C' to lose changes"))
   178 
   232 
   179     modified, added, removed, deleted, unknown = repo.status()[:5]
   233     status = repo.status()
       
   234     modified, added, removed, deleted, unknown = status[:5]
   180     if branchmerge and not forcemerge:
   235     if branchmerge and not forcemerge:
   181         if modified or added or removed:
   236         if modified or added or removed:
   182             raise util.Abort(_("outstanding uncommitted changes"))
   237             raise util.Abort(_("outstanding uncommitted changes"))
   183 
   238 
   184     m1 = repo.changectx(p1).manifest().copy()
   239     m1 = repo.changectx(p1).manifest().copy()
   185     m2 = repo.changectx(p2).manifest().copy()
   240     m2 = repo.changectx(p2).manifest().copy()
   186     ma = repo.changectx(pa).manifest()
   241     ma = repo.changectx(pa).manifest()
   187 
       
   188     if not force:
       
   189         for f in unknown:
       
   190             if f in m2:
       
   191                 if repo.file(f).cmp(m2[f], repo.wread(f)):
       
   192                     raise util.Abort(_("'%s' already exists in the working"
       
   193                                        " dir and differs from remote") % f)
       
   194 
   242 
   195     # resolve the manifest to determine which files
   243     # resolve the manifest to determine which files
   196     # we care about merging
   244     # we care about merging
   197     repo.ui.note(_("resolving manifests\n"))
   245     repo.ui.note(_("resolving manifests\n"))
   198     repo.ui.debug(_(" overwrite %s branchmerge %s partial %s linear %s\n") %
   246     repo.ui.debug(_(" overwrite %s branchmerge %s partial %s linear %s\n") %
   199                   (overwrite, branchmerge, bool(partial), linear_path))
   247                   (overwrite, branchmerge, bool(partial), linear_path))
   200     repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
   248     repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
   201                   (short(p1), short(p2), short(pa)))
   249                   (short(p1), short(p2), short(pa)))
   202 
   250 
   203     action = []
   251     action = []
   204 
   252     m1 = workingmanifest(repo, m1, status)
   205     # update m1 from working dir
   253 
   206     for i,l in (("a", added), ("m", modified), ("u", unknown)):
   254     if not force:
   207         for f in l:
   255         checkunknown(repo, m2, status)
   208             m1[f] = m1.get(f, nullid) + i
   256     if linear_path:
   209             m1.set(f, util.is_exec(repo.wjoin(f), m1.execf(f)))
   257         action += forgetremoved(m2, status)
   210 
   258     action += manifestmerge(repo.ui, m1, m2, ma, overwrite, backwards, partial)
   211     for f in deleted + removed:
       
   212         del m1[f]
       
   213 
       
   214         # If we're jumping between revisions (as opposed to merging),
       
   215         # and if neither the working directory nor the target rev has
       
   216         # the file, then we need to remove it from the dirstate, to
       
   217         # prevent the dirstate from listing the file when it is no
       
   218         # longer in the manifest.
       
   219         if linear_path and f not in m2:
       
   220             action.append((f, "f"))
       
   221 
       
   222     if partial:
       
   223         for f in m1.keys():
       
   224             if not partial(f): del m1[f]
       
   225         for f in m2.keys():
       
   226             if not partial(f): del m2[f]
       
   227 
       
   228     action += manifestmerge(repo.ui, m1, m2, ma, overwrite, backwards)
       
   229     del m1, m2, ma
   259     del m1, m2, ma
   230 
   260 
   231     ### apply phase
   261     ### apply phase
   232 
   262 
   233     if linear_path or overwrite:
   263     if linear_path or overwrite: