Show More
@@ -153,6 +153,14 b' def parents(ui, repo, node = None):' | |||||
153 | if n != hg.nullid: |
|
153 | if n != hg.nullid: | |
154 | ui.write("%d:%s\n" % (repo.changelog.rev(n), hg.hex(n))) |
|
154 | ui.write("%d:%s\n" % (repo.changelog.rev(n), hg.hex(n))) | |
155 |
|
155 | |||
|
156 | def resolve(ui, repo, node = None): | |||
|
157 | '''merge a given node or the current tip into the working dir''' | |||
|
158 | if not node: | |||
|
159 | node = repo.changelog.tip() | |||
|
160 | else: | |||
|
161 | node = repo.lookup(node) | |||
|
162 | repo.resolve(node) | |||
|
163 | ||||
156 | def status(ui, repo): |
|
164 | def status(ui, repo): | |
157 | '''show changed files in the working directory |
|
165 | '''show changed files in the working directory | |
158 |
|
166 | |||
@@ -184,6 +192,7 b' table = {' | |||||
184 | ('c', 'changeset', None, 'show changeset')], |
|
192 | ('c', 'changeset', None, 'show changeset')], | |
185 | 'hg annotate [-u] [-c] [-n] [-r id] [files]'), |
|
193 | 'hg annotate [-u] [-c] [-n] [-r id] [files]'), | |
186 | "parents": (parents, [], 'hg parents [node]'), |
|
194 | "parents": (parents, [], 'hg parents [node]'), | |
|
195 | "resolve": (resolve, [], 'hg resolve [node]'), | |||
187 | "status": (status, [], 'hg status'), |
|
196 | "status": (status, [], 'hg status'), | |
188 | "undo": (undo, [], 'hg undo'), |
|
197 | "undo": (undo, [], 'hg undo'), | |
189 | } |
|
198 | } |
@@ -858,174 +858,110 b' class localrepository:' | |||||
858 | tr.close() |
|
858 | tr.close() | |
859 | return |
|
859 | return | |
860 |
|
860 | |||
861 |
def |
|
861 | def resolve(self, node): | |
862 | changesets = files = revisions = 0 |
|
862 | pl = self.dirstate.parents() | |
863 |
|
863 | if pl[1] != nullid: | ||
864 | self.lock() |
|
864 | self.ui.warn("last merge not committed") | |
865 | class genread: |
|
|||
866 | def __init__(self, generator): |
|
|||
867 | self.g = generator |
|
|||
868 | self.buf = "" |
|
|||
869 | def read(self, l): |
|
|||
870 | while l > len(self.buf): |
|
|||
871 | try: |
|
|||
872 | self.buf += self.g.next() |
|
|||
873 | except StopIteration: |
|
|||
874 | break |
|
|||
875 | d, self.buf = self.buf[:l], self.buf[l:] |
|
|||
876 | return d |
|
|||
877 |
|
||||
878 | if not generator: return |
|
|||
879 | source = genread(generator) |
|
|||
880 |
|
||||
881 | def getchunk(): |
|
|||
882 | d = source.read(4) |
|
|||
883 | if not d: return "" |
|
|||
884 | l = struct.unpack(">l", d)[0] |
|
|||
885 | if l <= 4: return "" |
|
|||
886 | return source.read(l - 4) |
|
|||
887 |
|
||||
888 | def getgroup(): |
|
|||
889 | while 1: |
|
|||
890 | c = getchunk() |
|
|||
891 | if not c: break |
|
|||
892 | yield c |
|
|||
893 |
|
||||
894 | tr = self.transaction() |
|
|||
895 | simple = True |
|
|||
896 | need = {} |
|
|||
897 |
|
||||
898 | self.ui.status("adding changesets\n") |
|
|||
899 | # pull off the changeset group |
|
|||
900 | def report(x): |
|
|||
901 | self.ui.debug("add changeset %s\n" % short(x)) |
|
|||
902 | return self.changelog.count() |
|
|||
903 |
|
||||
904 | co = self.changelog.tip() |
|
|||
905 | cn = self.changelog.addgroup(getgroup(), report, tr) |
|
|||
906 |
|
||||
907 | changesets = self.changelog.rev(cn) - self.changelog.rev(co) |
|
|||
908 |
|
||||
909 | self.ui.status("adding manifests\n") |
|
|||
910 | # pull off the manifest group |
|
|||
911 | mm = self.manifest.tip() |
|
|||
912 | mo = self.manifest.addgroup(getgroup(), |
|
|||
913 | lambda x: self.changelog.rev(x), tr) |
|
|||
914 |
|
||||
915 | # do we need a resolve? |
|
|||
916 | if self.changelog.ancestor(co, cn) != co: |
|
|||
917 | simple = False |
|
|||
918 | resolverev = self.changelog.count() |
|
|||
919 |
|
||||
920 | # resolve the manifest to determine which files |
|
|||
921 | # we care about merging |
|
|||
922 | self.ui.status("resolving manifests\n") |
|
|||
923 | ma = self.manifest.ancestor(mm, mo) |
|
|||
924 | omap = self.manifest.read(mo) # other |
|
|||
925 | amap = self.manifest.read(ma) # ancestor |
|
|||
926 | mmap = self.manifest.read(mm) # mine |
|
|||
927 | nmap = {} |
|
|||
928 |
|
||||
929 | self.ui.debug(" ancestor %s local %s remote %s\n" % |
|
|||
930 | (short(ma), short(mm), short(mo))) |
|
|||
931 |
|
||||
932 | for f, mid in mmap.iteritems(): |
|
|||
933 | if f in omap: |
|
|||
934 | if mid != omap[f]: |
|
|||
935 | self.ui.debug(" %s versions differ, do resolve\n" % f) |
|
|||
936 | need[f] = mid # use merged version or local version |
|
|||
937 | else: |
|
|||
938 | nmap[f] = mid # keep ours |
|
|||
939 | del omap[f] |
|
|||
940 | elif f in amap: |
|
|||
941 | if mid != amap[f]: |
|
|||
942 | r = self.ui.prompt( |
|
|||
943 | (" local changed %s which remote deleted\n" % f) + |
|
|||
944 | "(k)eep or (d)elete?", "[kd]", "k") |
|
|||
945 | if r == "k": nmap[f] = mid |
|
|||
946 | else: |
|
|||
947 | self.ui.debug("other deleted %s\n" % f) |
|
|||
948 | pass # other deleted it |
|
|||
949 | else: |
|
|||
950 | self.ui.debug("local created %s\n" %f) |
|
|||
951 | nmap[f] = mid # we created it |
|
|||
952 |
|
||||
953 | del mmap |
|
|||
954 |
|
||||
955 | for f, oid in omap.iteritems(): |
|
|||
956 | if f in amap: |
|
|||
957 | if oid != amap[f]: |
|
|||
958 | r = self.ui.prompt( |
|
|||
959 | ("remote changed %s which local deleted\n" % f) + |
|
|||
960 | "(k)eep or (d)elete?", "[kd]", "k") |
|
|||
961 | if r == "k": nmap[f] = oid |
|
|||
962 | else: |
|
|||
963 | pass # probably safe |
|
|||
964 | else: |
|
|||
965 | self.ui.debug("remote created %s, do resolve\n" % f) |
|
|||
966 | need[f] = oid |
|
|||
967 |
|
||||
968 | del omap |
|
|||
969 | del amap |
|
|||
970 |
|
||||
971 | new = need.keys() |
|
|||
972 | new.sort() |
|
|||
973 |
|
||||
974 | # process the files |
|
|||
975 | self.ui.status("adding files\n") |
|
|||
976 | while 1: |
|
|||
977 | f = getchunk() |
|
|||
978 | if not f: break |
|
|||
979 | self.ui.debug("adding %s revisions\n" % f) |
|
|||
980 | fl = self.file(f) |
|
|||
981 | o = fl.tip() |
|
|||
982 | n = fl.addgroup(getgroup(), lambda x: self.changelog.rev(x), tr) |
|
|||
983 | revisions += fl.rev(n) - fl.rev(o) |
|
|||
984 | files += 1 |
|
|||
985 | if f in need: |
|
|||
986 | del need[f] |
|
|||
987 | # manifest resolve determined we need to merge the tips |
|
|||
988 | nmap[f] = self.merge3(fl, f, o, n, tr, resolverev) |
|
|||
989 |
|
||||
990 | if need: |
|
|||
991 | # we need to do trivial merges on local files |
|
|||
992 | for f in new: |
|
|||
993 | if f not in need: continue |
|
|||
994 | fl = self.file(f) |
|
|||
995 | nmap[f] = self.merge3(fl, f, need[f], fl.tip(), tr, resolverev) |
|
|||
996 | revisions += 1 |
|
|||
997 |
|
||||
998 | # For simple merges, we don't need to resolve manifests or changesets |
|
|||
999 | if simple: |
|
|||
1000 | self.ui.debug("simple merge, skipping resolve\n") |
|
|||
1001 | self.ui.status(("modified %d files, added %d changesets" + |
|
|||
1002 | " and %d new revisions\n") |
|
|||
1003 | % (files, changesets, revisions)) |
|
|||
1004 | tr.close() |
|
|||
1005 | return |
|
865 | return | |
1006 |
|
866 | |||
1007 | node = self.manifest.add(nmap, tr, resolverev, mm, mo) |
|
867 | p1, p2 = pl[0], node | |
1008 | revisions += 1 |
|
868 | m1n = self.changelog.read(p1)[0] | |
|
869 | m2n = self.changelog.read(p2)[0] | |||
|
870 | man = self.manifest.ancestor(m1n, m2n) | |||
|
871 | m1 = self.manifest.read(m1n) | |||
|
872 | m2 = self.manifest.read(m2n) | |||
|
873 | ma = self.manifest.read(man) | |||
|
874 | ||||
|
875 | (c, a, d, u) = self.diffdir(self.root) | |||
|
876 | ||||
|
877 | # resolve the manifest to determine which files | |||
|
878 | # we care about merging | |||
|
879 | self.ui.status("resolving manifests\n") | |||
|
880 | self.ui.debug(" ancestor %s local %s remote %s\n" % | |||
|
881 | (short(man), short(m1n), short(m2n))) | |||
|
882 | ||||
|
883 | merge = {} | |||
|
884 | get = {} | |||
|
885 | remove = [] | |||
1009 |
|
886 | |||
1010 | # Now all files and manifests are merged, we add the changed files |
|
887 | # construct a working dir manifest | |
1011 | # and manifest id to the changelog |
|
888 | mw = m1.copy() | |
1012 | self.ui.status("committing merge changeset\n") |
|
889 | for f in a + c: | |
1013 | if co == cn: cn = -1 |
|
890 | mw[f] = nullid | |
|
891 | for f in d: | |||
|
892 | del mw[f] | |||
|
893 | ||||
|
894 | for f, n in mw.iteritems(): | |||
|
895 | if f in m2: | |||
|
896 | if n != m2[f]: | |||
|
897 | self.ui.debug(" %s versions differ, do resolve\n" % f) | |||
|
898 | merge[f] = (m1.get(f, nullid), m2[f]) | |||
|
899 | del m2[f] | |||
|
900 | elif f in ma: | |||
|
901 | if n != ma[f]: | |||
|
902 | r = self.ui.prompt( | |||
|
903 | (" local changed %s which remote deleted\n" % f) + | |||
|
904 | "(k)eep or (d)elete?", "[kd]", "k") | |||
|
905 | if r == "d": | |||
|
906 | remove.append(f) | |||
|
907 | else: | |||
|
908 | self.ui.debug("other deleted %s\n" % f) | |||
|
909 | pass # other deleted it | |||
|
910 | else: | |||
|
911 | self.ui.debug("local created %s\n" %f) | |||
1014 |
|
912 | |||
1015 | edittext = "\nHG: merge resolve\n" + \ |
|
913 | for f, n in m2.iteritems(): | |
1016 | "HG: manifest hash %s\n" % hex(node) + \ |
|
914 | if f in ma: | |
1017 | "".join(["HG: changed %s\n" % f for f in new]) |
|
915 | if n != ma[f]: | |
1018 | edittext = self.ui.edit(edittext) |
|
916 | r = self.ui.prompt( | |
1019 | n = self.changelog.add(node, new, edittext, tr, co, cn) |
|
917 | ("remote changed %s which local deleted\n" % f) + | |
1020 | revisions += 1 |
|
918 | "(k)eep or (d)elete?", "[kd]", "k") | |
|
919 | if r == "d": remove.append(f) | |||
|
920 | else: | |||
|
921 | pass # probably safe | |||
|
922 | else: | |||
|
923 | self.ui.debug("remote created %s, do resolve\n" % f) | |||
|
924 | get[f] = n | |||
|
925 | ||||
|
926 | del mw, m1, m2, ma | |||
|
927 | ||||
|
928 | self.dirstate.setparents(p1, p2) | |||
1021 |
|
929 | |||
1022 | self.ui.status("added %d changesets, %d files, and %d new revisions\n" |
|
930 | # get the files we don't need to change | |
1023 | % (changesets, files, revisions)) |
|
931 | files = get.keys() | |
|
932 | files.sort() | |||
|
933 | for f in files: | |||
|
934 | if f[0] == "/": continue | |||
|
935 | self.ui.note(f, "\n") | |||
|
936 | t = self.file(f).revision(get[f]) | |||
|
937 | try: | |||
|
938 | file(f, "w").write(t) | |||
|
939 | except IOError: | |||
|
940 | os.makedirs(os.path.dirname(f)) | |||
|
941 | file(f, "w").write(t) | |||
|
942 | ||||
|
943 | # we have to remember what files we needed to get/change | |||
|
944 | # because any file that's different from either one of its | |||
|
945 | # parents must be in the changeset | |||
|
946 | self.dirstate.update(files, 'm') | |||
1024 |
|
947 | |||
1025 | tr.close() |
|
948 | # merge the tricky bits | |
|
949 | files = merge.keys() | |||
|
950 | files.sort() | |||
|
951 | for f in files: | |||
|
952 | m, o = merge[f] | |||
|
953 | self.merge3(f, m, o) | |||
1026 |
|
954 | |||
1027 | def merge3(self, fl, fn, my, other, transaction, link): |
|
955 | # same here | |
1028 | """perform a 3-way merge and append the result""" |
|
956 | self.dirstate.update(files, 'm') | |
|
957 | ||||
|
958 | for f in remove: | |||
|
959 | self.ui.note("removing %s\n" % f) | |||
|
960 | #os.unlink(f) | |||
|
961 | self.dirstate.update(remove, 'r') | |||
|
962 | ||||
|
963 | def merge3(self, fn, my, other): | |||
|
964 | """perform a 3-way merge in the working directory""" | |||
1029 |
|
965 | |||
1030 | def temp(prefix, node): |
|
966 | def temp(prefix, node): | |
1031 | pre = "%s~%s." % (os.path.basename(fn), prefix) |
|
967 | pre = "%s~%s." % (os.path.basename(fn), prefix) | |
@@ -1035,30 +971,23 b' class localrepository:' | |||||
1035 | f.close() |
|
971 | f.close() | |
1036 | return name |
|
972 | return name | |
1037 |
|
973 | |||
|
974 | fl = self.file(fn) | |||
1038 | base = fl.ancestor(my, other) |
|
975 | base = fl.ancestor(my, other) | |
1039 | self.ui.note("resolving %s\n" % fn) |
|
976 | a = fn | |
1040 | self.ui.debug("local %s remote %s ancestor %s\n" % |
|
977 | b = temp("other", other) | |
1041 | (short(my), short(other), short(base))) |
|
978 | c = temp("base", base) | |
1042 |
|
||||
1043 | if my == base: |
|
|||
1044 | text = fl.revision(other) |
|
|||
1045 | else: |
|
|||
1046 | a = temp("local", my) |
|
|||
1047 | b = temp("remote", other) |
|
|||
1048 | c = temp("parent", base) |
|
|||
1049 |
|
979 | |||
1050 | cmd = os.environ["HGMERGE"] |
|
980 | self.ui.note("resolving %s\n" % fn) | |
1051 |
|
|
981 | self.ui.debug("file %s: other %s ancestor %s\n" % | |
1052 | r = os.system("%s %s %s %s %s" % (cmd, a, b, c, fn)) |
|
982 | (fn, short(other), short(base))) | |
1053 | if r: |
|
|||
1054 | raise "Merge failed!" |
|
|||
1055 |
|
983 | |||
1056 | text = open(a).read() |
|
984 | cmd = os.environ["HGMERGE"] | |
1057 | os.unlink(a) |
|
985 | r = os.system("%s %s %s %s %s" % (cmd, a, b, c, fn)) | |
1058 | os.unlink(b) |
|
986 | if r: | |
1059 | os.unlink(c) |
|
987 | self.ui.warn("merging %s failed!\n" % f) | |
1060 |
|
988 | |||
1061 | return fl.add(text, transaction, link, my, other) |
|
989 | os.unlink(b) | |
|
990 | os.unlink(c) | |||
1062 |
|
991 | |||
1063 | class remoterepository: |
|
992 | class remoterepository: | |
1064 | def __init__(self, ui, path): |
|
993 | def __init__(self, ui, path): |
General Comments 0
You need to be logged in to leave comments.
Login now