##// END OF EJS Templates
[PATCH] Merging identical changes from another branch...
mpm@selenic.com -
r407:0e0d0670 default
parent child Browse files
Show More
@@ -0,0 +1,85 b''
1 #!/bin/sh -x
2
3 cat <<'EOF' > merge
4 #!/bin/sh
5 echo merging for `basename $1`
6 EOF
7 chmod +x merge
8
9 mkdir t
10 cd t
11 hg init
12 echo This is file a1 > a
13 hg add a
14 hg commit -t "commit #0" -d "0 0" -u user
15 echo This is file b1 > b
16 hg add b
17 hg commit -t "commit #1" -d "0 0" -u user
18
19 hg update 0
20 echo This is file c1 > c
21 hg add c
22 hg commit -t "commit #2" -d "0 0" -u user
23 echo This is file b1 > b
24 env HGMERGE=../merge hg update -m 1
25 # no merges expected
26 cd ..; /bin/rm -rf t
27
28 mkdir t
29 cd t
30 hg init
31 echo This is file a1 > a
32 hg add a
33 hg commit -t "commit #0" -d "0 0" -u user
34 echo This is file b1 > b
35 hg add b
36 hg commit -t "commit #1" -d "0 0" -u user
37
38 hg update 0
39 echo This is file c1 > c
40 hg add c
41 hg commit -t "commit #2" -d "0 0" -u user
42 echo This is file b2 > b
43 env HGMERGE=../merge hg update -m 1
44 # merge of b expected
45 cd ..; /bin/rm -rf t
46
47 mkdir t
48 cd t
49 hg init
50 echo This is file a1 > a
51 hg add a
52 hg commit -t "commit #0" -d "0 0" -u user
53 echo This is file b1 > b
54 hg add b
55 hg commit -t "commit #1" -d "0 0" -u user
56 echo This is file b22 > b
57 hg commit -t "commit #2" -d "0 0" -u user
58 hg update 1
59 echo This is file c1 > c
60 hg add c
61 hg commit -t "commit #3" -d "0 0" -u user
62 echo This is file b22 > b
63 env HGMERGE=../merge hg update -m 2
64 # no merges expected
65 cd ..; /bin/rm -rf t
66
67 mkdir t
68 cd t
69 hg init
70 echo This is file a1 > a
71 hg add a
72 hg commit -t "commit #0" -d "0 0" -u user
73 echo This is file b1 > b
74 hg add b
75 hg commit -t "commit #1" -d "0 0" -u user
76 echo This is file b22 > b
77 hg commit -t "commit #2" -d "0 0" -u user
78 hg update 1
79 echo This is file c1 > c
80 hg add c
81 hg commit -t "commit #3" -d "0 0" -u user
82 echo This is file b33 > b
83 env HGMERGE=../merge hg update -m 2
84 # merge of b expected
85 cd ..; /bin/rm -rf t
@@ -0,0 +1,78 b''
1 + cat
2 + chmod +x merge
3 + mkdir t
4 + cd t
5 + hg init
6 + echo This is file a1
7 + hg add a
8 + hg commit -t 'commit #0' -d '0 0' -u user
9 + echo This is file b1
10 + hg add b
11 + hg commit -t 'commit #1' -d '0 0' -u user
12 + hg update 0
13 + echo This is file c1
14 + hg add c
15 + hg commit -t 'commit #2' -d '0 0' -u user
16 + echo This is file b1
17 + env HGMERGE=../merge hg update -m 1
18 + cd ..
19 + /bin/rm -rf t
20 + mkdir t
21 + cd t
22 + hg init
23 + echo This is file a1
24 + hg add a
25 + hg commit -t 'commit #0' -d '0 0' -u user
26 + echo This is file b1
27 + hg add b
28 + hg commit -t 'commit #1' -d '0 0' -u user
29 + hg update 0
30 + echo This is file c1
31 + hg add c
32 + hg commit -t 'commit #2' -d '0 0' -u user
33 + echo This is file b2
34 + env HGMERGE=../merge hg update -m 1
35 merging for b
36 merging b
37 + cd ..
38 + /bin/rm -rf t
39 + mkdir t
40 + cd t
41 + hg init
42 + echo This is file a1
43 + hg add a
44 + hg commit -t 'commit #0' -d '0 0' -u user
45 + echo This is file b1
46 + hg add b
47 + hg commit -t 'commit #1' -d '0 0' -u user
48 + echo This is file b22
49 + hg commit -t 'commit #2' -d '0 0' -u user
50 + hg update 1
51 + echo This is file c1
52 + hg add c
53 + hg commit -t 'commit #3' -d '0 0' -u user
54 + echo This is file b22
55 + env HGMERGE=../merge hg update -m 2
56 + cd ..
57 + /bin/rm -rf t
58 + mkdir t
59 + cd t
60 + hg init
61 + echo This is file a1
62 + hg add a
63 + hg commit -t 'commit #0' -d '0 0' -u user
64 + echo This is file b1
65 + hg add b
66 + hg commit -t 'commit #1' -d '0 0' -u user
67 + echo This is file b22
68 + hg commit -t 'commit #2' -d '0 0' -u user
69 + hg update 1
70 + echo This is file c1
71 + hg add c
72 + hg commit -t 'commit #3' -d '0 0' -u user
73 + echo This is file b33
74 + env HGMERGE=../merge hg update -m 2
75 merging for b
76 merging b
77 + cd ..
78 + /bin/rm -rf t
@@ -1,1428 +1,1437 b''
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, os
8 import sys, struct, os
9 from revlog import *
9 from revlog import *
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "re lock urllib urllib2 transaction time socket")
11 demandload(globals(), "re lock urllib urllib2 transaction time socket")
12 demandload(globals(), "tempfile httprangereader difflib")
12 demandload(globals(), "tempfile httprangereader difflib")
13
13
14 def is_exec(f):
14 def is_exec(f):
15 return (os.stat(f).st_mode & 0100 != 0)
15 return (os.stat(f).st_mode & 0100 != 0)
16
16
17 def set_exec(f, mode):
17 def set_exec(f, mode):
18 s = os.stat(f).st_mode
18 s = os.stat(f).st_mode
19 if (s & 0100 != 0) == mode:
19 if (s & 0100 != 0) == mode:
20 return
20 return
21 if mode:
21 if mode:
22 # Turn on +x for every +r bit when making a file executable
22 # Turn on +x for every +r bit when making a file executable
23 # and obey umask.
23 # and obey umask.
24 umask = os.umask(0)
24 umask = os.umask(0)
25 os.umask(umask)
25 os.umask(umask)
26 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
26 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
27 else:
27 else:
28 os.chmod(f, s & 0666)
28 os.chmod(f, s & 0666)
29
29
30 class filelog(revlog):
30 class filelog(revlog):
31 def __init__(self, opener, path):
31 def __init__(self, opener, path):
32 revlog.__init__(self, opener,
32 revlog.__init__(self, opener,
33 os.path.join("data", path + ".i"),
33 os.path.join("data", path + ".i"),
34 os.path.join("data", path + ".d"))
34 os.path.join("data", path + ".d"))
35
35
36 def read(self, node):
36 def read(self, node):
37 t = self.revision(node)
37 t = self.revision(node)
38 if t[:2] != '\1\n':
38 if t[:2] != '\1\n':
39 return t
39 return t
40 s = t.find('\1\n', 2)
40 s = t.find('\1\n', 2)
41 return t[s+2:]
41 return t[s+2:]
42
42
43 def readmeta(self, node):
43 def readmeta(self, node):
44 t = self.revision(node)
44 t = self.revision(node)
45 if t[:2] != '\1\n':
45 if t[:2] != '\1\n':
46 return t
46 return t
47 s = t.find('\1\n', 2)
47 s = t.find('\1\n', 2)
48 mt = t[2:s]
48 mt = t[2:s]
49 for l in mt.splitlines():
49 for l in mt.splitlines():
50 k, v = l.split(": ", 1)
50 k, v = l.split(": ", 1)
51 m[k] = v
51 m[k] = v
52 return m
52 return m
53
53
54 def add(self, text, meta, transaction, link, p1=None, p2=None):
54 def add(self, text, meta, transaction, link, p1=None, p2=None):
55 if meta or text[:2] == '\1\n':
55 if meta or text[:2] == '\1\n':
56 mt = ""
56 mt = ""
57 if meta:
57 if meta:
58 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
58 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
59 text = "\1\n" + "".join(mt) + "\1\n" + text
59 text = "\1\n" + "".join(mt) + "\1\n" + text
60 return self.addrevision(text, transaction, link, p1, p2)
60 return self.addrevision(text, transaction, link, p1, p2)
61
61
62 def annotate(self, node):
62 def annotate(self, node):
63
63
64 def decorate(text, rev):
64 def decorate(text, rev):
65 return [(rev, l) for l in text.splitlines(1)]
65 return [(rev, l) for l in text.splitlines(1)]
66
66
67 def strip(annotation):
67 def strip(annotation):
68 return [e[1] for e in annotation]
68 return [e[1] for e in annotation]
69
69
70 def pair(parent, child):
70 def pair(parent, child):
71 new = []
71 new = []
72 sm = difflib.SequenceMatcher(None, strip(parent), strip(child))
72 sm = difflib.SequenceMatcher(None, strip(parent), strip(child))
73 for o, m, n, s, t in sm.get_opcodes():
73 for o, m, n, s, t in sm.get_opcodes():
74 if o == 'equal':
74 if o == 'equal':
75 new += parent[m:n]
75 new += parent[m:n]
76 else:
76 else:
77 new += child[s:t]
77 new += child[s:t]
78 return new
78 return new
79
79
80 # find all ancestors
80 # find all ancestors
81 needed = {node:1}
81 needed = {node:1}
82 visit = [node]
82 visit = [node]
83 while visit:
83 while visit:
84 n = visit.pop(0)
84 n = visit.pop(0)
85 for p in self.parents(n):
85 for p in self.parents(n):
86 if p not in needed:
86 if p not in needed:
87 needed[p] = 1
87 needed[p] = 1
88 visit.append(p)
88 visit.append(p)
89 else:
89 else:
90 # count how many times we'll use this
90 # count how many times we'll use this
91 needed[p] += 1
91 needed[p] += 1
92
92
93 # sort by revision which is a topological order
93 # sort by revision which is a topological order
94 visit = needed.keys()
94 visit = needed.keys()
95 visit = [ (self.rev(n), n) for n in visit ]
95 visit = [ (self.rev(n), n) for n in visit ]
96 visit.sort()
96 visit.sort()
97 visit = [ p[1] for p in visit ]
97 visit = [ p[1] for p in visit ]
98 hist = {}
98 hist = {}
99
99
100 for n in visit:
100 for n in visit:
101 curr = decorate(self.read(n), self.linkrev(n))
101 curr = decorate(self.read(n), self.linkrev(n))
102 for p in self.parents(n):
102 for p in self.parents(n):
103 if p != nullid:
103 if p != nullid:
104 curr = pair(hist[p], curr)
104 curr = pair(hist[p], curr)
105 # trim the history of unneeded revs
105 # trim the history of unneeded revs
106 needed[p] -= 1
106 needed[p] -= 1
107 if not needed[p]:
107 if not needed[p]:
108 del hist[p]
108 del hist[p]
109 hist[n] = curr
109 hist[n] = curr
110
110
111 return hist[n]
111 return hist[n]
112
112
113 class manifest(revlog):
113 class manifest(revlog):
114 def __init__(self, opener):
114 def __init__(self, opener):
115 self.mapcache = None
115 self.mapcache = None
116 self.listcache = None
116 self.listcache = None
117 self.addlist = None
117 self.addlist = None
118 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
118 revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
119
119
120 def read(self, node):
120 def read(self, node):
121 if node == nullid: return {} # don't upset local cache
121 if node == nullid: return {} # don't upset local cache
122 if self.mapcache and self.mapcache[0] == node:
122 if self.mapcache and self.mapcache[0] == node:
123 return self.mapcache[1].copy()
123 return self.mapcache[1].copy()
124 text = self.revision(node)
124 text = self.revision(node)
125 map = {}
125 map = {}
126 flag = {}
126 flag = {}
127 self.listcache = (text, text.splitlines(1))
127 self.listcache = (text, text.splitlines(1))
128 for l in self.listcache[1]:
128 for l in self.listcache[1]:
129 (f, n) = l.split('\0')
129 (f, n) = l.split('\0')
130 map[f] = bin(n[:40])
130 map[f] = bin(n[:40])
131 flag[f] = (n[40:-1] == "x")
131 flag[f] = (n[40:-1] == "x")
132 self.mapcache = (node, map, flag)
132 self.mapcache = (node, map, flag)
133 return map
133 return map
134
134
135 def readflags(self, node):
135 def readflags(self, node):
136 if node == nullid: return {} # don't upset local cache
136 if node == nullid: return {} # don't upset local cache
137 if not self.mapcache or self.mapcache[0] != node:
137 if not self.mapcache or self.mapcache[0] != node:
138 self.read(node)
138 self.read(node)
139 return self.mapcache[2]
139 return self.mapcache[2]
140
140
141 def diff(self, a, b):
141 def diff(self, a, b):
142 # this is sneaky, as we're not actually using a and b
142 # this is sneaky, as we're not actually using a and b
143 if self.listcache and self.addlist and self.listcache[0] == a:
143 if self.listcache and self.addlist and self.listcache[0] == a:
144 d = mdiff.diff(self.listcache[1], self.addlist, 1)
144 d = mdiff.diff(self.listcache[1], self.addlist, 1)
145 if mdiff.patch(a, d) != b:
145 if mdiff.patch(a, d) != b:
146 sys.stderr.write("*** sortdiff failed, falling back ***\n")
146 sys.stderr.write("*** sortdiff failed, falling back ***\n")
147 return mdiff.textdiff(a, b)
147 return mdiff.textdiff(a, b)
148 return d
148 return d
149 else:
149 else:
150 return mdiff.textdiff(a, b)
150 return mdiff.textdiff(a, b)
151
151
152 def add(self, map, flags, transaction, link, p1=None, p2=None):
152 def add(self, map, flags, transaction, link, p1=None, p2=None):
153 files = map.keys()
153 files = map.keys()
154 files.sort()
154 files.sort()
155
155
156 self.addlist = ["%s\000%s%s\n" %
156 self.addlist = ["%s\000%s%s\n" %
157 (f, hex(map[f]), flags[f] and "x" or '')
157 (f, hex(map[f]), flags[f] and "x" or '')
158 for f in files]
158 for f in files]
159 text = "".join(self.addlist)
159 text = "".join(self.addlist)
160
160
161 n = self.addrevision(text, transaction, link, p1, p2)
161 n = self.addrevision(text, transaction, link, p1, p2)
162 self.mapcache = (n, map, flags)
162 self.mapcache = (n, map, flags)
163 self.listcache = (text, self.addlist)
163 self.listcache = (text, self.addlist)
164 self.addlist = None
164 self.addlist = None
165
165
166 return n
166 return n
167
167
168 class changelog(revlog):
168 class changelog(revlog):
169 def __init__(self, opener):
169 def __init__(self, opener):
170 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
170 revlog.__init__(self, opener, "00changelog.i", "00changelog.d")
171
171
172 def extract(self, text):
172 def extract(self, text):
173 if not text:
173 if not text:
174 return (nullid, "", "0", [], "")
174 return (nullid, "", "0", [], "")
175 last = text.index("\n\n")
175 last = text.index("\n\n")
176 desc = text[last + 2:]
176 desc = text[last + 2:]
177 l = text[:last].splitlines()
177 l = text[:last].splitlines()
178 manifest = bin(l[0])
178 manifest = bin(l[0])
179 user = l[1]
179 user = l[1]
180 date = l[2]
180 date = l[2]
181 files = l[3:]
181 files = l[3:]
182 return (manifest, user, date, files, desc)
182 return (manifest, user, date, files, desc)
183
183
184 def read(self, node):
184 def read(self, node):
185 return self.extract(self.revision(node))
185 return self.extract(self.revision(node))
186
186
187 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
187 def add(self, manifest, list, desc, transaction, p1=None, p2=None,
188 user=None, date=None):
188 user=None, date=None):
189 user = (user or
189 user = (user or
190 os.environ.get("HGUSER") or
190 os.environ.get("HGUSER") or
191 os.environ.get("EMAIL") or
191 os.environ.get("EMAIL") or
192 os.environ.get("LOGNAME", "unknown") + '@' + socket.getfqdn())
192 os.environ.get("LOGNAME", "unknown") + '@' + socket.getfqdn())
193 date = date or "%d %d" % (time.time(), time.timezone)
193 date = date or "%d %d" % (time.time(), time.timezone)
194 list.sort()
194 list.sort()
195 l = [hex(manifest), user, date] + list + ["", desc]
195 l = [hex(manifest), user, date] + list + ["", desc]
196 text = "\n".join(l)
196 text = "\n".join(l)
197 return self.addrevision(text, transaction, self.count(), p1, p2)
197 return self.addrevision(text, transaction, self.count(), p1, p2)
198
198
199 class dirstate:
199 class dirstate:
200 def __init__(self, opener, ui, root):
200 def __init__(self, opener, ui, root):
201 self.opener = opener
201 self.opener = opener
202 self.root = root
202 self.root = root
203 self.dirty = 0
203 self.dirty = 0
204 self.ui = ui
204 self.ui = ui
205 self.map = None
205 self.map = None
206 self.pl = None
206 self.pl = None
207 self.copies = {}
207 self.copies = {}
208
208
209 def __del__(self):
209 def __del__(self):
210 if self.dirty:
210 if self.dirty:
211 self.write()
211 self.write()
212
212
213 def __getitem__(self, key):
213 def __getitem__(self, key):
214 try:
214 try:
215 return self.map[key]
215 return self.map[key]
216 except TypeError:
216 except TypeError:
217 self.read()
217 self.read()
218 return self[key]
218 return self[key]
219
219
220 def __contains__(self, key):
220 def __contains__(self, key):
221 if not self.map: self.read()
221 if not self.map: self.read()
222 return key in self.map
222 return key in self.map
223
223
224 def parents(self):
224 def parents(self):
225 if not self.pl:
225 if not self.pl:
226 self.read()
226 self.read()
227 return self.pl
227 return self.pl
228
228
229 def setparents(self, p1, p2 = nullid):
229 def setparents(self, p1, p2 = nullid):
230 self.dirty = 1
230 self.dirty = 1
231 self.pl = p1, p2
231 self.pl = p1, p2
232
232
233 def state(self, key):
233 def state(self, key):
234 try:
234 try:
235 return self[key][0]
235 return self[key][0]
236 except KeyError:
236 except KeyError:
237 return "?"
237 return "?"
238
238
239 def read(self):
239 def read(self):
240 if self.map is not None: return self.map
240 if self.map is not None: return self.map
241
241
242 self.map = {}
242 self.map = {}
243 self.pl = [nullid, nullid]
243 self.pl = [nullid, nullid]
244 try:
244 try:
245 st = self.opener("dirstate").read()
245 st = self.opener("dirstate").read()
246 if not st: return
246 if not st: return
247 except: return
247 except: return
248
248
249 self.pl = [st[:20], st[20: 40]]
249 self.pl = [st[:20], st[20: 40]]
250
250
251 pos = 40
251 pos = 40
252 while pos < len(st):
252 while pos < len(st):
253 e = struct.unpack(">cllll", st[pos:pos+17])
253 e = struct.unpack(">cllll", st[pos:pos+17])
254 l = e[4]
254 l = e[4]
255 pos += 17
255 pos += 17
256 f = st[pos:pos + l]
256 f = st[pos:pos + l]
257 if '\0' in f:
257 if '\0' in f:
258 f, c = f.split('\0')
258 f, c = f.split('\0')
259 self.copies[f] = c
259 self.copies[f] = c
260 self.map[f] = e[:4]
260 self.map[f] = e[:4]
261 pos += l
261 pos += l
262
262
263 def copy(self, source, dest):
263 def copy(self, source, dest):
264 self.read()
264 self.read()
265 self.dirty = 1
265 self.dirty = 1
266 self.copies[dest] = source
266 self.copies[dest] = source
267
267
268 def copied(self, file):
268 def copied(self, file):
269 return self.copies.get(file, None)
269 return self.copies.get(file, None)
270
270
271 def update(self, files, state):
271 def update(self, files, state):
272 ''' current states:
272 ''' current states:
273 n normal
273 n normal
274 m needs merging
274 m needs merging
275 r marked for removal
275 r marked for removal
276 a marked for addition'''
276 a marked for addition'''
277
277
278 if not files: return
278 if not files: return
279 self.read()
279 self.read()
280 self.dirty = 1
280 self.dirty = 1
281 for f in files:
281 for f in files:
282 if state == "r":
282 if state == "r":
283 self.map[f] = ('r', 0, 0, 0)
283 self.map[f] = ('r', 0, 0, 0)
284 else:
284 else:
285 s = os.stat(os.path.join(self.root, f))
285 s = os.stat(os.path.join(self.root, f))
286 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
286 self.map[f] = (state, s.st_mode, s.st_size, s.st_mtime)
287
287
288 def forget(self, files):
288 def forget(self, files):
289 if not files: return
289 if not files: return
290 self.read()
290 self.read()
291 self.dirty = 1
291 self.dirty = 1
292 for f in files:
292 for f in files:
293 try:
293 try:
294 del self.map[f]
294 del self.map[f]
295 except KeyError:
295 except KeyError:
296 self.ui.warn("not in dirstate: %s!\n" % f)
296 self.ui.warn("not in dirstate: %s!\n" % f)
297 pass
297 pass
298
298
299 def clear(self):
299 def clear(self):
300 self.map = {}
300 self.map = {}
301 self.dirty = 1
301 self.dirty = 1
302
302
303 def write(self):
303 def write(self):
304 st = self.opener("dirstate", "w")
304 st = self.opener("dirstate", "w")
305 st.write("".join(self.pl))
305 st.write("".join(self.pl))
306 for f, e in self.map.items():
306 for f, e in self.map.items():
307 c = self.copied(f)
307 c = self.copied(f)
308 if c:
308 if c:
309 f = f + "\0" + c
309 f = f + "\0" + c
310 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
310 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
311 st.write(e + f)
311 st.write(e + f)
312 self.dirty = 0
312 self.dirty = 0
313
313
314 def dup(self):
314 def dup(self):
315 self.read()
315 self.read()
316 return self.map.copy()
316 return self.map.copy()
317
317
318 # used to avoid circular references so destructors work
318 # used to avoid circular references so destructors work
319 def opener(base):
319 def opener(base):
320 p = base
320 p = base
321 def o(path, mode="r"):
321 def o(path, mode="r"):
322 if p[:7] == "http://":
322 if p[:7] == "http://":
323 f = os.path.join(p, urllib.quote(path))
323 f = os.path.join(p, urllib.quote(path))
324 return httprangereader.httprangereader(f)
324 return httprangereader.httprangereader(f)
325
325
326 f = os.path.join(p, path)
326 f = os.path.join(p, path)
327
327
328 mode += "b" # for that other OS
328 mode += "b" # for that other OS
329
329
330 if mode[0] != "r":
330 if mode[0] != "r":
331 try:
331 try:
332 s = os.stat(f)
332 s = os.stat(f)
333 except OSError:
333 except OSError:
334 d = os.path.dirname(f)
334 d = os.path.dirname(f)
335 if not os.path.isdir(d):
335 if not os.path.isdir(d):
336 os.makedirs(d)
336 os.makedirs(d)
337 else:
337 else:
338 if s.st_nlink > 1:
338 if s.st_nlink > 1:
339 file(f + ".tmp", "w").write(file(f).read())
339 file(f + ".tmp", "w").write(file(f).read())
340 os.rename(f+".tmp", f)
340 os.rename(f+".tmp", f)
341
341
342 return file(f, mode)
342 return file(f, mode)
343
343
344 return o
344 return o
345
345
346 class localrepository:
346 class localrepository:
347 def __init__(self, ui, path=None, create=0):
347 def __init__(self, ui, path=None, create=0):
348 self.remote = 0
348 self.remote = 0
349 if path and path[:7] == "http://":
349 if path and path[:7] == "http://":
350 self.remote = 1
350 self.remote = 1
351 self.path = path
351 self.path = path
352 else:
352 else:
353 if not path:
353 if not path:
354 p = os.getcwd()
354 p = os.getcwd()
355 while not os.path.isdir(os.path.join(p, ".hg")):
355 while not os.path.isdir(os.path.join(p, ".hg")):
356 p = os.path.dirname(p)
356 p = os.path.dirname(p)
357 if p == "/": raise "No repo found"
357 if p == "/": raise "No repo found"
358 path = p
358 path = p
359 self.path = os.path.join(path, ".hg")
359 self.path = os.path.join(path, ".hg")
360
360
361 if not create and not os.path.isdir(self.path):
361 if not create and not os.path.isdir(self.path):
362 raise "repository %s not found" % self.path
362 raise "repository %s not found" % self.path
363
363
364 self.root = path
364 self.root = path
365 self.ui = ui
365 self.ui = ui
366
366
367 if create:
367 if create:
368 os.mkdir(self.path)
368 os.mkdir(self.path)
369 os.mkdir(self.join("data"))
369 os.mkdir(self.join("data"))
370
370
371 self.opener = opener(self.path)
371 self.opener = opener(self.path)
372 self.wopener = opener(self.root)
372 self.wopener = opener(self.root)
373 self.manifest = manifest(self.opener)
373 self.manifest = manifest(self.opener)
374 self.changelog = changelog(self.opener)
374 self.changelog = changelog(self.opener)
375 self.ignorelist = None
375 self.ignorelist = None
376 self.tagscache = None
376 self.tagscache = None
377 self.nodetagscache = None
377 self.nodetagscache = None
378
378
379 if not self.remote:
379 if not self.remote:
380 self.dirstate = dirstate(self.opener, ui, self.root)
380 self.dirstate = dirstate(self.opener, ui, self.root)
381 try:
381 try:
382 self.ui.readconfig(self.opener("hgrc"))
382 self.ui.readconfig(self.opener("hgrc"))
383 except IOError: pass
383 except IOError: pass
384
384
385 def ignore(self, f):
385 def ignore(self, f):
386 if self.ignorelist is None:
386 if self.ignorelist is None:
387 self.ignorelist = []
387 self.ignorelist = []
388 try:
388 try:
389 l = self.wfile(".hgignore")
389 l = self.wfile(".hgignore")
390 for pat in l:
390 for pat in l:
391 if pat != "\n":
391 if pat != "\n":
392 self.ignorelist.append(re.compile(pat[:-1]))
392 self.ignorelist.append(re.compile(pat[:-1]))
393 except IOError: pass
393 except IOError: pass
394 for pat in self.ignorelist:
394 for pat in self.ignorelist:
395 if pat.search(f): return True
395 if pat.search(f): return True
396 return False
396 return False
397
397
398 def tags(self):
398 def tags(self):
399 '''return a mapping of tag to node'''
399 '''return a mapping of tag to node'''
400 if not self.tagscache:
400 if not self.tagscache:
401 self.tagscache = {}
401 self.tagscache = {}
402 try:
402 try:
403 # read each head of the tags file, ending with the tip
403 # read each head of the tags file, ending with the tip
404 # and add each tag found to the map, with "newer" ones
404 # and add each tag found to the map, with "newer" ones
405 # taking precedence
405 # taking precedence
406 fl = self.file(".hgtags")
406 fl = self.file(".hgtags")
407 h = fl.heads()
407 h = fl.heads()
408 h.reverse()
408 h.reverse()
409 for r in h:
409 for r in h:
410 for l in fl.revision(r).splitlines():
410 for l in fl.revision(r).splitlines():
411 if l:
411 if l:
412 n, k = l.split(" ", 1)
412 n, k = l.split(" ", 1)
413 self.tagscache[k.strip()] = bin(n)
413 self.tagscache[k.strip()] = bin(n)
414 except KeyError: pass
414 except KeyError: pass
415 self.tagscache['tip'] = self.changelog.tip()
415 self.tagscache['tip'] = self.changelog.tip()
416
416
417 return self.tagscache
417 return self.tagscache
418
418
419 def tagslist(self):
419 def tagslist(self):
420 '''return a list of tags ordered by revision'''
420 '''return a list of tags ordered by revision'''
421 l = []
421 l = []
422 for t,n in self.tags().items():
422 for t,n in self.tags().items():
423 try:
423 try:
424 r = self.changelog.rev(n)
424 r = self.changelog.rev(n)
425 except:
425 except:
426 r = -2 # sort to the beginning of the list if unknown
426 r = -2 # sort to the beginning of the list if unknown
427 l.append((r,t,n))
427 l.append((r,t,n))
428 l.sort()
428 l.sort()
429 return [(t,n) for r,t,n in l]
429 return [(t,n) for r,t,n in l]
430
430
431 def nodetags(self, node):
431 def nodetags(self, node):
432 '''return the tags associated with a node'''
432 '''return the tags associated with a node'''
433 if not self.nodetagscache:
433 if not self.nodetagscache:
434 self.nodetagscache = {}
434 self.nodetagscache = {}
435 for t,n in self.tags().items():
435 for t,n in self.tags().items():
436 self.nodetagscache.setdefault(n,[]).append(t)
436 self.nodetagscache.setdefault(n,[]).append(t)
437 return self.nodetagscache.get(node, [])
437 return self.nodetagscache.get(node, [])
438
438
439 def lookup(self, key):
439 def lookup(self, key):
440 try:
440 try:
441 return self.tags()[key]
441 return self.tags()[key]
442 except KeyError:
442 except KeyError:
443 return self.changelog.lookup(key)
443 return self.changelog.lookup(key)
444
444
445 def join(self, f):
445 def join(self, f):
446 return os.path.join(self.path, f)
446 return os.path.join(self.path, f)
447
447
448 def wjoin(self, f):
448 def wjoin(self, f):
449 return os.path.join(self.root, f)
449 return os.path.join(self.root, f)
450
450
451 def file(self, f):
451 def file(self, f):
452 if f[0] == '/': f = f[1:]
452 if f[0] == '/': f = f[1:]
453 return filelog(self.opener, f)
453 return filelog(self.opener, f)
454
454
455 def wfile(self, f, mode='r'):
455 def wfile(self, f, mode='r'):
456 return self.wopener(f, mode)
456 return self.wopener(f, mode)
457
457
458 def transaction(self):
458 def transaction(self):
459 # save dirstate for undo
459 # save dirstate for undo
460 try:
460 try:
461 ds = self.opener("dirstate").read()
461 ds = self.opener("dirstate").read()
462 except IOError:
462 except IOError:
463 ds = ""
463 ds = ""
464 self.opener("undo.dirstate", "w").write(ds)
464 self.opener("undo.dirstate", "w").write(ds)
465
465
466 return transaction.transaction(self.opener, self.join("journal"),
466 return transaction.transaction(self.opener, self.join("journal"),
467 self.join("undo"))
467 self.join("undo"))
468
468
469 def recover(self):
469 def recover(self):
470 lock = self.lock()
470 lock = self.lock()
471 if os.path.exists(self.join("recover")):
471 if os.path.exists(self.join("recover")):
472 self.ui.status("attempting to rollback interrupted transaction\n")
472 self.ui.status("attempting to rollback interrupted transaction\n")
473 return transaction.rollback(self.opener, self.join("recover"))
473 return transaction.rollback(self.opener, self.join("recover"))
474 else:
474 else:
475 self.ui.warn("no interrupted transaction available\n")
475 self.ui.warn("no interrupted transaction available\n")
476
476
477 def undo(self):
477 def undo(self):
478 lock = self.lock()
478 lock = self.lock()
479 if os.path.exists(self.join("undo")):
479 if os.path.exists(self.join("undo")):
480 self.ui.status("attempting to rollback last transaction\n")
480 self.ui.status("attempting to rollback last transaction\n")
481 transaction.rollback(self.opener, self.join("undo"))
481 transaction.rollback(self.opener, self.join("undo"))
482 self.dirstate = None
482 self.dirstate = None
483 os.rename(self.join("undo.dirstate"), self.join("dirstate"))
483 os.rename(self.join("undo.dirstate"), self.join("dirstate"))
484 self.dirstate = dirstate(self.opener, self.ui, self.root)
484 self.dirstate = dirstate(self.opener, self.ui, self.root)
485 else:
485 else:
486 self.ui.warn("no undo information available\n")
486 self.ui.warn("no undo information available\n")
487
487
488 def lock(self, wait = 1):
488 def lock(self, wait = 1):
489 try:
489 try:
490 return lock.lock(self.join("lock"), 0)
490 return lock.lock(self.join("lock"), 0)
491 except lock.LockHeld, inst:
491 except lock.LockHeld, inst:
492 if wait:
492 if wait:
493 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
493 self.ui.warn("waiting for lock held by %s\n" % inst.args[0])
494 return lock.lock(self.join("lock"), wait)
494 return lock.lock(self.join("lock"), wait)
495 raise inst
495 raise inst
496
496
497 def rawcommit(self, files, text, user, date, p1=None, p2=None):
497 def rawcommit(self, files, text, user, date, p1=None, p2=None):
498 p1 = p1 or self.dirstate.parents()[0] or nullid
498 p1 = p1 or self.dirstate.parents()[0] or nullid
499 p2 = p2 or self.dirstate.parents()[1] or nullid
499 p2 = p2 or self.dirstate.parents()[1] or nullid
500 c1 = self.changelog.read(p1)
500 c1 = self.changelog.read(p1)
501 c2 = self.changelog.read(p2)
501 c2 = self.changelog.read(p2)
502 m1 = self.manifest.read(c1[0])
502 m1 = self.manifest.read(c1[0])
503 mf1 = self.manifest.readflags(c1[0])
503 mf1 = self.manifest.readflags(c1[0])
504 m2 = self.manifest.read(c2[0])
504 m2 = self.manifest.read(c2[0])
505
505
506 tr = self.transaction()
506 tr = self.transaction()
507 mm = m1.copy()
507 mm = m1.copy()
508 mfm = mf1.copy()
508 mfm = mf1.copy()
509 linkrev = self.changelog.count()
509 linkrev = self.changelog.count()
510 self.dirstate.setparents(p1, p2)
510 self.dirstate.setparents(p1, p2)
511 for f in files:
511 for f in files:
512 try:
512 try:
513 t = self.wfile(f).read()
513 t = self.wfile(f).read()
514 tm = is_exec(self.wjoin(f))
514 tm = is_exec(self.wjoin(f))
515 r = self.file(f)
515 r = self.file(f)
516 mfm[f] = tm
516 mfm[f] = tm
517 mm[f] = r.add(t, {}, tr, linkrev,
517 mm[f] = r.add(t, {}, tr, linkrev,
518 m1.get(f, nullid), m2.get(f, nullid))
518 m1.get(f, nullid), m2.get(f, nullid))
519 self.dirstate.update([f], "n")
519 self.dirstate.update([f], "n")
520 except IOError:
520 except IOError:
521 try:
521 try:
522 del mm[f]
522 del mm[f]
523 del mfm[f]
523 del mfm[f]
524 self.dirstate.forget([f])
524 self.dirstate.forget([f])
525 except:
525 except:
526 # deleted from p2?
526 # deleted from p2?
527 pass
527 pass
528
528
529 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
529 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
530 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
530 n = self.changelog.add(mnode, files, text, tr, p1, p2, user, date)
531 tr.close()
531 tr.close()
532
532
533 def commit(self, files = None, text = "", user = None, date = None):
533 def commit(self, files = None, text = "", user = None, date = None):
534 commit = []
534 commit = []
535 remove = []
535 remove = []
536 if files:
536 if files:
537 for f in files:
537 for f in files:
538 s = self.dirstate.state(f)
538 s = self.dirstate.state(f)
539 if s in 'nmai':
539 if s in 'nmai':
540 commit.append(f)
540 commit.append(f)
541 elif s == 'r':
541 elif s == 'r':
542 remove.append(f)
542 remove.append(f)
543 else:
543 else:
544 self.ui.warn("%s not tracked!\n" % f)
544 self.ui.warn("%s not tracked!\n" % f)
545 else:
545 else:
546 (c, a, d, u) = self.diffdir(self.root)
546 (c, a, d, u) = self.diffdir(self.root)
547 commit = c + a
547 commit = c + a
548 remove = d
548 remove = d
549
549
550 if not commit and not remove:
550 if not commit and not remove:
551 self.ui.status("nothing changed\n")
551 self.ui.status("nothing changed\n")
552 return
552 return
553
553
554 p1, p2 = self.dirstate.parents()
554 p1, p2 = self.dirstate.parents()
555 c1 = self.changelog.read(p1)
555 c1 = self.changelog.read(p1)
556 c2 = self.changelog.read(p2)
556 c2 = self.changelog.read(p2)
557 m1 = self.manifest.read(c1[0])
557 m1 = self.manifest.read(c1[0])
558 mf1 = self.manifest.readflags(c1[0])
558 mf1 = self.manifest.readflags(c1[0])
559 m2 = self.manifest.read(c2[0])
559 m2 = self.manifest.read(c2[0])
560 lock = self.lock()
560 lock = self.lock()
561 tr = self.transaction()
561 tr = self.transaction()
562
562
563 # check in files
563 # check in files
564 new = {}
564 new = {}
565 linkrev = self.changelog.count()
565 linkrev = self.changelog.count()
566 commit.sort()
566 commit.sort()
567 for f in commit:
567 for f in commit:
568 self.ui.note(f + "\n")
568 self.ui.note(f + "\n")
569 try:
569 try:
570 fp = self.wjoin(f)
570 fp = self.wjoin(f)
571 mf1[f] = is_exec(fp)
571 mf1[f] = is_exec(fp)
572 t = file(fp).read()
572 t = file(fp).read()
573 except IOError:
573 except IOError:
574 self.warn("trouble committing %s!\n" % f)
574 self.warn("trouble committing %s!\n" % f)
575 raise
575 raise
576
576
577 meta = {}
577 meta = {}
578 cp = self.dirstate.copied(f)
578 cp = self.dirstate.copied(f)
579 if cp:
579 if cp:
580 meta["copy"] = cp
580 meta["copy"] = cp
581 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
581 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
582 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
582 self.ui.debug(" %s: copy %s:%s\n" % (f, cp, meta["copyrev"]))
583
583
584 r = self.file(f)
584 r = self.file(f)
585 fp1 = m1.get(f, nullid)
585 fp1 = m1.get(f, nullid)
586 fp2 = m2.get(f, nullid)
586 fp2 = m2.get(f, nullid)
587 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
587 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
588
588
589 # update manifest
589 # update manifest
590 m1.update(new)
590 m1.update(new)
591 for f in remove: del m1[f]
591 for f in remove: del m1[f]
592 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0])
592 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0])
593
593
594 # add changeset
594 # add changeset
595 new = new.keys()
595 new = new.keys()
596 new.sort()
596 new.sort()
597
597
598 if not text:
598 if not text:
599 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
599 edittext = "\n" + "HG: manifest hash %s\n" % hex(mn)
600 edittext += "".join(["HG: changed %s\n" % f for f in new])
600 edittext += "".join(["HG: changed %s\n" % f for f in new])
601 edittext += "".join(["HG: removed %s\n" % f for f in remove])
601 edittext += "".join(["HG: removed %s\n" % f for f in remove])
602 edittext = self.ui.edit(edittext)
602 edittext = self.ui.edit(edittext)
603 if not edittext.rstrip():
603 if not edittext.rstrip():
604 return 1
604 return 1
605 text = edittext
605 text = edittext
606
606
607 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
607 n = self.changelog.add(mn, new, text, tr, p1, p2, user, date)
608 tr.close()
608 tr.close()
609
609
610 self.dirstate.setparents(n)
610 self.dirstate.setparents(n)
611 self.dirstate.update(new, "n")
611 self.dirstate.update(new, "n")
612 self.dirstate.forget(remove)
612 self.dirstate.forget(remove)
613
613
614 def diffdir(self, path, changeset = None):
614 def diffdir(self, path, changeset = None):
615 changed = []
615 changed = []
616 added = []
616 added = []
617 unknown = []
617 unknown = []
618 mf = {}
618 mf = {}
619
619
620 if changeset:
620 if changeset:
621 change = self.changelog.read(changeset)
621 change = self.changelog.read(changeset)
622 mf = self.manifest.read(change[0])
622 mf = self.manifest.read(change[0])
623 dc = dict.fromkeys(mf)
623 dc = dict.fromkeys(mf)
624 else:
624 else:
625 changeset = self.dirstate.parents()[0]
625 changeset = self.dirstate.parents()[0]
626 change = self.changelog.read(changeset)
626 change = self.changelog.read(changeset)
627 mf = self.manifest.read(change[0])
627 mf = self.manifest.read(change[0])
628 dc = self.dirstate.dup()
628 dc = self.dirstate.dup()
629
629
630 def fcmp(fn):
630 def fcmp(fn):
631 t1 = self.wfile(fn).read()
631 t1 = self.wfile(fn).read()
632 t2 = self.file(fn).revision(mf[fn])
632 t2 = self.file(fn).revision(mf[fn])
633 return cmp(t1, t2)
633 return cmp(t1, t2)
634
634
635 for dir, subdirs, files in os.walk(path):
635 for dir, subdirs, files in os.walk(path):
636 d = dir[len(self.root)+1:]
636 d = dir[len(self.root)+1:]
637 if ".hg" in subdirs: subdirs.remove(".hg")
637 if ".hg" in subdirs: subdirs.remove(".hg")
638
638
639 for f in files:
639 for f in files:
640 fn = os.path.join(d, f)
640 fn = os.path.join(d, f)
641 try: s = os.stat(os.path.join(self.root, fn))
641 try: s = os.stat(os.path.join(self.root, fn))
642 except: continue
642 except: continue
643 if fn in dc:
643 if fn in dc:
644 c = dc[fn]
644 c = dc[fn]
645 del dc[fn]
645 del dc[fn]
646 if not c:
646 if not c:
647 if fcmp(fn):
647 if fcmp(fn):
648 changed.append(fn)
648 changed.append(fn)
649 elif c[0] == 'm':
649 elif c[0] == 'm':
650 changed.append(fn)
650 changed.append(fn)
651 elif c[0] == 'a':
651 elif c[0] == 'a':
652 added.append(fn)
652 added.append(fn)
653 elif c[0] == 'r':
653 elif c[0] == 'r':
654 unknown.append(fn)
654 unknown.append(fn)
655 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
655 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
656 changed.append(fn)
656 changed.append(fn)
657 elif c[1] != s.st_mode or c[3] != s.st_mtime:
657 elif c[1] != s.st_mode or c[3] != s.st_mtime:
658 if fcmp(fn):
658 if fcmp(fn):
659 changed.append(fn)
659 changed.append(fn)
660 else:
660 else:
661 if self.ignore(fn): continue
661 if self.ignore(fn): continue
662 unknown.append(fn)
662 unknown.append(fn)
663
663
664 deleted = dc.keys()
664 deleted = dc.keys()
665 deleted.sort()
665 deleted.sort()
666
666
667 return (changed, added, deleted, unknown)
667 return (changed, added, deleted, unknown)
668
668
669 def diffrevs(self, node1, node2):
669 def diffrevs(self, node1, node2):
670 changed, added = [], []
670 changed, added = [], []
671
671
672 change = self.changelog.read(node1)
672 change = self.changelog.read(node1)
673 mf1 = self.manifest.read(change[0])
673 mf1 = self.manifest.read(change[0])
674 change = self.changelog.read(node2)
674 change = self.changelog.read(node2)
675 mf2 = self.manifest.read(change[0])
675 mf2 = self.manifest.read(change[0])
676
676
677 for fn in mf2:
677 for fn in mf2:
678 if mf1.has_key(fn):
678 if mf1.has_key(fn):
679 if mf1[fn] != mf2[fn]:
679 if mf1[fn] != mf2[fn]:
680 changed.append(fn)
680 changed.append(fn)
681 del mf1[fn]
681 del mf1[fn]
682 else:
682 else:
683 added.append(fn)
683 added.append(fn)
684
684
685 deleted = mf1.keys()
685 deleted = mf1.keys()
686 deleted.sort()
686 deleted.sort()
687
687
688 return (changed, added, deleted)
688 return (changed, added, deleted)
689
689
690 def add(self, list):
690 def add(self, list):
691 for f in list:
691 for f in list:
692 p = self.wjoin(f)
692 p = self.wjoin(f)
693 if not os.path.isfile(p):
693 if not os.path.isfile(p):
694 self.ui.warn("%s does not exist!\n" % f)
694 self.ui.warn("%s does not exist!\n" % f)
695 elif self.dirstate.state(f) == 'n':
695 elif self.dirstate.state(f) == 'n':
696 self.ui.warn("%s already tracked!\n" % f)
696 self.ui.warn("%s already tracked!\n" % f)
697 else:
697 else:
698 self.dirstate.update([f], "a")
698 self.dirstate.update([f], "a")
699
699
700 def forget(self, list):
700 def forget(self, list):
701 for f in list:
701 for f in list:
702 if self.dirstate.state(f) not in 'ai':
702 if self.dirstate.state(f) not in 'ai':
703 self.ui.warn("%s not added!\n" % f)
703 self.ui.warn("%s not added!\n" % f)
704 else:
704 else:
705 self.dirstate.forget([f])
705 self.dirstate.forget([f])
706
706
707 def remove(self, list):
707 def remove(self, list):
708 for f in list:
708 for f in list:
709 p = self.wjoin(f)
709 p = self.wjoin(f)
710 if os.path.isfile(p):
710 if os.path.isfile(p):
711 self.ui.warn("%s still exists!\n" % f)
711 self.ui.warn("%s still exists!\n" % f)
712 elif self.dirstate.state(f) == 'a':
712 elif self.dirstate.state(f) == 'a':
713 self.ui.warn("%s never committed!\n" % f)
713 self.ui.warn("%s never committed!\n" % f)
714 self.dirstate.forget(f)
714 self.dirstate.forget(f)
715 elif f not in self.dirstate:
715 elif f not in self.dirstate:
716 self.ui.warn("%s not tracked!\n" % f)
716 self.ui.warn("%s not tracked!\n" % f)
717 else:
717 else:
718 self.dirstate.update([f], "r")
718 self.dirstate.update([f], "r")
719
719
720 def copy(self, source, dest):
720 def copy(self, source, dest):
721 p = self.wjoin(dest)
721 p = self.wjoin(dest)
722 if not os.path.isfile(dest):
722 if not os.path.isfile(dest):
723 self.ui.warn("%s does not exist!\n" % dest)
723 self.ui.warn("%s does not exist!\n" % dest)
724 else:
724 else:
725 if self.dirstate.state(dest) == '?':
725 if self.dirstate.state(dest) == '?':
726 self.dirstate.update([dest], "a")
726 self.dirstate.update([dest], "a")
727 self.dirstate.copy(source, dest)
727 self.dirstate.copy(source, dest)
728
728
729 def heads(self):
729 def heads(self):
730 return self.changelog.heads()
730 return self.changelog.heads()
731
731
732 def branches(self, nodes):
732 def branches(self, nodes):
733 if not nodes: nodes = [self.changelog.tip()]
733 if not nodes: nodes = [self.changelog.tip()]
734 b = []
734 b = []
735 for n in nodes:
735 for n in nodes:
736 t = n
736 t = n
737 while n:
737 while n:
738 p = self.changelog.parents(n)
738 p = self.changelog.parents(n)
739 if p[1] != nullid or p[0] == nullid:
739 if p[1] != nullid or p[0] == nullid:
740 b.append((t, n, p[0], p[1]))
740 b.append((t, n, p[0], p[1]))
741 break
741 break
742 n = p[0]
742 n = p[0]
743 return b
743 return b
744
744
745 def between(self, pairs):
745 def between(self, pairs):
746 r = []
746 r = []
747
747
748 for top, bottom in pairs:
748 for top, bottom in pairs:
749 n, l, i = top, [], 0
749 n, l, i = top, [], 0
750 f = 1
750 f = 1
751
751
752 while n != bottom:
752 while n != bottom:
753 p = self.changelog.parents(n)[0]
753 p = self.changelog.parents(n)[0]
754 if i == f:
754 if i == f:
755 l.append(n)
755 l.append(n)
756 f = f * 2
756 f = f * 2
757 n = p
757 n = p
758 i += 1
758 i += 1
759
759
760 r.append(l)
760 r.append(l)
761
761
762 return r
762 return r
763
763
764 def newer(self, nodes):
764 def newer(self, nodes):
765 m = {}
765 m = {}
766 nl = []
766 nl = []
767 pm = {}
767 pm = {}
768 cl = self.changelog
768 cl = self.changelog
769 t = l = cl.count()
769 t = l = cl.count()
770
770
771 # find the lowest numbered node
771 # find the lowest numbered node
772 for n in nodes:
772 for n in nodes:
773 l = min(l, cl.rev(n))
773 l = min(l, cl.rev(n))
774 m[n] = 1
774 m[n] = 1
775
775
776 for i in xrange(l, t):
776 for i in xrange(l, t):
777 n = cl.node(i)
777 n = cl.node(i)
778 if n in m: # explicitly listed
778 if n in m: # explicitly listed
779 pm[n] = 1
779 pm[n] = 1
780 nl.append(n)
780 nl.append(n)
781 continue
781 continue
782 for p in cl.parents(n):
782 for p in cl.parents(n):
783 if p in pm: # parent listed
783 if p in pm: # parent listed
784 pm[n] = 1
784 pm[n] = 1
785 nl.append(n)
785 nl.append(n)
786 break
786 break
787
787
788 return nl
788 return nl
789
789
790 def getchangegroup(self, remote):
790 def getchangegroup(self, remote):
791 m = self.changelog.nodemap
791 m = self.changelog.nodemap
792 search = []
792 search = []
793 fetch = []
793 fetch = []
794 seen = {}
794 seen = {}
795 seenbranch = {}
795 seenbranch = {}
796
796
797 # if we have an empty repo, fetch everything
797 # if we have an empty repo, fetch everything
798 if self.changelog.tip() == nullid:
798 if self.changelog.tip() == nullid:
799 self.ui.status("requesting all changes\n")
799 self.ui.status("requesting all changes\n")
800 return remote.changegroup([nullid])
800 return remote.changegroup([nullid])
801
801
802 # otherwise, assume we're closer to the tip than the root
802 # otherwise, assume we're closer to the tip than the root
803 self.ui.status("searching for changes\n")
803 self.ui.status("searching for changes\n")
804 heads = remote.heads()
804 heads = remote.heads()
805 unknown = []
805 unknown = []
806 for h in heads:
806 for h in heads:
807 if h not in m:
807 if h not in m:
808 unknown.append(h)
808 unknown.append(h)
809
809
810 if not unknown:
810 if not unknown:
811 self.ui.status("nothing to do!\n")
811 self.ui.status("nothing to do!\n")
812 return None
812 return None
813
813
814 rep = {}
814 rep = {}
815 reqcnt = 0
815 reqcnt = 0
816
816
817 unknown = remote.branches(unknown)
817 unknown = remote.branches(unknown)
818 while unknown:
818 while unknown:
819 r = []
819 r = []
820 while unknown:
820 while unknown:
821 n = unknown.pop(0)
821 n = unknown.pop(0)
822 if n[0] in seen:
822 if n[0] in seen:
823 continue
823 continue
824
824
825 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
825 self.ui.debug("examining %s:%s\n" % (short(n[0]), short(n[1])))
826 if n[0] == nullid:
826 if n[0] == nullid:
827 break
827 break
828 if n in seenbranch:
828 if n in seenbranch:
829 self.ui.debug("branch already found\n")
829 self.ui.debug("branch already found\n")
830 continue
830 continue
831 if n[1] and n[1] in m: # do we know the base?
831 if n[1] and n[1] in m: # do we know the base?
832 self.ui.debug("found incomplete branch %s:%s\n"
832 self.ui.debug("found incomplete branch %s:%s\n"
833 % (short(n[0]), short(n[1])))
833 % (short(n[0]), short(n[1])))
834 search.append(n) # schedule branch range for scanning
834 search.append(n) # schedule branch range for scanning
835 seenbranch[n] = 1
835 seenbranch[n] = 1
836 else:
836 else:
837 if n[1] not in seen and n[1] not in fetch:
837 if n[1] not in seen and n[1] not in fetch:
838 if n[2] in m and n[3] in m:
838 if n[2] in m and n[3] in m:
839 self.ui.debug("found new changeset %s\n" %
839 self.ui.debug("found new changeset %s\n" %
840 short(n[1]))
840 short(n[1]))
841 fetch.append(n[1]) # earliest unknown
841 fetch.append(n[1]) # earliest unknown
842 continue
842 continue
843
843
844 for a in n[2:4]:
844 for a in n[2:4]:
845 if a not in rep:
845 if a not in rep:
846 r.append(a)
846 r.append(a)
847 rep[a] = 1
847 rep[a] = 1
848
848
849 seen[n[0]] = 1
849 seen[n[0]] = 1
850
850
851 if r:
851 if r:
852 reqcnt += 1
852 reqcnt += 1
853 self.ui.debug("request %d: %s\n" %
853 self.ui.debug("request %d: %s\n" %
854 (reqcnt, " ".join(map(short, r))))
854 (reqcnt, " ".join(map(short, r))))
855 for p in range(0, len(r), 10):
855 for p in range(0, len(r), 10):
856 for b in remote.branches(r[p:p+10]):
856 for b in remote.branches(r[p:p+10]):
857 self.ui.debug("received %s:%s\n" %
857 self.ui.debug("received %s:%s\n" %
858 (short(b[0]), short(b[1])))
858 (short(b[0]), short(b[1])))
859 if b[0] not in m and b[0] not in seen:
859 if b[0] not in m and b[0] not in seen:
860 unknown.append(b)
860 unknown.append(b)
861
861
862 while search:
862 while search:
863 n = search.pop(0)
863 n = search.pop(0)
864 reqcnt += 1
864 reqcnt += 1
865 l = remote.between([(n[0], n[1])])[0]
865 l = remote.between([(n[0], n[1])])[0]
866 l.append(n[1])
866 l.append(n[1])
867 p = n[0]
867 p = n[0]
868 f = 1
868 f = 1
869 for i in l:
869 for i in l:
870 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
870 self.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
871 if i in m:
871 if i in m:
872 if f <= 2:
872 if f <= 2:
873 self.ui.debug("found new branch changeset %s\n" %
873 self.ui.debug("found new branch changeset %s\n" %
874 short(p))
874 short(p))
875 fetch.append(p)
875 fetch.append(p)
876 else:
876 else:
877 self.ui.debug("narrowed branch search to %s:%s\n"
877 self.ui.debug("narrowed branch search to %s:%s\n"
878 % (short(p), short(i)))
878 % (short(p), short(i)))
879 search.append((p, i))
879 search.append((p, i))
880 break
880 break
881 p, f = i, f * 2
881 p, f = i, f * 2
882
882
883 for f in fetch:
883 for f in fetch:
884 if f in m:
884 if f in m:
885 raise "already have", short(f[:4])
885 raise "already have", short(f[:4])
886
886
887 self.ui.note("adding new changesets starting at " +
887 self.ui.note("adding new changesets starting at " +
888 " ".join([short(f) for f in fetch]) + "\n")
888 " ".join([short(f) for f in fetch]) + "\n")
889
889
890 self.ui.debug("%d total queries\n" % reqcnt)
890 self.ui.debug("%d total queries\n" % reqcnt)
891
891
892 return remote.changegroup(fetch)
892 return remote.changegroup(fetch)
893
893
894 def changegroup(self, basenodes):
894 def changegroup(self, basenodes):
895 nodes = self.newer(basenodes)
895 nodes = self.newer(basenodes)
896
896
897 # construct the link map
897 # construct the link map
898 linkmap = {}
898 linkmap = {}
899 for n in nodes:
899 for n in nodes:
900 linkmap[self.changelog.rev(n)] = n
900 linkmap[self.changelog.rev(n)] = n
901
901
902 # construct a list of all changed files
902 # construct a list of all changed files
903 changed = {}
903 changed = {}
904 for n in nodes:
904 for n in nodes:
905 c = self.changelog.read(n)
905 c = self.changelog.read(n)
906 for f in c[3]:
906 for f in c[3]:
907 changed[f] = 1
907 changed[f] = 1
908 changed = changed.keys()
908 changed = changed.keys()
909 changed.sort()
909 changed.sort()
910
910
911 # the changegroup is changesets + manifests + all file revs
911 # the changegroup is changesets + manifests + all file revs
912 revs = [ self.changelog.rev(n) for n in nodes ]
912 revs = [ self.changelog.rev(n) for n in nodes ]
913
913
914 for y in self.changelog.group(linkmap): yield y
914 for y in self.changelog.group(linkmap): yield y
915 for y in self.manifest.group(linkmap): yield y
915 for y in self.manifest.group(linkmap): yield y
916 for f in changed:
916 for f in changed:
917 yield struct.pack(">l", len(f) + 4) + f
917 yield struct.pack(">l", len(f) + 4) + f
918 g = self.file(f).group(linkmap)
918 g = self.file(f).group(linkmap)
919 for y in g:
919 for y in g:
920 yield y
920 yield y
921
921
922 def addchangegroup(self, generator):
922 def addchangegroup(self, generator):
923
923
924 class genread:
924 class genread:
925 def __init__(self, generator):
925 def __init__(self, generator):
926 self.g = generator
926 self.g = generator
927 self.buf = ""
927 self.buf = ""
928 def read(self, l):
928 def read(self, l):
929 while l > len(self.buf):
929 while l > len(self.buf):
930 try:
930 try:
931 self.buf += self.g.next()
931 self.buf += self.g.next()
932 except StopIteration:
932 except StopIteration:
933 break
933 break
934 d, self.buf = self.buf[:l], self.buf[l:]
934 d, self.buf = self.buf[:l], self.buf[l:]
935 return d
935 return d
936
936
937 def getchunk():
937 def getchunk():
938 d = source.read(4)
938 d = source.read(4)
939 if not d: return ""
939 if not d: return ""
940 l = struct.unpack(">l", d)[0]
940 l = struct.unpack(">l", d)[0]
941 if l <= 4: return ""
941 if l <= 4: return ""
942 return source.read(l - 4)
942 return source.read(l - 4)
943
943
944 def getgroup():
944 def getgroup():
945 while 1:
945 while 1:
946 c = getchunk()
946 c = getchunk()
947 if not c: break
947 if not c: break
948 yield c
948 yield c
949
949
950 def csmap(x):
950 def csmap(x):
951 self.ui.debug("add changeset %s\n" % short(x))
951 self.ui.debug("add changeset %s\n" % short(x))
952 return self.changelog.count()
952 return self.changelog.count()
953
953
954 def revmap(x):
954 def revmap(x):
955 return self.changelog.rev(x)
955 return self.changelog.rev(x)
956
956
957 if not generator: return
957 if not generator: return
958 changesets = files = revisions = 0
958 changesets = files = revisions = 0
959
959
960 source = genread(generator)
960 source = genread(generator)
961 lock = self.lock()
961 lock = self.lock()
962 tr = self.transaction()
962 tr = self.transaction()
963
963
964 # pull off the changeset group
964 # pull off the changeset group
965 self.ui.status("adding changesets\n")
965 self.ui.status("adding changesets\n")
966 co = self.changelog.tip()
966 co = self.changelog.tip()
967 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
967 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
968 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
968 changesets = self.changelog.rev(cn) - self.changelog.rev(co)
969
969
970 # pull off the manifest group
970 # pull off the manifest group
971 self.ui.status("adding manifests\n")
971 self.ui.status("adding manifests\n")
972 mm = self.manifest.tip()
972 mm = self.manifest.tip()
973 mo = self.manifest.addgroup(getgroup(), revmap, tr)
973 mo = self.manifest.addgroup(getgroup(), revmap, tr)
974
974
975 # process the files
975 # process the files
976 self.ui.status("adding file revisions\n")
976 self.ui.status("adding file revisions\n")
977 while 1:
977 while 1:
978 f = getchunk()
978 f = getchunk()
979 if not f: break
979 if not f: break
980 self.ui.debug("adding %s revisions\n" % f)
980 self.ui.debug("adding %s revisions\n" % f)
981 fl = self.file(f)
981 fl = self.file(f)
982 o = fl.tip()
982 o = fl.tip()
983 n = fl.addgroup(getgroup(), revmap, tr)
983 n = fl.addgroup(getgroup(), revmap, tr)
984 revisions += fl.rev(n) - fl.rev(o)
984 revisions += fl.rev(n) - fl.rev(o)
985 files += 1
985 files += 1
986
986
987 self.ui.status(("modified %d files, added %d changesets" +
987 self.ui.status(("modified %d files, added %d changesets" +
988 " and %d new revisions\n")
988 " and %d new revisions\n")
989 % (files, changesets, revisions))
989 % (files, changesets, revisions))
990
990
991 tr.close()
991 tr.close()
992 return
992 return
993
993
994 def update(self, node, allow=False, force=False):
994 def update(self, node, allow=False, force=False):
995 pl = self.dirstate.parents()
995 pl = self.dirstate.parents()
996 if not force and pl[1] != nullid:
996 if not force and pl[1] != nullid:
997 self.ui.warn("aborting: outstanding uncommitted merges\n")
997 self.ui.warn("aborting: outstanding uncommitted merges\n")
998 return
998 return
999
999
1000 p1, p2 = pl[0], node
1000 p1, p2 = pl[0], node
1001 pa = self.changelog.ancestor(p1, p2)
1001 pa = self.changelog.ancestor(p1, p2)
1002 m1n = self.changelog.read(p1)[0]
1002 m1n = self.changelog.read(p1)[0]
1003 m2n = self.changelog.read(p2)[0]
1003 m2n = self.changelog.read(p2)[0]
1004 man = self.manifest.ancestor(m1n, m2n)
1004 man = self.manifest.ancestor(m1n, m2n)
1005 m1 = self.manifest.read(m1n)
1005 m1 = self.manifest.read(m1n)
1006 mf1 = self.manifest.readflags(m1n)
1006 mf1 = self.manifest.readflags(m1n)
1007 m2 = self.manifest.read(m2n)
1007 m2 = self.manifest.read(m2n)
1008 mf2 = self.manifest.readflags(m2n)
1008 mf2 = self.manifest.readflags(m2n)
1009 ma = self.manifest.read(man)
1009 ma = self.manifest.read(man)
1010 mfa = self.manifest.readflags(m2n)
1010 mfa = self.manifest.readflags(m2n)
1011
1011
1012 (c, a, d, u) = self.diffdir(self.root)
1012 (c, a, d, u) = self.diffdir(self.root)
1013
1013
1014 # resolve the manifest to determine which files
1014 # resolve the manifest to determine which files
1015 # we care about merging
1015 # we care about merging
1016 self.ui.note("resolving manifests\n")
1016 self.ui.note("resolving manifests\n")
1017 self.ui.debug(" ancestor %s local %s remote %s\n" %
1017 self.ui.debug(" ancestor %s local %s remote %s\n" %
1018 (short(man), short(m1n), short(m2n)))
1018 (short(man), short(m1n), short(m2n)))
1019
1019
1020 merge = {}
1020 merge = {}
1021 get = {}
1021 get = {}
1022 remove = []
1022 remove = []
1023 mark = {}
1023 mark = {}
1024
1024
1025 # construct a working dir manifest
1025 # construct a working dir manifest
1026 mw = m1.copy()
1026 mw = m1.copy()
1027 mfw = mf1.copy()
1027 mfw = mf1.copy()
1028 for f in a + c + u:
1028 for f in a + c + u:
1029 mw[f] = ""
1029 mw[f] = ""
1030 mfw[f] = is_exec(self.wjoin(f))
1030 mfw[f] = is_exec(self.wjoin(f))
1031 for f in d:
1031 for f in d:
1032 if f in mw: del mw[f]
1032 if f in mw: del mw[f]
1033
1033
1034 for f, n in mw.iteritems():
1034 for f, n in mw.iteritems():
1035 if f in m2:
1035 if f in m2:
1036 s = 0
1036 s = 0
1037
1037
1038 # is the wfile new since m1, and match m2?
1039 if n not in m1:
1040 t1 = self.wfile(f).read()
1041 t2 = self.file(f).revision(m2[f])
1042 if cmp(t1, t2) == 0:
1043 mark[f] = 1
1044 n = m2[f]
1045 del t1, t2
1046
1038 # are files different?
1047 # are files different?
1039 if n != m2[f]:
1048 if n != m2[f]:
1040 a = ma.get(f, nullid)
1049 a = ma.get(f, nullid)
1041 # are both different from the ancestor?
1050 # are both different from the ancestor?
1042 if n != a and m2[f] != a:
1051 if n != a and m2[f] != a:
1043 self.ui.debug(" %s versions differ, resolve\n" % f)
1052 self.ui.debug(" %s versions differ, resolve\n" % f)
1044 merge[f] = (m1.get(f, nullid), m2[f])
1053 merge[f] = (m1.get(f, nullid), m2[f])
1045 # merge executable bits
1054 # merge executable bits
1046 # "if we changed or they changed, change in merge"
1055 # "if we changed or they changed, change in merge"
1047 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1056 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1048 mode = ((a^b) | (a^c)) ^ a
1057 mode = ((a^b) | (a^c)) ^ a
1049 merge[f] = (m1.get(f, nullid), m2[f], mode)
1058 merge[f] = (m1.get(f, nullid), m2[f], mode)
1050 s = 1
1059 s = 1
1051 # are we clobbering?
1060 # are we clobbering?
1052 # is remote's version newer?
1061 # is remote's version newer?
1053 # or are we going back in time?
1062 # or are we going back in time?
1054 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1063 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1055 self.ui.debug(" remote %s is newer, get\n" % f)
1064 self.ui.debug(" remote %s is newer, get\n" % f)
1056 get[f] = m2[f]
1065 get[f] = m2[f]
1057 s = 1
1066 s = 1
1058 else:
1067 else:
1059 mark[f] = 1
1068 mark[f] = 1
1060
1069
1061 if not s and mfw[f] != mf2[f]:
1070 if not s and mfw[f] != mf2[f]:
1062 if force:
1071 if force:
1063 self.ui.debug(" updating permissions for %s\n" % f)
1072 self.ui.debug(" updating permissions for %s\n" % f)
1064 set_exec(self.wjoin(f), mf2[f])
1073 set_exec(self.wjoin(f), mf2[f])
1065 else:
1074 else:
1066 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1075 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1067 mode = ((a^b) | (a^c)) ^ a
1076 mode = ((a^b) | (a^c)) ^ a
1068 if mode != b:
1077 if mode != b:
1069 self.ui.debug(" updating permissions for %s\n" % f)
1078 self.ui.debug(" updating permissions for %s\n" % f)
1070 set_exec(self.wjoin(f), mode)
1079 set_exec(self.wjoin(f), mode)
1071 mark[f] = 1
1080 mark[f] = 1
1072 del m2[f]
1081 del m2[f]
1073 elif f in ma:
1082 elif f in ma:
1074 if not force and n != ma[f]:
1083 if not force and n != ma[f]:
1075 r = self.ui.prompt(
1084 r = self.ui.prompt(
1076 (" local changed %s which remote deleted\n" % f) +
1085 (" local changed %s which remote deleted\n" % f) +
1077 "(k)eep or (d)elete?", "[kd]", "k")
1086 "(k)eep or (d)elete?", "[kd]", "k")
1078 if r == "d":
1087 if r == "d":
1079 remove.append(f)
1088 remove.append(f)
1080 else:
1089 else:
1081 self.ui.debug("other deleted %s\n" % f)
1090 self.ui.debug("other deleted %s\n" % f)
1082 remove.append(f) # other deleted it
1091 remove.append(f) # other deleted it
1083 else:
1092 else:
1084 if n == m1.get(f, nullid): # same as parent
1093 if n == m1.get(f, nullid): # same as parent
1085 if p2 == pa: # going backwards?
1094 if p2 == pa: # going backwards?
1086 self.ui.debug("remote deleted %s\n" % f)
1095 self.ui.debug("remote deleted %s\n" % f)
1087 remove.append(f)
1096 remove.append(f)
1088 else:
1097 else:
1089 self.ui.debug("local created %s, keeping\n" % f)
1098 self.ui.debug("local created %s, keeping\n" % f)
1090 else:
1099 else:
1091 self.ui.debug("working dir created %s, keeping\n" % f)
1100 self.ui.debug("working dir created %s, keeping\n" % f)
1092
1101
1093 for f, n in m2.iteritems():
1102 for f, n in m2.iteritems():
1094 if f[0] == "/": continue
1103 if f[0] == "/": continue
1095 if not force and f in ma and n != ma[f]:
1104 if not force and f in ma and n != ma[f]:
1096 r = self.ui.prompt(
1105 r = self.ui.prompt(
1097 ("remote changed %s which local deleted\n" % f) +
1106 ("remote changed %s which local deleted\n" % f) +
1098 "(k)eep or (d)elete?", "[kd]", "k")
1107 "(k)eep or (d)elete?", "[kd]", "k")
1099 if r == "d": remove.append(f)
1108 if r == "d": remove.append(f)
1100 else:
1109 else:
1101 self.ui.debug("remote created %s\n" % f)
1110 self.ui.debug("remote created %s\n" % f)
1102 get[f] = n
1111 get[f] = n
1103
1112
1104 del mw, m1, m2, ma
1113 del mw, m1, m2, ma
1105
1114
1106 if force:
1115 if force:
1107 for f in merge:
1116 for f in merge:
1108 get[f] = merge[f][1]
1117 get[f] = merge[f][1]
1109 merge = {}
1118 merge = {}
1110
1119
1111 if pa == p1 or pa == p2:
1120 if pa == p1 or pa == p2:
1112 # we don't need to do any magic, just jump to the new rev
1121 # we don't need to do any magic, just jump to the new rev
1113 mode = 'n'
1122 mode = 'n'
1114 p1, p2 = p2, nullid
1123 p1, p2 = p2, nullid
1115 else:
1124 else:
1116 if not allow:
1125 if not allow:
1117 self.ui.status("this update spans a branch" +
1126 self.ui.status("this update spans a branch" +
1118 " affecting the following files:\n")
1127 " affecting the following files:\n")
1119 fl = merge.keys() + get.keys()
1128 fl = merge.keys() + get.keys()
1120 fl.sort()
1129 fl.sort()
1121 for f in fl:
1130 for f in fl:
1122 cf = ""
1131 cf = ""
1123 if f in merge: cf = " (resolve)"
1132 if f in merge: cf = " (resolve)"
1124 self.ui.status(" %s%s\n" % (f, cf))
1133 self.ui.status(" %s%s\n" % (f, cf))
1125 self.ui.warn("aborting update spanning branches!\n")
1134 self.ui.warn("aborting update spanning branches!\n")
1126 self.ui.status("(use update -m to perform a branch merge)\n")
1135 self.ui.status("(use update -m to perform a branch merge)\n")
1127 return 1
1136 return 1
1128 # we have to remember what files we needed to get/change
1137 # we have to remember what files we needed to get/change
1129 # because any file that's different from either one of its
1138 # because any file that's different from either one of its
1130 # parents must be in the changeset
1139 # parents must be in the changeset
1131 mode = 'm'
1140 mode = 'm'
1132 self.dirstate.update(mark.keys(), "m")
1141 self.dirstate.update(mark.keys(), "m")
1133
1142
1134 self.dirstate.setparents(p1, p2)
1143 self.dirstate.setparents(p1, p2)
1135
1144
1136 # get the files we don't need to change
1145 # get the files we don't need to change
1137 files = get.keys()
1146 files = get.keys()
1138 files.sort()
1147 files.sort()
1139 for f in files:
1148 for f in files:
1140 if f[0] == "/": continue
1149 if f[0] == "/": continue
1141 self.ui.note("getting %s\n" % f)
1150 self.ui.note("getting %s\n" % f)
1142 t = self.file(f).read(get[f])
1151 t = self.file(f).read(get[f])
1143 try:
1152 try:
1144 self.wfile(f, "w").write(t)
1153 self.wfile(f, "w").write(t)
1145 except IOError:
1154 except IOError:
1146 os.makedirs(os.path.dirname(self.wjoin(f)))
1155 os.makedirs(os.path.dirname(self.wjoin(f)))
1147 self.wfile(f, "w").write(t)
1156 self.wfile(f, "w").write(t)
1148 set_exec(self.wjoin(f), mf2[f])
1157 set_exec(self.wjoin(f), mf2[f])
1149 self.dirstate.update([f], mode)
1158 self.dirstate.update([f], mode)
1150
1159
1151 # merge the tricky bits
1160 # merge the tricky bits
1152 files = merge.keys()
1161 files = merge.keys()
1153 files.sort()
1162 files.sort()
1154 for f in files:
1163 for f in files:
1155 self.ui.status("merging %s\n" % f)
1164 self.ui.status("merging %s\n" % f)
1156 m, o, flag = merge[f]
1165 m, o, flag = merge[f]
1157 self.merge3(f, m, o)
1166 self.merge3(f, m, o)
1158 set_exec(self.wjoin(f), flag)
1167 set_exec(self.wjoin(f), flag)
1159 self.dirstate.update([f], 'm')
1168 self.dirstate.update([f], 'm')
1160
1169
1161 for f in remove:
1170 for f in remove:
1162 self.ui.note("removing %s\n" % f)
1171 self.ui.note("removing %s\n" % f)
1163 os.unlink(f)
1172 os.unlink(f)
1164 if mode == 'n':
1173 if mode == 'n':
1165 self.dirstate.forget(remove)
1174 self.dirstate.forget(remove)
1166 else:
1175 else:
1167 self.dirstate.update(remove, 'r')
1176 self.dirstate.update(remove, 'r')
1168
1177
1169 def merge3(self, fn, my, other):
1178 def merge3(self, fn, my, other):
1170 """perform a 3-way merge in the working directory"""
1179 """perform a 3-way merge in the working directory"""
1171
1180
1172 def temp(prefix, node):
1181 def temp(prefix, node):
1173 pre = "%s~%s." % (os.path.basename(fn), prefix)
1182 pre = "%s~%s." % (os.path.basename(fn), prefix)
1174 (fd, name) = tempfile.mkstemp("", pre)
1183 (fd, name) = tempfile.mkstemp("", pre)
1175 f = os.fdopen(fd, "w")
1184 f = os.fdopen(fd, "w")
1176 f.write(fl.revision(node))
1185 f.write(fl.revision(node))
1177 f.close()
1186 f.close()
1178 return name
1187 return name
1179
1188
1180 fl = self.file(fn)
1189 fl = self.file(fn)
1181 base = fl.ancestor(my, other)
1190 base = fl.ancestor(my, other)
1182 a = self.wjoin(fn)
1191 a = self.wjoin(fn)
1183 b = temp("base", base)
1192 b = temp("base", base)
1184 c = temp("other", other)
1193 c = temp("other", other)
1185
1194
1186 self.ui.note("resolving %s\n" % fn)
1195 self.ui.note("resolving %s\n" % fn)
1187 self.ui.debug("file %s: other %s ancestor %s\n" %
1196 self.ui.debug("file %s: other %s ancestor %s\n" %
1188 (fn, short(other), short(base)))
1197 (fn, short(other), short(base)))
1189
1198
1190 cmd = os.environ.get("HGMERGE", "hgmerge")
1199 cmd = os.environ.get("HGMERGE", "hgmerge")
1191 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1200 r = os.system("%s %s %s %s" % (cmd, a, b, c))
1192 if r:
1201 if r:
1193 self.ui.warn("merging %s failed!\n" % fn)
1202 self.ui.warn("merging %s failed!\n" % fn)
1194
1203
1195 os.unlink(b)
1204 os.unlink(b)
1196 os.unlink(c)
1205 os.unlink(c)
1197
1206
1198 def verify(self):
1207 def verify(self):
1199 filelinkrevs = {}
1208 filelinkrevs = {}
1200 filenodes = {}
1209 filenodes = {}
1201 changesets = revisions = files = 0
1210 changesets = revisions = files = 0
1202 errors = 0
1211 errors = 0
1203
1212
1204 seen = {}
1213 seen = {}
1205 self.ui.status("checking changesets\n")
1214 self.ui.status("checking changesets\n")
1206 for i in range(self.changelog.count()):
1215 for i in range(self.changelog.count()):
1207 changesets += 1
1216 changesets += 1
1208 n = self.changelog.node(i)
1217 n = self.changelog.node(i)
1209 if n in seen:
1218 if n in seen:
1210 self.ui.warn("duplicate changeset at revision %d\n" % i)
1219 self.ui.warn("duplicate changeset at revision %d\n" % i)
1211 errors += 1
1220 errors += 1
1212 seen[n] = 1
1221 seen[n] = 1
1213
1222
1214 for p in self.changelog.parents(n):
1223 for p in self.changelog.parents(n):
1215 if p not in self.changelog.nodemap:
1224 if p not in self.changelog.nodemap:
1216 self.ui.warn("changeset %s has unknown parent %s\n" %
1225 self.ui.warn("changeset %s has unknown parent %s\n" %
1217 (short(n), short(p)))
1226 (short(n), short(p)))
1218 errors += 1
1227 errors += 1
1219 try:
1228 try:
1220 changes = self.changelog.read(n)
1229 changes = self.changelog.read(n)
1221 except Exception, inst:
1230 except Exception, inst:
1222 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1231 self.ui.warn("unpacking changeset %s: %s\n" % (short(n), inst))
1223 errors += 1
1232 errors += 1
1224
1233
1225 for f in changes[3]:
1234 for f in changes[3]:
1226 filelinkrevs.setdefault(f, []).append(i)
1235 filelinkrevs.setdefault(f, []).append(i)
1227
1236
1228 seen = {}
1237 seen = {}
1229 self.ui.status("checking manifests\n")
1238 self.ui.status("checking manifests\n")
1230 for i in range(self.manifest.count()):
1239 for i in range(self.manifest.count()):
1231 n = self.manifest.node(i)
1240 n = self.manifest.node(i)
1232 if n in seen:
1241 if n in seen:
1233 self.ui.warn("duplicate manifest at revision %d\n" % i)
1242 self.ui.warn("duplicate manifest at revision %d\n" % i)
1234 errors += 1
1243 errors += 1
1235 seen[n] = 1
1244 seen[n] = 1
1236
1245
1237 for p in self.manifest.parents(n):
1246 for p in self.manifest.parents(n):
1238 if p not in self.manifest.nodemap:
1247 if p not in self.manifest.nodemap:
1239 self.ui.warn("manifest %s has unknown parent %s\n" %
1248 self.ui.warn("manifest %s has unknown parent %s\n" %
1240 (short(n), short(p)))
1249 (short(n), short(p)))
1241 errors += 1
1250 errors += 1
1242
1251
1243 try:
1252 try:
1244 delta = mdiff.patchtext(self.manifest.delta(n))
1253 delta = mdiff.patchtext(self.manifest.delta(n))
1245 except KeyboardInterrupt:
1254 except KeyboardInterrupt:
1246 print "aborted"
1255 print "aborted"
1247 sys.exit(0)
1256 sys.exit(0)
1248 except Exception, inst:
1257 except Exception, inst:
1249 self.ui.warn("unpacking manifest %s: %s\n"
1258 self.ui.warn("unpacking manifest %s: %s\n"
1250 % (short(n), inst))
1259 % (short(n), inst))
1251 errors += 1
1260 errors += 1
1252
1261
1253 ff = [ l.split('\0') for l in delta.splitlines() ]
1262 ff = [ l.split('\0') for l in delta.splitlines() ]
1254 for f, fn in ff:
1263 for f, fn in ff:
1255 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1264 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1256
1265
1257 self.ui.status("crosschecking files in changesets and manifests\n")
1266 self.ui.status("crosschecking files in changesets and manifests\n")
1258 for f in filenodes:
1267 for f in filenodes:
1259 if f not in filelinkrevs:
1268 if f not in filelinkrevs:
1260 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1269 self.ui.warn("file %s in manifest but not in changesets\n" % f)
1261 errors += 1
1270 errors += 1
1262
1271
1263 for f in filelinkrevs:
1272 for f in filelinkrevs:
1264 if f not in filenodes:
1273 if f not in filenodes:
1265 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1274 self.ui.warn("file %s in changeset but not in manifest\n" % f)
1266 errors += 1
1275 errors += 1
1267
1276
1268 self.ui.status("checking files\n")
1277 self.ui.status("checking files\n")
1269 ff = filenodes.keys()
1278 ff = filenodes.keys()
1270 ff.sort()
1279 ff.sort()
1271 for f in ff:
1280 for f in ff:
1272 if f == "/dev/null": continue
1281 if f == "/dev/null": continue
1273 files += 1
1282 files += 1
1274 fl = self.file(f)
1283 fl = self.file(f)
1275 nodes = { nullid: 1 }
1284 nodes = { nullid: 1 }
1276 seen = {}
1285 seen = {}
1277 for i in range(fl.count()):
1286 for i in range(fl.count()):
1278 revisions += 1
1287 revisions += 1
1279 n = fl.node(i)
1288 n = fl.node(i)
1280
1289
1281 if n in seen:
1290 if n in seen:
1282 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1291 self.ui.warn("%s: duplicate revision %d\n" % (f, i))
1283 errors += 1
1292 errors += 1
1284
1293
1285 if n not in filenodes[f]:
1294 if n not in filenodes[f]:
1286 self.ui.warn("%s: %d:%s not in manifests\n"
1295 self.ui.warn("%s: %d:%s not in manifests\n"
1287 % (f, i, short(n)))
1296 % (f, i, short(n)))
1288 print len(filenodes[f].keys()), fl.count(), f
1297 print len(filenodes[f].keys()), fl.count(), f
1289 errors += 1
1298 errors += 1
1290 else:
1299 else:
1291 del filenodes[f][n]
1300 del filenodes[f][n]
1292
1301
1293 flr = fl.linkrev(n)
1302 flr = fl.linkrev(n)
1294 if flr not in filelinkrevs[f]:
1303 if flr not in filelinkrevs[f]:
1295 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1304 self.ui.warn("%s:%s points to unexpected changeset %d\n"
1296 % (f, short(n), fl.linkrev(n)))
1305 % (f, short(n), fl.linkrev(n)))
1297 errors += 1
1306 errors += 1
1298 else:
1307 else:
1299 filelinkrevs[f].remove(flr)
1308 filelinkrevs[f].remove(flr)
1300
1309
1301 # verify contents
1310 # verify contents
1302 try:
1311 try:
1303 t = fl.read(n)
1312 t = fl.read(n)
1304 except Exception, inst:
1313 except Exception, inst:
1305 self.ui.warn("unpacking file %s %s: %s\n"
1314 self.ui.warn("unpacking file %s %s: %s\n"
1306 % (f, short(n), inst))
1315 % (f, short(n), inst))
1307 errors += 1
1316 errors += 1
1308
1317
1309 # verify parents
1318 # verify parents
1310 (p1, p2) = fl.parents(n)
1319 (p1, p2) = fl.parents(n)
1311 if p1 not in nodes:
1320 if p1 not in nodes:
1312 self.ui.warn("file %s:%s unknown parent 1 %s" %
1321 self.ui.warn("file %s:%s unknown parent 1 %s" %
1313 (f, short(n), short(p1)))
1322 (f, short(n), short(p1)))
1314 errors += 1
1323 errors += 1
1315 if p2 not in nodes:
1324 if p2 not in nodes:
1316 self.ui.warn("file %s:%s unknown parent 2 %s" %
1325 self.ui.warn("file %s:%s unknown parent 2 %s" %
1317 (f, short(n), short(p1)))
1326 (f, short(n), short(p1)))
1318 errors += 1
1327 errors += 1
1319 nodes[n] = 1
1328 nodes[n] = 1
1320
1329
1321 # cross-check
1330 # cross-check
1322 for node in filenodes[f]:
1331 for node in filenodes[f]:
1323 self.ui.warn("node %s in manifests not in %s\n"
1332 self.ui.warn("node %s in manifests not in %s\n"
1324 % (hex(n), f))
1333 % (hex(n), f))
1325 errors += 1
1334 errors += 1
1326
1335
1327 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1336 self.ui.status("%d files, %d changesets, %d total revisions\n" %
1328 (files, changesets, revisions))
1337 (files, changesets, revisions))
1329
1338
1330 if errors:
1339 if errors:
1331 self.ui.warn("%d integrity errors encountered!\n" % errors)
1340 self.ui.warn("%d integrity errors encountered!\n" % errors)
1332 return 1
1341 return 1
1333
1342
1334 class remoterepository:
1343 class remoterepository:
1335 def __init__(self, ui, path):
1344 def __init__(self, ui, path):
1336 self.url = path
1345 self.url = path
1337 self.ui = ui
1346 self.ui = ui
1338 no_list = [ "localhost", "127.0.0.1" ]
1347 no_list = [ "localhost", "127.0.0.1" ]
1339 host = ui.config("http_proxy", "host")
1348 host = ui.config("http_proxy", "host")
1340 user = ui.config("http_proxy", "user")
1349 user = ui.config("http_proxy", "user")
1341 passwd = ui.config("http_proxy", "passwd")
1350 passwd = ui.config("http_proxy", "passwd")
1342 no = ui.config("http_proxy", "no")
1351 no = ui.config("http_proxy", "no")
1343 if no:
1352 if no:
1344 no_list = no_list + no.split(",")
1353 no_list = no_list + no.split(",")
1345
1354
1346 no_proxy = 0
1355 no_proxy = 0
1347 for h in no_list:
1356 for h in no_list:
1348 if (path.startswith("http://" + h + "/") or
1357 if (path.startswith("http://" + h + "/") or
1349 path.startswith("http://" + h + ":") or
1358 path.startswith("http://" + h + ":") or
1350 path == "http://" + h):
1359 path == "http://" + h):
1351 no_proxy = 1
1360 no_proxy = 1
1352
1361
1353 # Note: urllib2 takes proxy values from the environment and those will
1362 # Note: urllib2 takes proxy values from the environment and those will
1354 # take precedence
1363 # take precedence
1355
1364
1356 proxy_handler = urllib2.BaseHandler()
1365 proxy_handler = urllib2.BaseHandler()
1357 if host and not no_proxy:
1366 if host and not no_proxy:
1358 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1367 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
1359
1368
1360 authinfo = None
1369 authinfo = None
1361 if user and passwd:
1370 if user and passwd:
1362 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1371 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1363 passmgr.add_password(None, host, user, passwd)
1372 passmgr.add_password(None, host, user, passwd)
1364 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1373 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
1365
1374
1366 opener = urllib2.build_opener(proxy_handler, authinfo)
1375 opener = urllib2.build_opener(proxy_handler, authinfo)
1367 urllib2.install_opener(opener)
1376 urllib2.install_opener(opener)
1368
1377
1369 def do_cmd(self, cmd, **args):
1378 def do_cmd(self, cmd, **args):
1370 self.ui.debug("sending %s command\n" % cmd)
1379 self.ui.debug("sending %s command\n" % cmd)
1371 q = {"cmd": cmd}
1380 q = {"cmd": cmd}
1372 q.update(args)
1381 q.update(args)
1373 qs = urllib.urlencode(q)
1382 qs = urllib.urlencode(q)
1374 cu = "%s?%s" % (self.url, qs)
1383 cu = "%s?%s" % (self.url, qs)
1375 return urllib2.urlopen(cu)
1384 return urllib2.urlopen(cu)
1376
1385
1377 def heads(self):
1386 def heads(self):
1378 d = self.do_cmd("heads").read()
1387 d = self.do_cmd("heads").read()
1379 try:
1388 try:
1380 return map(bin, d[:-1].split(" "))
1389 return map(bin, d[:-1].split(" "))
1381 except:
1390 except:
1382 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1391 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1383 raise
1392 raise
1384
1393
1385 def branches(self, nodes):
1394 def branches(self, nodes):
1386 n = " ".join(map(hex, nodes))
1395 n = " ".join(map(hex, nodes))
1387 d = self.do_cmd("branches", nodes=n).read()
1396 d = self.do_cmd("branches", nodes=n).read()
1388 try:
1397 try:
1389 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1398 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1390 return br
1399 return br
1391 except:
1400 except:
1392 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1401 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1393 raise
1402 raise
1394
1403
1395 def between(self, pairs):
1404 def between(self, pairs):
1396 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1405 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1397 d = self.do_cmd("between", pairs=n).read()
1406 d = self.do_cmd("between", pairs=n).read()
1398 try:
1407 try:
1399 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1408 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
1400 return p
1409 return p
1401 except:
1410 except:
1402 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1411 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1403 raise
1412 raise
1404
1413
1405 def changegroup(self, nodes):
1414 def changegroup(self, nodes):
1406 n = " ".join(map(hex, nodes))
1415 n = " ".join(map(hex, nodes))
1407 zd = zlib.decompressobj()
1416 zd = zlib.decompressobj()
1408 f = self.do_cmd("changegroup", roots=n)
1417 f = self.do_cmd("changegroup", roots=n)
1409 bytes = 0
1418 bytes = 0
1410 while 1:
1419 while 1:
1411 d = f.read(4096)
1420 d = f.read(4096)
1412 bytes += len(d)
1421 bytes += len(d)
1413 if not d:
1422 if not d:
1414 yield zd.flush()
1423 yield zd.flush()
1415 break
1424 break
1416 yield zd.decompress(d)
1425 yield zd.decompress(d)
1417 self.ui.note("%d bytes of data transfered\n" % bytes)
1426 self.ui.note("%d bytes of data transfered\n" % bytes)
1418
1427
1419 def repository(ui, path=None, create=0):
1428 def repository(ui, path=None, create=0):
1420 if path and path[:7] == "http://":
1429 if path and path[:7] == "http://":
1421 return remoterepository(ui, path)
1430 return remoterepository(ui, path)
1422 if path and path[:5] == "hg://":
1431 if path and path[:5] == "hg://":
1423 return remoterepository(ui, path.replace("hg://", "http://"))
1432 return remoterepository(ui, path.replace("hg://", "http://"))
1424 if path and path[:11] == "old-http://":
1433 if path and path[:11] == "old-http://":
1425 return localrepository(ui, path.replace("old-http://", "http://"))
1434 return localrepository(ui, path.replace("old-http://", "http://"))
1426 else:
1435 else:
1427 return localrepository(ui, path, create)
1436 return localrepository(ui, path, create)
1428
1437
General Comments 0
You need to be logged in to leave comments. Login now