325 |
325 |
326 def lookup(self, key): |
326 def lookup(self, key): |
327 if self.tags is None: |
327 if self.tags is None: |
328 self.tags = {} |
328 self.tags = {} |
329 try: |
329 try: |
|
330 # read each head of the tags file, ending with the tip |
|
331 # and add each tag found to the map, with "newer" ones |
|
332 # taking precedence |
330 fl = self.file(".hgtags") |
333 fl = self.file(".hgtags") |
331 for l in fl.revision(fl.tip()).splitlines(): |
334 h = fl.heads() |
332 if l: |
335 h.reverse() |
333 n, k = l.split(" ") |
336 for r in h: |
334 self.tags[k] = bin(n) |
337 for l in fl.revision(r).splitlines(): |
|
338 if l: |
|
339 n, k = l.split(" ") |
|
340 self.tags[k] = bin(n) |
335 except KeyError: pass |
341 except KeyError: pass |
336 try: |
342 try: |
337 return self.tags[key] |
343 return self.tags[key] |
338 except KeyError: |
344 except KeyError: |
339 return self.changelog.lookup(key) |
345 return self.changelog.lookup(key) |
473 tr.close() |
479 tr.close() |
474 |
480 |
475 self.dirstate.setparents(n) |
481 self.dirstate.setparents(n) |
476 self.dirstate.update(new, "n") |
482 self.dirstate.update(new, "n") |
477 self.dirstate.forget(remove) |
483 self.dirstate.forget(remove) |
478 |
|
479 def checkout(self, node): |
|
480 # checkout is really dumb at the moment |
|
481 # it ought to basically merge |
|
482 change = self.changelog.read(node) |
|
483 l = self.manifest.read(change[0]).items() |
|
484 l.sort() |
|
485 |
|
486 for f,n in l: |
|
487 if f[0] == "/": continue |
|
488 self.ui.note(f, "\n") |
|
489 t = self.file(f).revision(n) |
|
490 try: |
|
491 file(self.wjoin(f), "w").write(t) |
|
492 except IOError: |
|
493 os.makedirs(os.path.dirname(f)) |
|
494 file(self.wjoin(f), "w").write(t) |
|
495 |
|
496 self.dirstate.setparents(node) |
|
497 self.dirstate.clear() |
|
498 self.dirstate.update([f for f,n in l], "n") |
|
499 |
484 |
500 def diffdir(self, path, changeset = None): |
485 def diffdir(self, path, changeset = None): |
501 changed = [] |
486 changed = [] |
502 added = [] |
487 added = [] |
503 unknown = [] |
488 unknown = [] |
846 % (files, changesets, revisions)) |
831 % (files, changesets, revisions)) |
847 |
832 |
848 tr.close() |
833 tr.close() |
849 return |
834 return |
850 |
835 |
851 def resolve(self, node): |
836 def update(self, node): |
852 pl = self.dirstate.parents() |
837 pl = self.dirstate.parents() |
853 if pl[1] != nullid: |
838 if pl[1] != nullid: |
854 self.ui.warn("last merge not committed") |
839 self.ui.warn("aborting: outstanding uncommitted merges\n") |
855 return |
840 return |
856 |
841 |
857 p1, p2 = pl[0], node |
842 p1, p2 = pl[0], node |
858 m1n = self.changelog.read(p1)[0] |
843 m1n = self.changelog.read(p1)[0] |
859 m2n = self.changelog.read(p2)[0] |
844 m2n = self.changelog.read(p2)[0] |
864 |
849 |
865 (c, a, d, u) = self.diffdir(self.root) |
850 (c, a, d, u) = self.diffdir(self.root) |
866 |
851 |
867 # resolve the manifest to determine which files |
852 # resolve the manifest to determine which files |
868 # we care about merging |
853 # we care about merging |
869 self.ui.status("resolving manifests\n") |
854 self.ui.note("resolving manifests\n") |
870 self.ui.debug(" ancestor %s local %s remote %s\n" % |
855 self.ui.debug(" ancestor %s local %s remote %s\n" % |
871 (short(man), short(m1n), short(m2n))) |
856 (short(man), short(m1n), short(m2n))) |
872 |
857 |
873 merge = {} |
858 merge = {} |
874 get = {} |
859 get = {} |
875 remove = [] |
860 remove = [] |
876 |
861 |
877 # construct a working dir manifest |
862 # construct a working dir manifest |
878 mw = m1.copy() |
863 mw = m1.copy() |
879 for f in a + c: |
864 for f in a + c + u: |
880 mw[f] = nullid |
865 mw[f] = "" |
881 for f in d: |
866 for f in d: |
882 del mw[f] |
867 if f in mw: del mw[f] |
883 |
868 |
884 for f, n in mw.iteritems(): |
869 for f, n in mw.iteritems(): |
885 if f in m2: |
870 if f in m2: |
886 if n != m2[f]: |
871 if n != m2[f]: |
887 self.ui.debug(" %s versions differ, do resolve\n" % f) |
872 a = ma.get(f, nullid) |
888 merge[f] = (m1.get(f, nullid), m2[f]) |
873 if n != a and m2[f] != a: |
|
874 self.ui.debug(" %s versions differ, do resolve\n" % f) |
|
875 merge[f] = (m1.get(f, nullid), m2[f]) |
|
876 else: |
|
877 get[f] = m2[f] |
889 del m2[f] |
878 del m2[f] |
890 elif f in ma: |
879 elif f in ma: |
891 if n != ma[f]: |
880 if n != ma[f]: |
892 r = self.ui.prompt( |
881 r = self.ui.prompt( |
893 (" local changed %s which remote deleted\n" % f) + |
882 (" local changed %s which remote deleted\n" % f) + |
894 "(k)eep or (d)elete?", "[kd]", "k") |
883 "(k)eep or (d)elete?", "[kd]", "k") |
895 if r == "d": |
884 if r == "d": |
896 remove.append(f) |
885 remove.append(f) |
897 else: |
886 else: |
898 self.ui.debug("other deleted %s\n" % f) |
887 self.ui.debug("other deleted %s\n" % f) |
899 pass # other deleted it |
888 remove.append(f) # other deleted it |
900 else: |
889 else: |
901 self.ui.debug("local created %s\n" %f) |
890 if n == m1.get(f, nullid): # same as parent |
|
891 self.ui.debug("remote deleted %s\n" % f) |
|
892 remove.append(f) |
|
893 else: |
|
894 self.ui.debug("working dir created %s, keeping\n" % f) |
902 |
895 |
903 for f, n in m2.iteritems(): |
896 for f, n in m2.iteritems(): |
904 if f in ma: |
897 if f in ma and n != ma[f]: |
905 if n != ma[f]: |
|
906 r = self.ui.prompt( |
898 r = self.ui.prompt( |
907 ("remote changed %s which local deleted\n" % f) + |
899 ("remote changed %s which local deleted\n" % f) + |
908 "(k)eep or (d)elete?", "[kd]", "k") |
900 "(k)eep or (d)elete?", "[kd]", "k") |
909 if r == "d": remove.append(f) |
901 if r == "d": remove.append(f) |
910 else: |
|
911 pass # probably safe |
|
912 else: |
902 else: |
913 self.ui.debug("remote created %s, do resolve\n" % f) |
903 self.ui.debug("remote created %s\n" % f) |
914 get[f] = n |
904 get[f] = n |
915 |
905 |
916 del mw, m1, m2, ma |
906 del mw, m1, m2, ma |
|
907 |
|
908 if not merge: |
|
909 # we don't need to do any magic, just jump to the new rev |
|
910 mode = 'n' |
|
911 p1, p2 = p2, nullid |
|
912 else: |
|
913 # we have to remember what files we needed to get/change |
|
914 # because any file that's different from either one of its |
|
915 # parents must be in the changeset |
|
916 mode = 'm' |
917 |
917 |
918 self.dirstate.setparents(p1, p2) |
918 self.dirstate.setparents(p1, p2) |
919 |
919 |
920 # get the files we don't need to change |
920 # get the files we don't need to change |
921 files = get.keys() |
921 files = get.keys() |
927 try: |
927 try: |
928 file(self.wjoin(f), "w").write(t) |
928 file(self.wjoin(f), "w").write(t) |
929 except IOError: |
929 except IOError: |
930 os.makedirs(os.path.dirname(f)) |
930 os.makedirs(os.path.dirname(f)) |
931 file(self.wjoin(f), "w").write(t) |
931 file(self.wjoin(f), "w").write(t) |
932 |
932 self.dirstate.update([f], mode) |
933 # we have to remember what files we needed to get/change |
|
934 # because any file that's different from either one of its |
|
935 # parents must be in the changeset |
|
936 self.dirstate.update(files, 'm') |
|
937 |
933 |
938 # merge the tricky bits |
934 # merge the tricky bits |
939 files = merge.keys() |
935 files = merge.keys() |
940 files.sort() |
936 files.sort() |
941 for f in files: |
937 for f in files: |
|
938 self.status("mering %f\n" % f) |
942 m, o = merge[f] |
939 m, o = merge[f] |
943 self.merge3(f, m, o) |
940 self.merge3(f, m, o) |
944 |
941 self.dirstate.update([f], 'm') |
945 # same here |
|
946 self.dirstate.update(files, 'm') |
|
947 |
942 |
948 for f in remove: |
943 for f in remove: |
949 self.ui.note("removing %s\n" % f) |
944 self.ui.note("removing %s\n" % f) |
950 #os.unlink(f) |
945 os.unlink(f) |
951 self.dirstate.update(remove, 'r') |
946 if mode == 'n': |
|
947 self.dirstate.forget(remove) |
|
948 else: |
|
949 self.dirstate.update(remove, 'r') |
952 |
950 |
953 def merge3(self, fn, my, other): |
951 def merge3(self, fn, my, other): |
954 """perform a 3-way merge in the working directory""" |
952 """perform a 3-way merge in the working directory""" |
955 |
953 |
956 import tempfile |
954 import tempfile |