Show More
@@ -153,6 +153,14 b' def parents(ui, repo, node = None):' | |||
|
153 | 153 | if n != hg.nullid: |
|
154 | 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 | 164 | def status(ui, repo): |
|
157 | 165 | '''show changed files in the working directory |
|
158 | 166 | |
@@ -184,6 +192,7 b' table = {' | |||
|
184 | 192 | ('c', 'changeset', None, 'show changeset')], |
|
185 | 193 | 'hg annotate [-u] [-c] [-n] [-r id] [files]'), |
|
186 | 194 | "parents": (parents, [], 'hg parents [node]'), |
|
195 | "resolve": (resolve, [], 'hg resolve [node]'), | |
|
187 | 196 | "status": (status, [], 'hg status'), |
|
188 | 197 | "undo": (undo, [], 'hg undo'), |
|
189 | 198 | } |
@@ -858,174 +858,110 b' class localrepository:' | |||
|
858 | 858 | tr.close() |
|
859 | 859 | return |
|
860 | 860 | |
|
861 |
def |
|
|
862 | changesets = files = revisions = 0 | |
|
863 | ||
|
864 | self.lock() | |
|
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() | |
|
861 | def resolve(self, node): | |
|
862 | pl = self.dirstate.parents() | |
|
863 | if pl[1] != nullid: | |
|
864 | self.ui.warn("last merge not committed") | |
|
1005 | 865 | return |
|
1006 | 866 | |
|
1007 | node = self.manifest.add(nmap, tr, resolverev, mm, mo) | |
|
1008 | revisions += 1 | |
|
867 | p1, p2 = pl[0], node | |
|
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 | |
|
1011 | # and manifest id to the changelog | |
|
1012 | self.ui.status("committing merge changeset\n") | |
|
1013 | if co == cn: cn = -1 | |
|
887 | # construct a working dir manifest | |
|
888 | mw = m1.copy() | |
|
889 | for f in a + c: | |
|
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" + \ | |
|
1016 | "HG: manifest hash %s\n" % hex(node) + \ | |
|
1017 | "".join(["HG: changed %s\n" % f for f in new]) | |
|
1018 | edittext = self.ui.edit(edittext) | |
|
1019 | n = self.changelog.add(node, new, edittext, tr, co, cn) | |
|
1020 | revisions += 1 | |
|
913 | for f, n in m2.iteritems(): | |
|
914 | if f in ma: | |
|
915 | if n != ma[f]: | |
|
916 | r = self.ui.prompt( | |
|
917 | ("remote changed %s which local deleted\n" % f) + | |
|
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" | |
|
1023 | % (changesets, files, revisions)) | |
|
930 | # get the files we don't need to change | |
|
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): | |
|
1028 | """perform a 3-way merge and append the result""" | |
|
955 | # same here | |
|
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 | 966 | def temp(prefix, node): |
|
1031 | 967 | pre = "%s~%s." % (os.path.basename(fn), prefix) |
@@ -1035,30 +971,23 b' class localrepository:' | |||
|
1035 | 971 | f.close() |
|
1036 | 972 | return name |
|
1037 | 973 | |
|
974 | fl = self.file(fn) | |
|
1038 | 975 | base = fl.ancestor(my, other) |
|
1039 | self.ui.note("resolving %s\n" % fn) | |
|
1040 | self.ui.debug("local %s remote %s ancestor %s\n" % | |
|
1041 | (short(my), short(other), short(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) | |
|
976 | a = fn | |
|
977 | b = temp("other", other) | |
|
978 | c = temp("base", base) | |
|
1049 | 979 | |
|
1050 | cmd = os.environ["HGMERGE"] | |
|
1051 |
|
|
|
1052 | r = os.system("%s %s %s %s %s" % (cmd, a, b, c, fn)) | |
|
1053 | if r: | |
|
1054 | raise "Merge failed!" | |
|
980 | self.ui.note("resolving %s\n" % fn) | |
|
981 | self.ui.debug("file %s: other %s ancestor %s\n" % | |
|
982 | (fn, short(other), short(base))) | |
|
1055 | 983 | |
|
1056 | text = open(a).read() | |
|
1057 | os.unlink(a) | |
|
1058 | os.unlink(b) | |
|
1059 | os.unlink(c) | |
|
1060 | ||
|
1061 | return fl.add(text, transaction, link, my, other) | |
|
984 | cmd = os.environ["HGMERGE"] | |
|
985 | r = os.system("%s %s %s %s %s" % (cmd, a, b, c, fn)) | |
|
986 | if r: | |
|
987 | self.ui.warn("merging %s failed!\n" % f) | |
|
988 | ||
|
989 | os.unlink(b) | |
|
990 | os.unlink(c) | |
|
1062 | 991 | |
|
1063 | 992 | class remoterepository: |
|
1064 | 993 | def __init__(self, ui, path): |
General Comments 0
You need to be logged in to leave comments.
Login now