# HG changeset patch # User mpm@selenic.com # Date 1116995435 28800 # Node ID 4a828422247de16207fd1df3df6e87fae64ecf37 # Parent fbce9fc531d207e326be5f55940c5f1f7cc95291 Handle merge with deletions If you merge with a repo that has deleted a file after editing it, hg attempted to resolve the file. This (correctly) resulted in hg verify errors because the resolved version didn't show up in the manifests. This moves the manifest resolution before file resolution and decides which files to resolve based on the (partially) resolved manifest. After files are resolved, the final manifest is committed. diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -612,6 +612,7 @@ class localrepository: tr = self.transaction() simple = True + need = {} self.ui.status("adding changesets\n") # pull off the changeset group @@ -634,9 +635,62 @@ class localrepository: simple = False resolverev = self.changelog.count() + # resolve the manifest to determine which files + # we care about merging + self.ui.status("resolving manifests\n") + ma = self.manifest.ancestor(mm, mo) + omap = self.manifest.read(mo) # other + amap = self.manifest.read(ma) # ancestor + mmap = self.manifest.read(mm) # mine + nmap = {} + + self.ui.debug(" ancestor %s local %s remote %s\n" % + (short(ma), short(mm), short(mo))) + + for f, mid in mmap.iteritems(): + if f in omap: + if mid != omap[f]: + self.ui.debug(" %s versions differ, do resolve\n" % f) + need[f] = mid # use merged version or local version + else: + nmap[f] = mid # keep ours + del omap[f] + elif f in amap: + if mid != amap[f]: + r = self.ui.prompt( + (" local changed %s which remote deleted\n" % f) + + "(k)eep or (d)elete?", "[kd]", "k") + if r == "k": nmap[f] = mid + else: + self.ui.debug("other deleted %s\n" % f) + pass # other deleted it + else: + self.ui.debug("local created %s\n" %f) + nmap[f] = mid # we created it + + del mmap + + for f, oid in omap.iteritems(): + if f in amap: + if oid != amap[f]: + r = self.ui.prompt( + ("remote changed %s which local deleted\n" % f) + + "(k)eep or (d)elete?", "[kd]", "k") + if r == "k": nmap[f] = oid + else: + pass # probably safe + else: + self.ui.debug("remote created %s, do resolve\n" % f) + need[f] = oid + + del omap + del amap + + new = need.keys() + new.sort() + # process the files self.ui.status("adding files\n") - new = {} while 1: f = getchunk(4) if not f: break @@ -645,11 +699,17 @@ class localrepository: fl = self.file(f) o = fl.tip() n = fl.addgroup(fg, lambda x: self.changelog.rev(x), tr) - if not simple: - if o == n: continue - # this file has changed between branches, so it must be - # represented in the merge changeset - new[f] = self.merge3(fl, f, o, n, tr, resolverev) + if f in need: + del need[f] + # manifest resolve determined we need to merge the tips + nmap[f] = self.merge3(fl, f, o, n, tr, resolverev) + + if need: + # we need to do trivial merges on local files + for f in new: + if f not in need: continue + fl = self.file(f) + nmap[f] = self.merge3(fl, f, need[f], fl.tip(), tr, resolverev) # For simple merges, we don't need to resolve manifests or changesets if simple: @@ -657,64 +717,11 @@ class localrepository: tr.close() return - # resolve the manifest to point to all the merged files - self.ui.status("resolving manifests\n") - ma = self.manifest.ancestor(mm, mo) - omap = self.manifest.read(mo) # other - amap = self.manifest.read(ma) # ancestor - mmap = self.manifest.read(mm) # mine - self.ui.debug("ancestor %s local %s remote %s\n" % - (short(ma), short(mm), short(mo))) - nmap = {} - - for f, mid in mmap.iteritems(): - if f in omap: - if mid != omap[f]: - self.ui.debug("%s versions differ\n" % f) - if f in new: self.ui.debug("%s updated in resolve\n" % f) - # use merged version or local version - nmap[f] = new.get(f, mid) - else: - nmap[f] = mid # keep ours - del omap[f] - elif f in amap: - if mid != amap[f]: - r = self.ui.prompt( - ("local changed %s which remote deleted\n" % f) + - "(k)eep or (d)elete?", "[kd]", "k") - if r == "k": nmap[f] = mid - else: - self.ui.debug("other deleted %s\n" % f) - pass # other deleted it - else: - self.ui.debug("local created %s\n" %f) - nmap[f] = mid # we created it - - del mmap - - for f, oid in omap.iteritems(): - if f in amap: - if oid != amap[f]: - r = self.ui.prompt( - ("remote changed %s which local deleted\n" % f) + - "(k)eep or (d)elete?", "[kd]", "k") - if r == "k": nmap[f] = oid - else: - pass # probably safe - else: - self.ui.debug("remote created %s\n" % f) - nmap[f] = new.get(f, oid) # remote created it - - del omap - del amap - node = self.manifest.add(nmap, tr, resolverev, mm, mo) # Now all files and manifests are merged, we add the changed files # and manifest id to the changelog self.ui.status("committing merge changeset\n") - new = new.keys() - new.sort() if co == cn: cn = -1 edittext = "\nHG: merge resolve\n" + \