##// END OF EJS Templates
hg resolve: merge a given node into the working directory...
mpm@selenic.com -
r232:fc4a6e5b default
parent child Browse files
Show More
@@ -1,252 +1,261
1 import os, re, traceback, sys, signal, time
1 import os, re, traceback, sys, signal, time
2 from mercurial import fancyopts, ui, hg
2 from mercurial import fancyopts, ui, hg
3
3
4 class UnknownCommand(Exception): pass
4 class UnknownCommand(Exception): pass
5
5
6 def filterfiles(list, files):
6 def filterfiles(list, files):
7 l = [ x for x in list if x in files ]
7 l = [ x for x in list if x in files ]
8
8
9 for f in files:
9 for f in files:
10 if f[-1] != os.sep: f += os.sep
10 if f[-1] != os.sep: f += os.sep
11 l += [ x for x in list if x.startswith(f) ]
11 l += [ x for x in list if x.startswith(f) ]
12 return l
12 return l
13
13
14 def relfilter(repo, args):
14 def relfilter(repo, args):
15 if os.getcwd() != repo.root:
15 if os.getcwd() != repo.root:
16 p = os.getcwd()[len(repo.root) + 1: ]
16 p = os.getcwd()[len(repo.root) + 1: ]
17 return filterfiles(p, args)
17 return filterfiles(p, args)
18 return args
18 return args
19
19
20 def relpath(repo, args):
20 def relpath(repo, args):
21 if os.getcwd() != repo.root:
21 if os.getcwd() != repo.root:
22 p = os.getcwd()[len(repo.root) + 1: ]
22 p = os.getcwd()[len(repo.root) + 1: ]
23 return [ os.path.join(p, x) for x in args ]
23 return [ os.path.join(p, x) for x in args ]
24 return args
24 return args
25
25
26 def help(ui, cmd=None):
26 def help(ui, cmd=None):
27 '''show help'''
27 '''show help'''
28 if cmd:
28 if cmd:
29 try:
29 try:
30 i = find(cmd)
30 i = find(cmd)
31 ui.write("%s\n\n" % i[2])
31 ui.write("%s\n\n" % i[2])
32 ui.write(i[0].__doc__, "\n")
32 ui.write(i[0].__doc__, "\n")
33 except UnknownCommand:
33 except UnknownCommand:
34 ui.warn("unknown command %s", cmd)
34 ui.warn("unknown command %s", cmd)
35 sys.exit(0)
35 sys.exit(0)
36
36
37 ui.status("""\
37 ui.status("""\
38 hg commands:
38 hg commands:
39
39
40 add [files...] add the given files in the next commit
40 add [files...] add the given files in the next commit
41 addremove add all new files, delete all missing files
41 addremove add all new files, delete all missing files
42 annotate [files...] show changeset number per file line
42 annotate [files...] show changeset number per file line
43 branch <path> create a branch of <path> in this directory
43 branch <path> create a branch of <path> in this directory
44 checkout [changeset] checkout the latest or given changeset
44 checkout [changeset] checkout the latest or given changeset
45 commit commit all changes to the repository
45 commit commit all changes to the repository
46 diff [files...] diff working directory (or selected files)
46 diff [files...] diff working directory (or selected files)
47 dump <file> [rev] dump the latest or given revision of a file
47 dump <file> [rev] dump the latest or given revision of a file
48 dumpmanifest [rev] dump the latest or given revision of the manifest
48 dumpmanifest [rev] dump the latest or given revision of the manifest
49 export <rev> dump the changeset header and diffs for a revision
49 export <rev> dump the changeset header and diffs for a revision
50 history show changeset history
50 history show changeset history
51 init create a new repository in this directory
51 init create a new repository in this directory
52 log <file> show revision history of a single file
52 log <file> show revision history of a single file
53 merge <path> merge changes from <path> into local repository
53 merge <path> merge changes from <path> into local repository
54 recover rollback an interrupted transaction
54 recover rollback an interrupted transaction
55 remove [files...] remove the given files in the next commit
55 remove [files...] remove the given files in the next commit
56 serve export the repository via HTTP
56 serve export the repository via HTTP
57 status show new, missing, and changed files in working dir
57 status show new, missing, and changed files in working dir
58 tags show current changeset tags
58 tags show current changeset tags
59 undo undo the last transaction
59 undo undo the last transaction
60 """)
60 """)
61
61
62 def init(ui):
62 def init(ui):
63 """create a repository"""
63 """create a repository"""
64 hg.repository(ui, ".", create=1)
64 hg.repository(ui, ".", create=1)
65
65
66 def branch(ui, path):
66 def branch(ui, path):
67 '''branch from a local repository'''
67 '''branch from a local repository'''
68 # this should eventually support remote repos
68 # this should eventually support remote repos
69 os.system("cp -al %s/.hg .hg" % path)
69 os.system("cp -al %s/.hg .hg" % path)
70
70
71 def checkout(ui, repo, changeset=None):
71 def checkout(ui, repo, changeset=None):
72 '''checkout a given changeset or the current tip'''
72 '''checkout a given changeset or the current tip'''
73 (c, a, d, u) = repo.diffdir(repo.root)
73 (c, a, d, u) = repo.diffdir(repo.root)
74 if c or a or d:
74 if c or a or d:
75 ui.warn("aborting (outstanding changes in working directory)\n")
75 ui.warn("aborting (outstanding changes in working directory)\n")
76 sys.exit(1)
76 sys.exit(1)
77
77
78 node = repo.changelog.tip()
78 node = repo.changelog.tip()
79 if changeset:
79 if changeset:
80 node = repo.lookup(changeset)
80 node = repo.lookup(changeset)
81 repo.checkout(node)
81 repo.checkout(node)
82
82
83 def annotate(u, repo, *args, **ops):
83 def annotate(u, repo, *args, **ops):
84 def getnode(rev):
84 def getnode(rev):
85 return hg.short(repo.changelog.node(rev))
85 return hg.short(repo.changelog.node(rev))
86
86
87 def getname(rev):
87 def getname(rev):
88 try:
88 try:
89 return bcache[rev]
89 return bcache[rev]
90 except KeyError:
90 except KeyError:
91 cl = repo.changelog.read(repo.changelog.node(rev))
91 cl = repo.changelog.read(repo.changelog.node(rev))
92 name = cl[1]
92 name = cl[1]
93 f = name.find('@')
93 f = name.find('@')
94 if f >= 0:
94 if f >= 0:
95 name = name[:f]
95 name = name[:f]
96 bcache[rev] = name
96 bcache[rev] = name
97 return name
97 return name
98
98
99 bcache = {}
99 bcache = {}
100 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
100 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
101 if not ops['user'] and not ops['changeset']:
101 if not ops['user'] and not ops['changeset']:
102 ops['number'] = 1
102 ops['number'] = 1
103
103
104 args = relpath(repo, args)
104 args = relpath(repo, args)
105 node = repo.dirstate.parents()[0]
105 node = repo.dirstate.parents()[0]
106 if ops['revision']:
106 if ops['revision']:
107 node = repo.changelog.lookup(ops['revision'])
107 node = repo.changelog.lookup(ops['revision'])
108 change = repo.changelog.read(node)
108 change = repo.changelog.read(node)
109 mmap = repo.manifest.read(change[0])
109 mmap = repo.manifest.read(change[0])
110 maxuserlen = 0
110 maxuserlen = 0
111 maxchangelen = 0
111 maxchangelen = 0
112 for f in args:
112 for f in args:
113 lines = repo.file(f).annotate(mmap[f])
113 lines = repo.file(f).annotate(mmap[f])
114 pieces = []
114 pieces = []
115
115
116 for o, f in opmap:
116 for o, f in opmap:
117 if ops[o]:
117 if ops[o]:
118 l = [ f(n) for n,t in lines ]
118 l = [ f(n) for n,t in lines ]
119 m = max(map(len, l))
119 m = max(map(len, l))
120 pieces.append([ "%*s" % (m, x) for x in l])
120 pieces.append([ "%*s" % (m, x) for x in l])
121
121
122 for p,l in zip(zip(*pieces), lines):
122 for p,l in zip(zip(*pieces), lines):
123 u.write(" ".join(p) + ": " + l[1])
123 u.write(" ".join(p) + ": " + l[1])
124
124
125 def heads(ui, repo):
125 def heads(ui, repo):
126 '''show current repository heads'''
126 '''show current repository heads'''
127 for n in repo.changelog.heads():
127 for n in repo.changelog.heads():
128 i = repo.changelog.rev(n)
128 i = repo.changelog.rev(n)
129 changes = repo.changelog.read(n)
129 changes = repo.changelog.read(n)
130 (p1, p2) = repo.changelog.parents(n)
130 (p1, p2) = repo.changelog.parents(n)
131 (h, h1, h2) = map(hg.hex, (n, p1, p2))
131 (h, h1, h2) = map(hg.hex, (n, p1, p2))
132 (i1, i2) = map(repo.changelog.rev, (p1, p2))
132 (i1, i2) = map(repo.changelog.rev, (p1, p2))
133 print "rev: %4d:%s" % (i, h)
133 print "rev: %4d:%s" % (i, h)
134 print "parents: %4d:%s" % (i1, h1)
134 print "parents: %4d:%s" % (i1, h1)
135 if i2: print " %4d:%s" % (i2, h2)
135 if i2: print " %4d:%s" % (i2, h2)
136 print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]),
136 print "manifest: %4d:%s" % (repo.manifest.rev(changes[0]),
137 hg.hex(changes[0]))
137 hg.hex(changes[0]))
138 print "user:", changes[1]
138 print "user:", changes[1]
139 print "date:", time.asctime(
139 print "date:", time.asctime(
140 time.localtime(float(changes[2].split(' ')[0])))
140 time.localtime(float(changes[2].split(' ')[0])))
141 if ui.verbose: print "files:", " ".join(changes[3])
141 if ui.verbose: print "files:", " ".join(changes[3])
142 print "description:"
142 print "description:"
143 print changes[4]
143 print changes[4]
144
144
145 def parents(ui, repo, node = None):
145 def parents(ui, repo, node = None):
146 '''show the parents of the current working dir'''
146 '''show the parents of the current working dir'''
147 if node:
147 if node:
148 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
148 p = repo.changelog.parents(repo.lookup(hg.bin(node)))
149 else:
149 else:
150 p = repo.dirstate.parents()
150 p = repo.dirstate.parents()
151
151
152 for n in p:
152 for n in p:
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
159 C = changed
167 C = changed
160 A = added
168 A = added
161 R = removed
169 R = removed
162 ? = not tracked'''
170 ? = not tracked'''
163 (c, a, d, u) = repo.diffdir(repo.root)
171 (c, a, d, u) = repo.diffdir(repo.root)
164 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
172 (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u))
165
173
166 for f in c: print "C", f
174 for f in c: print "C", f
167 for f in a: print "A", f
175 for f in a: print "A", f
168 for f in d: print "R", f
176 for f in d: print "R", f
169 for f in u: print "?", f
177 for f in u: print "?", f
170
178
171 def undo(ui, repo):
179 def undo(ui, repo):
172 repo.undo()
180 repo.undo()
173
181
174 table = {
182 table = {
175 "init": (init, [], 'hg init'),
183 "init": (init, [], 'hg init'),
176 "branch|clone": (branch, [], 'hg branch [path]'),
184 "branch|clone": (branch, [], 'hg branch [path]'),
177 "heads": (heads, [], 'hg heads'),
185 "heads": (heads, [], 'hg heads'),
178 "help": (help, [], 'hg help [command]'),
186 "help": (help, [], 'hg help [command]'),
179 "checkout|co": (checkout, [], 'hg checkout [changeset]'),
187 "checkout|co": (checkout, [], 'hg checkout [changeset]'),
180 "ann|annotate": (annotate,
188 "ann|annotate": (annotate,
181 [('r', 'revision', '', 'revision'),
189 [('r', 'revision', '', 'revision'),
182 ('u', 'user', None, 'show user'),
190 ('u', 'user', None, 'show user'),
183 ('n', 'number', None, 'show revision number'),
191 ('n', 'number', None, 'show revision number'),
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 }
190
199
191 norepo = "init branch help"
200 norepo = "init branch help"
192
201
193 def find(cmd):
202 def find(cmd):
194 i = None
203 i = None
195 for e in table.keys():
204 for e in table.keys():
196 if re.match(e + "$", cmd):
205 if re.match(e + "$", cmd):
197 return table[e]
206 return table[e]
198
207
199 raise UnknownCommand(cmd)
208 raise UnknownCommand(cmd)
200
209
201 class SignalInterrupt(Exception): pass
210 class SignalInterrupt(Exception): pass
202
211
203 def catchterm(*args):
212 def catchterm(*args):
204 raise SignalInterrupt
213 raise SignalInterrupt
205
214
206 def dispatch(args):
215 def dispatch(args):
207 options = {}
216 options = {}
208 opts = [('v', 'verbose', None, 'verbose'),
217 opts = [('v', 'verbose', None, 'verbose'),
209 ('d', 'debug', None, 'debug'),
218 ('d', 'debug', None, 'debug'),
210 ('q', 'quiet', None, 'quiet'),
219 ('q', 'quiet', None, 'quiet'),
211 ('y', 'noninteractive', None, 'run non-interactively'),
220 ('y', 'noninteractive', None, 'run non-interactively'),
212 ]
221 ]
213
222
214 args = fancyopts.fancyopts(args, opts, options,
223 args = fancyopts.fancyopts(args, opts, options,
215 'hg [options] <command> [options] [files]')
224 'hg [options] <command> [options] [files]')
216
225
217 if not args:
226 if not args:
218 cmd = "help"
227 cmd = "help"
219 else:
228 else:
220 cmd, args = args[0], args[1:]
229 cmd, args = args[0], args[1:]
221
230
222 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
231 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
223 not options["noninteractive"])
232 not options["noninteractive"])
224
233
225 # deal with unfound commands later
234 # deal with unfound commands later
226 i = find(cmd)
235 i = find(cmd)
227
236
228 signal.signal(signal.SIGTERM, catchterm)
237 signal.signal(signal.SIGTERM, catchterm)
229
238
230 cmdoptions = {}
239 cmdoptions = {}
231 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
240 args = fancyopts.fancyopts(args, i[1], cmdoptions, i[2])
232
241
233 if cmd not in norepo.split():
242 if cmd not in norepo.split():
234 repo = hg.repository(ui = u)
243 repo = hg.repository(ui = u)
235 d = lambda: i[0](u, repo, *args, **cmdoptions)
244 d = lambda: i[0](u, repo, *args, **cmdoptions)
236 else:
245 else:
237 d = lambda: i[0](u, *args, **cmdoptions)
246 d = lambda: i[0](u, *args, **cmdoptions)
238
247
239 try:
248 try:
240 d()
249 d()
241 except SignalInterrupt:
250 except SignalInterrupt:
242 u.warn("killed!\n")
251 u.warn("killed!\n")
243 except KeyboardInterrupt:
252 except KeyboardInterrupt:
244 u.warn("interrupted!\n")
253 u.warn("interrupted!\n")
245 except TypeError, inst:
254 except TypeError, inst:
246 # was this an argument error?
255 # was this an argument error?
247 tb = traceback.extract_tb(sys.exc_info()[2])
256 tb = traceback.extract_tb(sys.exc_info()[2])
248 if len(tb) > 2: # no
257 if len(tb) > 2: # no
249 raise
258 raise
250 u.warn("%s: invalid arguments\n" % i[0].__name__)
259 u.warn("%s: invalid arguments\n" % i[0].__name__)
251 u.warn("syntax: %s\n" % i[2])
260 u.warn("syntax: %s\n" % i[2])
252 sys.exit(-1)
261 sys.exit(-1)
@@ -1,1142 +1,1071
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import sys, struct, sha, socket, os, time, re, urllib2, tempfile
8 import sys, struct, sha, socket, os, time, re, urllib2, tempfile
9 import urllib
9 import urllib
10 from mercurial import byterange, lock
10 from mercurial import byterange, lock
11 from mercurial.transaction import *
11 from mercurial.transaction import *
12 from mercurial.revlog import *
12 from mercurial.revlog import *
13 from difflib import SequenceMatcher
13 from difflib import SequenceMatcher
14
14
15 class filelog(revlog):
15 class filelog(revlog):
16 def __init__(self, opener, path):
16 def __init__(self, opener, path):
17 revlog.__init__(self, opener,
17 revlog.__init__(self, opener,
18 os.path.join("data", path + ".i"),
18 os.path.join("data", path + ".i"),
19 os.path.join("data", path + ".d"))
19 os.path.join("data", path + ".d"))
20
20
21 def read(self, node):
21 def read(self, node):
22 return self.revision(node)
22 return self.revision(node)
23 def add(self, text, transaction, link, p1=None, p2=None):
23 def add(self, text, transaction, link, p1=None, p2=None):
24 return self.addrevision(text, transaction, link, p1, p2)
24 return self.addrevision(text, transaction, link, p1, p2)
25
25
26 def annotate(self, node):
26 def annotate(self, node):
27
27
28 def decorate(text, rev):
28 def decorate(text, rev):
29 return [(rev, l) for l in text.splitlines(1)]
29 return [(rev, l) for l in text.splitlines(1)]
30
30
31 def strip(annotation):
31 def strip(annotation):
32 return [e[1] for e in annotation]
32 return [e[1] for e in annotation]
33
33
34 def pair(parent, child):
34 def pair(parent, child):
35 new = []
35 new = []
36 sm = SequenceMatcher(None, strip(parent), strip(child))
36 sm = SequenceMatcher(None, strip(parent), strip(child))
37 for o, m, n, s, t in sm.get_opcodes():
37 for o, m, n, s, t in sm.get_opcodes():
38 if o == 'equal':
38 if o == 'equal':
39 new += parent[m:n]
39 new += parent[m:n]
40 else:
40 else:
41 new += child[s:t]
41 new += child[s:t]
42 return new
42 return new
43
43
44 # find all ancestors
44 # find all ancestors
45 needed = {node:1}
45 needed = {node:1}
46 visit = [node]
46 visit = [node]
47 while visit:
47 while visit:
48 n = visit.pop(0)
48 n = visit.pop(0)
49 for p in self.parents(n):
49 for p in self.parents(n):
50 if p not in needed:
50 if p not in needed:
51 needed[p] = 1
51 needed[p] = 1
52 visit.append(p)
52 visit.append(p)
53 else:
53 else:
54 # count how many times we'll use this
54 # count how many times we'll use this
55 needed[p] += 1
55 needed[p] += 1
56
56
57 # sort by revision which is a topological order
57 # sort by revision which is a topological order
58 visit = needed.keys()
58 visit = needed.keys()
59 visit = [ (self.rev(n), n) for n in visit ]
59 visit = [ (self.rev(n), n) for n in visit ]
60 visit.sort()
60 visit.sort()
61 visit = [ p[1] for p in visit ]
61 visit = [ p[1] for p in visit ]
62 hist = {}
62 hist = {}
63
63
64 for n in visit:
64 for n in visit:
65 curr = decorate(self.read(n), self.linkrev(n))
65 curr = decorate(self.read(n), self.linkrev(n))
66 for p in self.parents(n):
66 for p in self.parents(n):
67 if p != nullid:
67 if p != nullid:
68 curr = pair(hist[p], curr)
68 curr = pair(hist[p], curr)
69 # trim the history of unneeded revs
69 # trim the history of unneeded revs
70 needed[p] -= 1
70 needed[p] -= 1
71 if not needed[p]:
71 if not needed[p]:
72 del hist[p]
72 del hist[p]
73 hist[n] = curr
73 hist[n] = curr
74
74
75 return hist[n]
75 return hist[n]
76
76
77 class manifest(revlog):
77 class manifest(revlog):
78 def __init__(self, opener):
78 def __init__(self, opener):
79 self.mapcache = None
79 self.mapcache = None
80 self.listcache = None
80 self.listcache = None
81 self.addlist = None
81 self.addlist = None
82 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
82 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
83
83
84 def read(self, node):
84 def read(self, node):
85 if self.mapcache and self.mapcache[0] == node:
85 if self.mapcache and self.mapcache[0] == node:
86 return self.mapcache[1].copy()
86 return self.mapcache[1].copy()
87 text = self.revision(node)
87 text = self.revision(node)
88 map = {}
88 map = {}
89 self.listcache = (text, text.splitlines(1))
89 self.listcache = (text, text.splitlines(1))
90 for l in self.listcache[1]:
90 for l in self.listcache[1]:
91 (f, n) = l.split('\0')
91 (f, n) = l.split('\0')
92 map[f] = bin(n[:40])
92 map[f] = bin(n[:40])
93 self.mapcache = (node, map)
93 self.mapcache = (node, map)
94 return map
94 return map
95
95
96 def diff(self, a, b):
96 def diff(self, a, b):
97 # this is sneaky, as we're not actually using a and b
97 # this is sneaky, as we're not actually using a and b
98 if self.listcache and self.addlist and self.listcache[0] == a:
98 if self.listcache and self.addlist and self.listcache[0] == a:
99 d = mdiff.diff(self.listcache[1], self.addlist, 1)
99 d = mdiff.diff(self.listcache[1], self.addlist, 1)
100 if mdiff.patch(a, d) != b:
100 if mdiff.patch(a, d) != b:
101 sys.stderr.write("*** sortdiff failed, falling back ***\n")
101 sys.stderr.write("*** sortdiff failed, falling back ***\n")
102 return mdiff.textdiff(a, b)
102 return mdiff.textdiff(a, b)
103 return d
103 return d
104 else:
104 else:
105 return mdiff.textdiff(a, b)
105 return mdiff.textdiff(a, b)
106
106
107 def add(self, map, transaction, link, p1=None, p2=None):
107 def add(self, map, transaction, link, p1=None, p2=None):
108 files = map.keys()
108 files = map.keys()
109 files.sort()
109 files.sort()
110
110
111 self.addlist = ["%s\000%s\n" % (f, hex(map[f])) for f in files]
111 self.addlist = ["%s\000%s\n" % (f, hex(map[f])) for f in files]
112 text = "".join(self.addlist)
112 text = "".join(self.addlist)
113
113
114 n = self.addrevision(text, transaction, link, p1, p2)
114 n = self.addrevision(text, transaction, link, p1, p2)
115 self.mapcache = (n, map)
115 self.mapcache = (n, map)
116 self.listcache = (text, self.addlist)
116 self.listcache = (text, self.addlist)
117 self.addlist = None
117 self.addlist = None
118
118
119 return n
119 return n
120
120
121 class changelog(revlog):
121 class changelog(revlog):
122 def __init__(self, opener):
122 def __init__(self, opener):
123 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
123 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
124
124
125 def extract(self, text):
125 def extract(self, text):
126 if not text:
126 if not text:
127 return (nullid, "", "0", [], "")
127 return (nullid, "", "0", [], "")
128 last = text.index("\n\n")
128 last = text.index("\n\n")
129 desc = text[last + 2:]
129 desc = text[last + 2:]
130 l = text[:last].splitlines()
130 l = text[:last].splitlines()
131 manifest = bin(l[0])
131 manifest = bin(l[0])
132 user = l[1]
132 user = l[1]
133 date = l[2]
133 date = l[2]
134 files = l[3:]
134 files = l[3:]
135 return (manifest, user, date, files, desc)
135 return (manifest, user, date, files, desc)
136
136
137 def read(self, node):
137 def read(self, node):
138 return self.extract(self.revision(node))
138 return self.extract(self.revision(node))
139
139
140 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
140 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
141 user=None, date=None):
141 user=None, date=None):
142 user = (user or
142 user = (user or
143 os.environ.get("HGUSER") or
143 os.environ.get("HGUSER") or
144 os.environ.get("EMAIL") or
144 os.environ.get("EMAIL") or
145 os.environ.get("LOGNAME", "unknown") + '@' + socket.getfqdn())
145 os.environ.get("LOGNAME", "unknown") + '@' + socket.getfqdn())
146 date = date or "%d %d" % (time.time(), time.timezone)
146 date = date or "%d %d" % (time.time(), time.timezone)
147 list.sort()
147 list.sort()
148 l = [hex(manifest), user, date] + list + ["", desc]
148 l = [hex(manifest), user, date] + list + ["", desc]
149 text = "\n".join(l)
149 text = "\n".join(l)
150 return self.addrevision(text, transaction, self.count(), p1, p2)
150 return self.addrevision(text, transaction, self.count(), p1, p2)
151
151
152 class dirstate:
152 class dirstate:
153 def __init__(self, opener, ui):
153 def __init__(self, opener, ui):
154 self.opener = opener
154 self.opener = opener
155 self.dirty = 0
155 self.dirty = 0
156 self.ui = ui
156 self.ui = ui
157 self.map = None
157 self.map = None
158 self.pl = None
158 self.pl = None
159
159
160 def __del__(self):
160 def __del__(self):
161 if self.dirty:
161 if self.dirty:
162 self.write()
162 self.write()
163
163
164 def __getitem__(self, key):
164 def __getitem__(self, key):
165 try:
165 try:
166 return self.map[key]
166 return self.map[key]
167 except TypeError:
167 except TypeError:
168 self.read()
168 self.read()
169 return self[key]
169 return self[key]
170
170
171 def __contains__(self, key):
171 def __contains__(self, key):
172 if not self.map: self.read()
172 if not self.map: self.read()
173 return key in self.map
173 return key in self.map
174
174
175 def parents(self):
175 def parents(self):
176 if not self.pl:
176 if not self.pl:
177 self.read()
177 self.read()
178 return self.pl
178 return self.pl
179
179
180 def setparents(self, p1, p2 = nullid):
180 def setparents(self, p1, p2 = nullid):
181 self.dirty = 1
181 self.dirty = 1
182 self.pl = p1, p2
182 self.pl = p1, p2
183
183
184 def state(self, key):
184 def state(self, key):
185 try:
185 try:
186 return self[key][0]
186 return self[key][0]
187 except KeyError:
187 except KeyError:
188 return "?"
188 return "?"
189
189
190 def read(self):
190 def read(self):
191 if self.map is not None: return self.map
191 if self.map is not None: return self.map
192
192
193 self.map = {}
193 self.map = {}
194 self.pl = [nullid, nullid]
194 self.pl = [nullid, nullid]
195 try:
195 try:
196 st = self.opener("dirstate").read()
196 st = self.opener("dirstate").read()
197 except: return
197 except: return
198
198
199 self.pl = [st[:20], st[20: 40]]
199 self.pl = [st[:20], st[20: 40]]
200
200
201 pos = 40
201 pos = 40
202 while pos < len(st):
202 while pos < len(st):
203 e = struct.unpack(">cllll", st[pos:pos+17])
203 e = struct.unpack(">cllll", st[pos:pos+17])
204 l = e[4]
204 l = e[4]
205 pos += 17
205 pos += 17
206 f = st[pos:pos + l]
206 f = st[pos:pos + l]
207 self.map[f] = e[:4]
207 self.map[f] = e[:4]
208 pos += l
208 pos += l
209
209
210 def update(self, files, state):
210 def update(self, files, state):
211 ''' current states:
211 ''' current states:
212 n normal
212 n normal
213 m needs merging
213 m needs merging
214 i invalid
214 i invalid
215 r marked for removal
215 r marked for removal
216 a marked for addition'''
216 a marked for addition'''
217
217
218 if not files: return
218 if not files: return
219 self.read()
219 self.read()
220 self.dirty = 1
220 self.dirty = 1
221 for f in files:
221 for f in files:
222 if state == "r":
222 if state == "r":
223 self.map[f] = ('r', 0, 0, 0)
223 self.map[f] = ('r', 0, 0, 0)
224 else:
224 else:
225 try:
225 try:
226 s = os.stat(f)
226 s = os.stat(f)
227 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
227 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
228 except OSError:
228 except OSError:
229 if state != "i": raise
229 if state != "i": raise
230 self.map[f] = ('r', 0, 0, 0)
230 self.map[f] = ('r', 0, 0, 0)
231
231
232 def forget(self, files):
232 def forget(self, files):
233 if not files: return
233 if not files: return
234 self.read()
234 self.read()
235 self.dirty = 1
235 self.dirty = 1
236 for f in files:
236 for f in files:
237 try:
237 try:
238 del self.map[f]
238 del self.map[f]
239 except KeyError:
239 except KeyError:
240 self.ui.warn("not in dirstate: %s!\n" % f)
240 self.ui.warn("not in dirstate: %s!\n" % f)
241 pass
241 pass
242
242
243 def clear(self):
243 def clear(self):
244 self.map = {}
244 self.map = {}
245 self.dirty = 1
245 self.dirty = 1
246
246
247 def write(self):
247 def write(self):
248 st = self.opener("dirstate", "w")
248 st = self.opener("dirstate", "w")
249 st.write("".join(self.pl))
249 st.write("".join(self.pl))
250 for f, e in self.map.items():
250 for f, e in self.map.items():
251 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
251 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
252 st.write(e + f)
252 st.write(e + f)
253 self.dirty = 0
253 self.dirty = 0
254
254
255 def copy(self):
255 def copy(self):
256 self.read()
256 self.read()
257 return self.map.copy()
257 return self.map.copy()
258
258
259 # used to avoid circular references so destructors work
259 # used to avoid circular references so destructors work
260 def opener(base):
260 def opener(base):
261 p = base
261 p = base
262 def o(path, mode="r"):
262 def o(path, mode="r"):
263 if p[:7] == "http://":
263 if p[:7] == "http://":
264 f = os.path.join(p, urllib.quote(path))
264 f = os.path.join(p, urllib.quote(path))
265 return httprangereader(f)
265 return httprangereader(f)
266
266
267 f = os.path.join(p, path)
267 f = os.path.join(p, path)
268
268
269 if mode != "r":
269 if mode != "r":
270 try:
270 try:
271 s = os.stat(f)
271 s = os.stat(f)
272 except OSError:
272 except OSError:
273 d = os.path.dirname(f)
273 d = os.path.dirname(f)
274 if not os.path.isdir(d):
274 if not os.path.isdir(d):
275 os.makedirs(d)
275 os.makedirs(d)
276 else:
276 else:
277 if s.st_nlink > 1:
277 if s.st_nlink > 1:
278 file(f + ".tmp", "w").write(file(f).read())
278 file(f + ".tmp", "w").write(file(f).read())
279 os.rename(f+".tmp", f)
279 os.rename(f+".tmp", f)
280
280
281 return file(f, mode)
281 return file(f, mode)
282
282
283 return o
283 return o
284
284
285 class localrepository:
285 class localrepository:
286 def __init__(self, ui, path=None, create=0):
286 def __init__(self, ui, path=None, create=0):
287 self.remote = 0
287 self.remote = 0
288 if path and path[:7] == "http://":
288 if path and path[:7] == "http://":
289 self.remote = 1
289 self.remote = 1
290 self.path = path
290 self.path = path
291 else:
291 else:
292 if not path:
292 if not path:
293 p = os.getcwd()
293 p = os.getcwd()
294 while not os.path.isdir(os.path.join(p, ".hg")):
294 while not os.path.isdir(os.path.join(p, ".hg")):
295 p = os.path.dirname(p)
295 p = os.path.dirname(p)
296 if p == "/": raise "No repo found"
296 if p == "/": raise "No repo found"
297 path = p
297 path = p
298 self.path = os.path.join(path, ".hg")
298 self.path = os.path.join(path, ".hg")
299
299
300 self.root = path
300 self.root = path
301 self.ui = ui
301 self.ui = ui
302
302
303 if create:
303 if create:
304 os.mkdir(self.path)
304 os.mkdir(self.path)
305 os.mkdir(self.join("data"))
305 os.mkdir(self.join("data"))
306
306
307 self.opener = opener(self.path)
307 self.opener = opener(self.path)
308 self.manifest = manifest(self.opener)
308 self.manifest = manifest(self.opener)
309 self.changelog = changelog(self.opener)
309 self.changelog = changelog(self.opener)
310 self.ignorelist = None
310 self.ignorelist = None
311 self.tags = None
311 self.tags = None
312
312
313 if not self.remote:
313 if not self.remote:
314 self.dirstate = dirstate(self.opener, ui)
314 self.dirstate = dirstate(self.opener, ui)
315
315
316 def ignore(self, f):
316 def ignore(self, f):
317 if self.ignorelist is None:
317 if self.ignorelist is None:
318 self.ignorelist = []
318 self.ignorelist = []
319 try:
319 try:
320 l = open(os.path.join(self.root, ".hgignore"))
320 l = open(os.path.join(self.root, ".hgignore"))
321 for pat in l:
321 for pat in l:
322 if pat != "\n":
322 if pat != "\n":
323 self.ignorelist.append(re.compile(pat[:-1]))
323 self.ignorelist.append(re.compile(pat[:-1]))
324 except IOError: pass
324 except IOError: pass
325 for pat in self.ignorelist:
325 for pat in self.ignorelist:
326 if pat.search(f): return True
326 if pat.search(f): return True
327 return False
327 return False
328
328
329 def lookup(self, key):
329 def lookup(self, key):
330 if self.tags is None:
330 if self.tags is None:
331 self.tags = {}
331 self.tags = {}
332 try:
332 try:
333 fl = self.file(".hgtags")
333 fl = self.file(".hgtags")
334 for l in fl.revision(fl.tip()).splitlines():
334 for l in fl.revision(fl.tip()).splitlines():
335 if l:
335 if l:
336 n, k = l.split(" ")
336 n, k = l.split(" ")
337 self.tags[k] = bin(n)
337 self.tags[k] = bin(n)
338 except KeyError: pass
338 except KeyError: pass
339 try:
339 try:
340 return self.tags[key]
340 return self.tags[key]
341 except KeyError:
341 except KeyError:
342 return self.changelog.lookup(key)
342 return self.changelog.lookup(key)
343
343
344 def join(self, f):
344 def join(self, f):
345 return os.path.join(self.path, f)
345 return os.path.join(self.path, f)
346
346
347 def file(self, f):
347 def file(self, f):
348 if f[0] == '/': f = f[1:]
348 if f[0] == '/': f = f[1:]
349 return filelog(self.opener, f)
349 return filelog(self.opener, f)
350
350
351 def transaction(self):
351 def transaction(self):
352 return transaction(self.opener, self.join("journal"),
352 return transaction(self.opener, self.join("journal"),
353 self.join("undo"))
353 self.join("undo"))
354
354
355 def recover(self):
355 def recover(self):
356 lock = self.lock()
356 lock = self.lock()
357 if os.path.exists(self.join("recover")):
357 if os.path.exists(self.join("recover")):
358 self.ui.status("attempting to rollback interrupted transaction\n")
358 self.ui.status("attempting to rollback interrupted transaction\n")
359 return rollback(self.opener, self.join("recover"))
359 return rollback(self.opener, self.join("recover"))
360 else:
360 else:
361 self.ui.warn("no interrupted transaction available\n")
361 self.ui.warn("no interrupted transaction available\n")
362
362
363 def undo(self):
363 def undo(self):
364 lock = self.lock()
364 lock = self.lock()
365 if os.path.exists(self.join("undo")):
365 if os.path.exists(self.join("undo")):
366 f = self.changelog.read(self.changelog.tip())[3]
366 f = self.changelog.read(self.changelog.tip())[3]
367 self.ui.status("attempting to rollback last transaction\n")
367 self.ui.status("attempting to rollback last transaction\n")
368 rollback(self.opener, self.join("undo"))
368 rollback(self.opener, self.join("undo"))
369 self.manifest = manifest(self.opener)
369 self.manifest = manifest(self.opener)
370 self.changelog = changelog(self.opener)
370 self.changelog = changelog(self.opener)
371
371
372 self.ui.status("discarding dirstate\n")
372 self.ui.status("discarding dirstate\n")
373 node = self.changelog.tip()
373 node = self.changelog.tip()
374 f.sort()
374 f.sort()
375
375
376 self.dirstate.setparents(node)
376 self.dirstate.setparents(node)
377 self.dirstate.update(f, 'i')
377 self.dirstate.update(f, 'i')
378
378
379 else:
379 else:
380 self.ui.warn("no undo information available\n")
380 self.ui.warn("no undo information available\n")
381
381
382 def lock(self, wait = 1):
382 def lock(self, wait = 1):
383 try:
383 try:
384 return lock.lock(self.join("lock"), 0)
384 return lock.lock(self.join("lock"), 0)
385 except lock.LockHeld, inst:
385 except lock.LockHeld, inst:
386 if wait:
386 if wait:
387 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
387 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
388 return lock.lock(self.join("lock"), wait)
388 return lock.lock(self.join("lock"), wait)
389 raise inst
389 raise inst
390
390
391 def rawcommit(self, files, text, user, date, p1=None, p2=None):
391 def rawcommit(self, files, text, user, date, p1=None, p2=None):
392 p1 = p1 or self.dirstate.parents()[0] or nullid
392 p1 = p1 or self.dirstate.parents()[0] or nullid
393 p2 = p2 or self.dirstate.parents()[1] or nullid
393 p2 = p2 or self.dirstate.parents()[1] or nullid
394 pchange = self.changelog.read(p1)
394 pchange = self.changelog.read(p1)
395 pmmap = self.manifest.read(pchange[0])
395 pmmap = self.manifest.read(pchange[0])
396 tr = self.transaction()
396 tr = self.transaction()
397 mmap = {}
397 mmap = {}
398 linkrev = self.changelog.count()
398 linkrev = self.changelog.count()
399 for f in files:
399 for f in files:
400 try:
400 try:
401 t = file(f).read()
401 t = file(f).read()
402 except IOError:
402 except IOError:
403 self.ui.warn("Read file %s error, skipped\n" % f)
403 self.ui.warn("Read file %s error, skipped\n" % f)
404 continue
404 continue
405 r = self.file(f)
405 r = self.file(f)
406 # FIXME - need to find both parents properly
406 # FIXME - need to find both parents properly
407 prev = pmmap.get(f, nullid)
407 prev = pmmap.get(f, nullid)
408 mmap[f] = r.add(t, tr, linkrev, prev)
408 mmap[f] = r.add(t, tr, linkrev, prev)
409
409
410 mnode = self.manifest.add(mmap, tr, linkrev, pchange[0])
410 mnode = self.manifest.add(mmap, tr, linkrev, pchange[0])
411 n = self.changelog.add(mnode, files, text, tr, p1, p2, user ,date, )
411 n = self.changelog.add(mnode, files, text, tr, p1, p2, user ,date, )
412 tr.close()
412 tr.close()
413 self.dirstate.setparents(p1, p2)
413 self.dirstate.setparents(p1, p2)
414 self.dirstate.clear()
414 self.dirstate.clear()
415 self.dirstate.update(mmap.keys(), "n")
415 self.dirstate.update(mmap.keys(), "n")
416
416
417 def commit(self, files = None, text = ""):
417 def commit(self, files = None, text = ""):
418 commit = []
418 commit = []
419 remove = []
419 remove = []
420 if files:
420 if files:
421 for f in files:
421 for f in files:
422 s = self.dirstate.state(f)
422 s = self.dirstate.state(f)
423 if s in 'cai':
423 if s in 'cai':
424 commit.append(f)
424 commit.append(f)
425 elif s == 'r':
425 elif s == 'r':
426 remove.append(f)
426 remove.append(f)
427 else:
427 else:
428 self.warn("%s not tracked!\n")
428 self.warn("%s not tracked!\n")
429 else:
429 else:
430 (c, a, d, u) = self.diffdir(self.root)
430 (c, a, d, u) = self.diffdir(self.root)
431 commit = c + a
431 commit = c + a
432 remove = d
432 remove = d
433
433
434 if not commit and not remove:
434 if not commit and not remove:
435 self.ui.status("nothing changed\n")
435 self.ui.status("nothing changed\n")
436 return
436 return
437
437
438 p1, p2 = self.dirstate.parents()
438 p1, p2 = self.dirstate.parents()
439 c1 = self.changelog.read(p1)
439 c1 = self.changelog.read(p1)
440 c2 = self.changelog.read(p2)
440 c2 = self.changelog.read(p2)
441 m1 = self.manifest.read(c1[0])
441 m1 = self.manifest.read(c1[0])
442 m2 = self.manifest.read(c2[0])
442 m2 = self.manifest.read(c2[0])
443 lock = self.lock()
443 lock = self.lock()
444 tr = self.transaction()
444 tr = self.transaction()
445
445
446 # check in files
446 # check in files
447 new = {}
447 new = {}
448 linkrev = self.changelog.count()
448 linkrev = self.changelog.count()
449 commit.sort()
449 commit.sort()
450 for f in commit:
450 for f in commit:
451 self.ui.note(f + "\n")
451 self.ui.note(f + "\n")
452 try:
452 try:
453 t = file(f).read()
453 t = file(f).read()
454 except IOError:
454 except IOError:
455 self.warn("trouble committing %s!\n" % f)
455 self.warn("trouble committing %s!\n" % f)
456 raise
456 raise
457
457
458 r = self.file(f)
458 r = self.file(f)
459 fp1 = m1.get(f, nullid)
459 fp1 = m1.get(f, nullid)
460 fp2 = m2.get(f, nullid)
460 fp2 = m2.get(f, nullid)
461 new[f] = r.add(t, tr, linkrev, fp1, fp2)
461 new[f] = r.add(t, tr, linkrev, fp1, fp2)
462
462
463 # update manifest
463 # update manifest
464 m1.update(new)
464 m1.update(new)
465 for f in remove: del m1[f]
465 for f in remove: del m1[f]
466 mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0])
466 mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0])
467
467
468 # add changeset
468 # add changeset
469 new = new.keys()
469 new = new.keys()
470 new.sort()
470 new.sort()
471
471
472 edittext = text + "\n" + "HG: manifest hash %s\n" % hex(mn)
472 edittext = text + "\n" + "HG: manifest hash %s\n" % hex(mn)
473 edittext += "".join(["HG: changed %s\n" % f for f in new])
473 edittext += "".join(["HG: changed %s\n" % f for f in new])
474 edittext += "".join(["HG: removed %s\n" % f for f in remove])
474 edittext += "".join(["HG: removed %s\n" % f for f in remove])
475 edittext = self.ui.edit(edittext)
475 edittext = self.ui.edit(edittext)
476
476
477 n = self.changelog.add(mn, new, edittext, tr, p1, p2)
477 n = self.changelog.add(mn, new, edittext, tr, p1, p2)
478 tr.close()
478 tr.close()
479
479
480 self.dirstate.setparents(n)
480 self.dirstate.setparents(n)
481 self.dirstate.update(new, "n")
481 self.dirstate.update(new, "n")
482 self.dirstate.forget(remove)
482 self.dirstate.forget(remove)
483
483
484 def checkout(self, node):
484 def checkout(self, node):
485 # checkout is really dumb at the moment
485 # checkout is really dumb at the moment
486 # it ought to basically merge
486 # it ought to basically merge
487 change = self.changelog.read(node)
487 change = self.changelog.read(node)
488 l = self.manifest.read(change[0]).items()
488 l = self.manifest.read(change[0]).items()
489 l.sort()
489 l.sort()
490
490
491 for f,n in l:
491 for f,n in l:
492 if f[0] == "/": continue
492 if f[0] == "/": continue
493 self.ui.note(f, "\n")
493 self.ui.note(f, "\n")
494 t = self.file(f).revision(n)
494 t = self.file(f).revision(n)
495 try:
495 try:
496 file(f, "w").write(t)
496 file(f, "w").write(t)
497 except IOError:
497 except IOError:
498 os.makedirs(os.path.dirname(f))
498 os.makedirs(os.path.dirname(f))
499 file(f, "w").write(t)
499 file(f, "w").write(t)
500
500
501 self.dirstate.setparents(node)
501 self.dirstate.setparents(node)
502 self.dirstate.clear()
502 self.dirstate.clear()
503 self.dirstate.update([f for f,n in l], "n")
503 self.dirstate.update([f for f,n in l], "n")
504
504
505 def diffdir(self, path, changeset = None):
505 def diffdir(self, path, changeset = None):
506 changed = []
506 changed = []
507 added = []
507 added = []
508 unknown = []
508 unknown = []
509 mf = {}
509 mf = {}
510
510
511 if changeset:
511 if changeset:
512 change = self.changelog.read(changeset)
512 change = self.changelog.read(changeset)
513 mf = self.manifest.read(change[0])
513 mf = self.manifest.read(change[0])
514 dc = dict.fromkeys(mf)
514 dc = dict.fromkeys(mf)
515 else:
515 else:
516 changeset = self.dirstate.parents()[0]
516 changeset = self.dirstate.parents()[0]
517 change = self.changelog.read(changeset)
517 change = self.changelog.read(changeset)
518 mf = self.manifest.read(change[0])
518 mf = self.manifest.read(change[0])
519 dc = self.dirstate.copy()
519 dc = self.dirstate.copy()
520
520
521 def fcmp(fn):
521 def fcmp(fn):
522 t1 = file(os.path.join(self.root, fn)).read()
522 t1 = file(os.path.join(self.root, fn)).read()
523 t2 = self.file(fn).revision(mf[fn])
523 t2 = self.file(fn).revision(mf[fn])
524 return cmp(t1, t2)
524 return cmp(t1, t2)
525
525
526 for dir, subdirs, files in os.walk(self.root):
526 for dir, subdirs, files in os.walk(self.root):
527 d = dir[len(self.root)+1:]
527 d = dir[len(self.root)+1:]
528 if ".hg" in subdirs: subdirs.remove(".hg")
528 if ".hg" in subdirs: subdirs.remove(".hg")
529
529
530 for f in files:
530 for f in files:
531 fn = os.path.join(d, f)
531 fn = os.path.join(d, f)
532 try: s = os.stat(os.path.join(self.root, fn))
532 try: s = os.stat(os.path.join(self.root, fn))
533 except: continue
533 except: continue
534 if fn in dc:
534 if fn in dc:
535 c = dc[fn]
535 c = dc[fn]
536 del dc[fn]
536 del dc[fn]
537 if not c:
537 if not c:
538 if fcmp(fn):
538 if fcmp(fn):
539 changed.append(fn)
539 changed.append(fn)
540 elif c[0] == 'i':
540 elif c[0] == 'i':
541 if fn not in mf:
541 if fn not in mf:
542 added.append(fn)
542 added.append(fn)
543 elif fcmp(fn):
543 elif fcmp(fn):
544 changed.append(fn)
544 changed.append(fn)
545 elif c[0] == 'm':
545 elif c[0] == 'm':
546 changed.append(fn)
546 changed.append(fn)
547 elif c[0] == 'a':
547 elif c[0] == 'a':
548 added.append(fn)
548 added.append(fn)
549 elif c[0] == 'r':
549 elif c[0] == 'r':
550 unknown.append(fn)
550 unknown.append(fn)
551 elif c[2] != s.st_size:
551 elif c[2] != s.st_size:
552 changed.append(fn)
552 changed.append(fn)
553 elif c[1] != s.st_mode or c[3] != s.st_mtime:
553 elif c[1] != s.st_mode or c[3] != s.st_mtime:
554 if fcmp(fn):
554 if fcmp(fn):
555 changed.append(fn)
555 changed.append(fn)
556 else:
556 else:
557 if self.ignore(fn): continue
557 if self.ignore(fn): continue
558 unknown.append(fn)
558 unknown.append(fn)
559
559
560 deleted = dc.keys()
560 deleted = dc.keys()
561 deleted.sort()
561 deleted.sort()
562
562
563 return (changed, added, deleted, unknown)
563 return (changed, added, deleted, unknown)
564
564
565 def diffrevs(self, node1, node2):
565 def diffrevs(self, node1, node2):
566 changed, added = [], []
566 changed, added = [], []
567
567
568 change = self.changelog.read(node1)
568 change = self.changelog.read(node1)
569 mf1 = self.manifest.read(change[0])
569 mf1 = self.manifest.read(change[0])
570 change = self.changelog.read(node2)
570 change = self.changelog.read(node2)
571 mf2 = self.manifest.read(change[0])
571 mf2 = self.manifest.read(change[0])
572
572
573 for fn in mf2:
573 for fn in mf2:
574 if mf1.has_key(fn):
574 if mf1.has_key(fn):
575 if mf1[fn] != mf2[fn]:
575 if mf1[fn] != mf2[fn]:
576 changed.append(fn)
576 changed.append(fn)
577 del mf1[fn]
577 del mf1[fn]
578 else:
578 else:
579 added.append(fn)
579 added.append(fn)
580
580
581 deleted = mf1.keys()
581 deleted = mf1.keys()
582 deleted.sort()
582 deleted.sort()
583
583
584 return (changed, added, deleted)
584 return (changed, added, deleted)
585
585
586 def add(self, list):
586 def add(self, list):
587 for f in list:
587 for f in list:
588 p = os.path.join(self.root, f)
588 p = os.path.join(self.root, f)
589 if not os.path.isfile(p):
589 if not os.path.isfile(p):
590 self.ui.warn("%s does not exist!\n" % f)
590 self.ui.warn("%s does not exist!\n" % f)
591 elif self.dirstate.state(f) == 'n':
591 elif self.dirstate.state(f) == 'n':
592 self.ui.warn("%s already tracked!\n" % f)
592 self.ui.warn("%s already tracked!\n" % f)
593 else:
593 else:
594 self.dirstate.update([f], "a")
594 self.dirstate.update([f], "a")
595
595
596 def forget(self, list):
596 def forget(self, list):
597 for f in list:
597 for f in list:
598 if self.dirstate.state(f) not in 'ai':
598 if self.dirstate.state(f) not in 'ai':
599 self.ui.warn("%s not added!\n" % f)
599 self.ui.warn("%s not added!\n" % f)
600 else:
600 else:
601 self.dirstate.forget([f])
601 self.dirstate.forget([f])
602
602
603 def remove(self, list):
603 def remove(self, list):
604 for f in list:
604 for f in list:
605 p = os.path.join(self.root, f)
605 p = os.path.join(self.root, f)
606 if os.path.isfile(p):
606 if os.path.isfile(p):
607 self.ui.warn("%s still exists!\n" % f)
607 self.ui.warn("%s still exists!\n" % f)
608 elif f not in self.dirstate:
608 elif f not in self.dirstate:
609 self.ui.warn("%s not tracked!\n" % f)
609 self.ui.warn("%s not tracked!\n" % f)
610 else:
610 else:
611 self.dirstate.update([f], "r")
611 self.dirstate.update([f], "r")
612
612
613 def heads(self):
613 def heads(self):
614 return self.changelog.heads()
614 return self.changelog.heads()
615
615
616 def branches(self, nodes):
616 def branches(self, nodes):
617 if not nodes: nodes = [self.changelog.tip()]
617 if not nodes: nodes = [self.changelog.tip()]
618 b = []
618 b = []
619 for n in nodes:
619 for n in nodes:
620 t = n
620 t = n
621 while n:
621 while n:
622 p = self.changelog.parents(n)
622 p = self.changelog.parents(n)
623 if p[1] != nullid or p[0] == nullid:
623 if p[1] != nullid or p[0] == nullid:
624 b.append((t, n, p[0], p[1]))
624 b.append((t, n, p[0], p[1]))
625 break
625 break
626 n = p[0]
626 n = p[0]
627 return b
627 return b
628
628
629 def between(self, pairs):
629 def between(self, pairs):
630 r = []
630 r = []
631
631
632 for top, bottom in pairs:
632 for top, bottom in pairs:
633 n, l, i = top, [], 0
633 n, l, i = top, [], 0
634 f = 1
634 f = 1
635
635
636 while n != bottom:
636 while n != bottom:
637 p = self.changelog.parents(n)[0]
637 p = self.changelog.parents(n)[0]
638 if i == f:
638 if i == f:
639 l.append(n)
639 l.append(n)
640 f = f * 2
640 f = f * 2
641 n = p
641 n = p
642 i += 1
642 i += 1
643
643
644 r.append(l)
644 r.append(l)
645
645
646 return r
646 return r
647
647
648 def newer(self, nodes):
648 def newer(self, nodes):
649 m = {}
649 m = {}
650 nl = []
650 nl = []
651 pm = {}
651 pm = {}
652 cl = self.changelog
652 cl = self.changelog
653 t = l = cl.count()
653 t = l = cl.count()
654
654
655 # find the lowest numbered node
655 # find the lowest numbered node
656 for n in nodes:
656 for n in nodes:
657 l = min(l, cl.rev(n))
657 l = min(l, cl.rev(n))
658 m[n] = 1
658 m[n] = 1
659
659
660 for i in xrange(l, t):
660 for i in xrange(l, t):
661 n = cl.node(i)
661 n = cl.node(i)
662 if n in m: # explicitly listed
662 if n in m: # explicitly listed
663 pm[n] = 1
663 pm[n] = 1
664 nl.append(n)
664 nl.append(n)
665 continue
665 continue
666 for p in cl.parents(n):
666 for p in cl.parents(n):
667 if p in pm: # parent listed
667 if p in pm: # parent listed
668 pm[n] = 1
668 pm[n] = 1
669 nl.append(n)
669 nl.append(n)
670 break
670 break
671
671
672 return nl
672 return nl
673
673
674 def getchangegroup(self, remote):
674 def getchangegroup(self, remote):
675 m = self.changelog.nodemap
675 m = self.changelog.nodemap
676 search = []
676 search = []
677 fetch = []
677 fetch = []
678 seen = {}
678 seen = {}
679 seenbranch = {}
679 seenbranch = {}
680
680
681 # if we have an empty repo, fetch everything
681 # if we have an empty repo, fetch everything
682 if self.changelog.tip() == nullid:
682 if self.changelog.tip() == nullid:
683 self.ui.status("requesting all changes\n")
683 self.ui.status("requesting all changes\n")
684 return remote.changegroup([nullid])
684 return remote.changegroup([nullid])
685
685
686 # otherwise, assume we're closer to the tip than the root
686 # otherwise, assume we're closer to the tip than the root
687 self.ui.status("searching for changes\n")
687 self.ui.status("searching for changes\n")
688 heads = remote.heads()
688 heads = remote.heads()
689 unknown = []
689 unknown = []
690 for h in heads:
690 for h in heads:
691 if h not in m:
691 if h not in m:
692 unknown.append(h)
692 unknown.append(h)
693
693
694 if not unknown:
694 if not unknown:
695 self.ui.status("nothing to do!\n")
695 self.ui.status("nothing to do!\n")
696 return None
696 return None
697
697
698 unknown = remote.branches(unknown)
698 unknown = remote.branches(unknown)
699 while unknown:
699 while unknown:
700 n = unknown.pop(0)
700 n = unknown.pop(0)
701 seen[n[0]] = 1
701 seen[n[0]] = 1
702
702
703 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
703 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
704 if n == nullid: break
704 if n == nullid: break
705 if n in seenbranch:
705 if n in seenbranch:
706 self.ui.debug("branch already found\n")
706 self.ui.debug("branch already found\n")
707 continue
707 continue
708 if n[1] and n[1] in m: # do we know the base?
708 if n[1] and n[1] in m: # do we know the base?
709 self.ui.debug("found incomplete branch %s:%s\n"
709 self.ui.debug("found incomplete branch %s:%s\n"
710 % (short(n[0]), short(n[1])))
710 % (short(n[0]), short(n[1])))
711 search.append(n) # schedule branch range for scanning
711 search.append(n) # schedule branch range for scanning
712 seenbranch[n] = 1
712 seenbranch[n] = 1
713 else:
713 else:
714 if n[2] in m and n[3] in m:
714 if n[2] in m and n[3] in m:
715 if n[1] not in fetch:
715 if n[1] not in fetch:
716 self.ui.debug("found new changeset %s\n" %
716 self.ui.debug("found new changeset %s\n" %
717 short(n[1]))
717 short(n[1]))
718 fetch.append(n[1]) # earliest unknown
718 fetch.append(n[1]) # earliest unknown
719 continue
719 continue
720
720
721 r = []
721 r = []
722 for a in n[2:4]:
722 for a in n[2:4]:
723 if a not in seen: r.append(a)
723 if a not in seen: r.append(a)
724
724
725 if r:
725 if r:
726 self.ui.debug("requesting %s\n" %
726 self.ui.debug("requesting %s\n" %
727 " ".join(map(short, r)))
727 " ".join(map(short, r)))
728 for b in remote.branches(r):
728 for b in remote.branches(r):
729 self.ui.debug("received %s:%s\n" %
729 self.ui.debug("received %s:%s\n" %
730 (short(b[0]), short(b[1])))
730 (short(b[0]), short(b[1])))
731 if b[0] not in m and b[0] not in seen:
731 if b[0] not in m and b[0] not in seen:
732 unknown.append(b)
732 unknown.append(b)
733
733
734 while search:
734 while search:
735 n = search.pop(0)
735 n = search.pop(0)
736 l = remote.between([(n[0], n[1])])[0]
736 l = remote.between([(n[0], n[1])])[0]
737 p = n[0]
737 p = n[0]
738 f = 1
738 f = 1
739 for i in l + [n[1]]:
739 for i in l + [n[1]]:
740 if i in m:
740 if i in m:
741 if f <= 2:
741 if f <= 2:
742 self.ui.debug("found new branch changeset %s\n" %
742 self.ui.debug("found new branch changeset %s\n" %
743 short(p))
743 short(p))
744 fetch.append(p)
744 fetch.append(p)
745 else:
745 else:
746 self.ui.debug("narrowed branch search to %s:%s\n"
746 self.ui.debug("narrowed branch search to %s:%s\n"
747 % (short(p), short(i)))
747 % (short(p), short(i)))
748 search.append((p, i))
748 search.append((p, i))
749 break
749 break
750 p, f = i, f * 2
750 p, f = i, f * 2
751
751
752 for f in fetch:
752 for f in fetch:
753 if f in m:
753 if f in m:
754 raise "already have", short(f[:4])
754 raise "already have", short(f[:4])
755
755
756 self.ui.note("adding new changesets starting at " +
756 self.ui.note("adding new changesets starting at " +
757 " ".join([short(f) for f in fetch]) + "\n")
757 " ".join([short(f) for f in fetch]) + "\n")
758
758
759 return remote.changegroup(fetch)
759 return remote.changegroup(fetch)
760
760
761 def changegroup(self, basenodes):
761 def changegroup(self, basenodes):
762 nodes = self.newer(basenodes)
762 nodes = self.newer(basenodes)
763
763
764 # construct the link map
764 # construct the link map
765 linkmap = {}
765 linkmap = {}
766 for n in nodes:
766 for n in nodes:
767 linkmap[self.changelog.rev(n)] = n
767 linkmap[self.changelog.rev(n)] = n
768
768
769 # construct a list of all changed files
769 # construct a list of all changed files
770 changed = {}
770 changed = {}
771 for n in nodes:
771 for n in nodes:
772 c = self.changelog.read(n)
772 c = self.changelog.read(n)
773 for f in c[3]:
773 for f in c[3]:
774 changed[f] = 1
774 changed[f] = 1
775 changed = changed.keys()
775 changed = changed.keys()
776 changed.sort()
776 changed.sort()
777
777
778 # the changegroup is changesets + manifests + all file revs
778 # the changegroup is changesets + manifests + all file revs
779 revs = [ self.changelog.rev(n) for n in nodes ]
779 revs = [ self.changelog.rev(n) for n in nodes ]
780
780
781 for y in self.changelog.group(linkmap): yield y
781 for y in self.changelog.group(linkmap): yield y
782 for y in self.manifest.group(linkmap): yield y
782 for y in self.manifest.group(linkmap): yield y
783 for f in changed:
783 for f in changed:
784 yield struct.pack(">l", len(f) + 4) + f
784 yield struct.pack(">l", len(f) + 4) + f
785 g = self.file(f).group(linkmap)
785 g = self.file(f).group(linkmap)
786 for y in g:
786 for y in g:
787 yield y
787 yield y
788
788
789 def addchangegroup(self, generator):
789 def addchangegroup(self, generator):
790
790
791 class genread:
791 class genread:
792 def __init__(self, generator):
792 def __init__(self, generator):
793 self.g = generator
793 self.g = generator
794 self.buf = ""
794 self.buf = ""
795 def read(self, l):
795 def read(self, l):
796 while l > len(self.buf):
796 while l > len(self.buf):
797 try:
797 try:
798 self.buf += self.g.next()
798 self.buf += self.g.next()
799 except StopIteration:
799 except StopIteration:
800 break
800 break
801 d, self.buf = self.buf[:l], self.buf[l:]
801 d, self.buf = self.buf[:l], self.buf[l:]
802 return d
802 return d
803
803
804 def getchunk():
804 def getchunk():
805 d = source.read(4)
805 d = source.read(4)
806 if not d: return ""
806 if not d: return ""
807 l = struct.unpack(">l", d)[0]
807 l = struct.unpack(">l", d)[0]
808 if l <= 4: return ""
808 if l <= 4: return ""
809 return source.read(l - 4)
809 return source.read(l - 4)
810
810
811 def getgroup():
811 def getgroup():
812 while 1:
812 while 1:
813 c = getchunk()
813 c = getchunk()
814 if not c: break
814 if not c: break
815 yield c
815 yield c
816
816
817 def csmap(x):
817 def csmap(x):
818 self.ui.debug("add changeset %s\n" % short(x))
818 self.ui.debug("add changeset %s\n" % short(x))
819 return self.changelog.count()
819 return self.changelog.count()
820
820
821 def revmap(x):
821 def revmap(x):
822 return self.changelog.rev(x)
822 return self.changelog.rev(x)
823
823
824 if not generator: return
824 if not generator: return
825 changesets = files = revisions = 0
825 changesets = files = revisions = 0
826
826
827 source = genread(generator)
827 source = genread(generator)
828 lock = self.lock()
828 lock = self.lock()
829 tr = self.transaction()
829 tr = self.transaction()
830
830
831 # pull off the changeset group
831 # pull off the changeset group
832 self.ui.status("adding changesets\n")
832 self.ui.status("adding changesets\n")
833 co = self.changelog.tip()
833 co = self.changelog.tip()
834 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
834 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
835 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
835 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
836
836
837 # pull off the manifest group
837 # pull off the manifest group
838 self.ui.status("adding manifests\n")
838 self.ui.status("adding manifests\n")
839 mm = self.manifest.tip()
839 mm = self.manifest.tip()
840 mo = self.manifest.addgroup(getgroup(), revmap, tr)
840 mo = self.manifest.addgroup(getgroup(), revmap, tr)
841
841
842 # process the files
842 # process the files
843 self.ui.status("adding file revisions\n")
843 self.ui.status("adding file revisions\n")
844 while 1:
844 while 1:
845 f = getchunk()
845 f = getchunk()
846 if not f: break
846 if not f: break
847 self.ui.debug("adding %s revisions\n" % f)
847 self.ui.debug("adding %s revisions\n" % f)
848 fl = self.file(f)
848 fl = self.file(f)
849 o = fl.tip()
849 o = fl.tip()
850 n = fl.addgroup(getgroup(), revmap, tr)
850 n = fl.addgroup(getgroup(), revmap, tr)
851 revisions += fl.rev(n) - fl.rev(o)
851 revisions += fl.rev(n) - fl.rev(o)
852 files += 1
852 files += 1
853
853
854 self.ui.status(("modified %d files, added %d changesets" +
854 self.ui.status(("modified %d files, added %d changesets" +
855 " and %d new revisions\n")
855 " and %d new revisions\n")
856 % (files, changesets, revisions))
856 % (files, changesets, revisions))
857
857
858 tr.close()
858 tr.close()
859 return
859 return
860
860
861 def merge(self, generator):
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)
1032 (fd, name) = tempfile.mkstemp("", pre)
968 (fd, name) = tempfile.mkstemp("", pre)
1033 f = os.fdopen(fd, "w")
969 f = os.fdopen(fd, "w")
1034 f.write(fl.revision(node))
970 f.write(fl.revision(node))
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 self.ui.debug("invoking merge with %s\n" % cmd)
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):
1065 self.url = path
994 self.url = path
1066 self.ui = ui
995 self.ui = ui
1067
996
1068 def do_cmd(self, cmd, **args):
997 def do_cmd(self, cmd, **args):
1069 self.ui.debug("sending %s command\n" % cmd)
998 self.ui.debug("sending %s command\n" % cmd)
1070 q = {"cmd": cmd}
999 q = {"cmd": cmd}
1071 q.update(args)
1000 q.update(args)
1072 qs = urllib.urlencode(q)
1001 qs = urllib.urlencode(q)
1073 cu = "%s?%s" % (self.url, qs)
1002 cu = "%s?%s" % (self.url, qs)
1074 return urllib.urlopen(cu)
1003 return urllib.urlopen(cu)
1075
1004
1076 def heads(self):
1005 def heads(self):
1077 d = self.do_cmd("heads").read()
1006 d = self.do_cmd("heads").read()
1078 try:
1007 try:
1079 return map(bin, d[:-1].split(" "))
1008 return map(bin, d[:-1].split(" "))
1080 except:
1009 except:
1081 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1010 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1082 raise
1011 raise
1083
1012
1084 def branches(self, nodes):
1013 def branches(self, nodes):
1085 n = " ".join(map(hex, nodes))
1014 n = " ".join(map(hex, nodes))
1086 d = self.do_cmd("branches", nodes=n).read()
1015 d = self.do_cmd("branches", nodes=n).read()
1087 try:
1016 try:
1088 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1017 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1089 return br
1018 return br
1090 except:
1019 except:
1091 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1020 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1092 raise
1021 raise
1093
1022
1094 def between(self, pairs):
1023 def between(self, pairs):
1095 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1024 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1096 d = self.do_cmd("between", pairs=n).read()
1025 d = self.do_cmd("between", pairs=n).read()
1097 try:
1026 try:
1098 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1027 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1099 return p
1028 return p
1100 except:
1029 except:
1101 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1030 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1102 raise
1031 raise
1103
1032
1104 def changegroup(self, nodes):
1033 def changegroup(self, nodes):
1105 n = " ".join(map(hex, nodes))
1034 n = " ".join(map(hex, nodes))
1106 zd = zlib.decompressobj()
1035 zd = zlib.decompressobj()
1107 f = self.do_cmd("changegroup", roots=n)
1036 f = self.do_cmd("changegroup", roots=n)
1108 bytes = 0
1037 bytes = 0
1109 while 1:
1038 while 1:
1110 d = f.read(4096)
1039 d = f.read(4096)
1111 bytes += len(d)
1040 bytes += len(d)
1112 if not d:
1041 if not d:
1113 yield zd.flush()
1042 yield zd.flush()
1114 break
1043 break
1115 yield zd.decompress(d)
1044 yield zd.decompress(d)
1116 self.ui.note("%d bytes of data transfered\n" % bytes)
1045 self.ui.note("%d bytes of data transfered\n" % bytes)
1117
1046
1118 def repository(ui, path=None, create=0):
1047 def repository(ui, path=None, create=0):
1119 if path and path[:7] == "http://":
1048 if path and path[:7] == "http://":
1120 return remoterepository(ui, path)
1049 return remoterepository(ui, path)
1121 if path and path[:5] == "hg://":
1050 if path and path[:5] == "hg://":
1122 return remoterepository(ui, path.replace("hg://", "http://"))
1051 return remoterepository(ui, path.replace("hg://", "http://"))
1123 if path and path[:11] == "old-http://":
1052 if path and path[:11] == "old-http://":
1124 return localrepository(ui, path.replace("old-http://", "http://"))
1053 return localrepository(ui, path.replace("old-http://", "http://"))
1125 else:
1054 else:
1126 return localrepository(ui, path, create)
1055 return localrepository(ui, path, create)
1127
1056
1128 class httprangereader:
1057 class httprangereader:
1129 def __init__(self, url):
1058 def __init__(self, url):
1130 self.url = url
1059 self.url = url
1131 self.pos = 0
1060 self.pos = 0
1132 def seek(self, pos):
1061 def seek(self, pos):
1133 self.pos = pos
1062 self.pos = pos
1134 def read(self, bytes=None):
1063 def read(self, bytes=None):
1135 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
1064 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
1136 urllib2.install_opener(opener)
1065 urllib2.install_opener(opener)
1137 req = urllib2.Request(self.url)
1066 req = urllib2.Request(self.url)
1138 end = ''
1067 end = ''
1139 if bytes: end = self.pos + bytes
1068 if bytes: end = self.pos + bytes
1140 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
1069 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
1141 f = urllib2.urlopen(req)
1070 f = urllib2.urlopen(req)
1142 return f.read()
1071 return f.read()
General Comments 0
You need to be logged in to leave comments. Login now