##// END OF EJS Templates
Replace tkmerge with hgmerge...
mpm@selenic.com -
r240:737c66b6 default
parent child Browse files
Show More
@@ -0,0 +1,68 b''
1 #!/bin/bash
2 #
3 # hgmerge - default merge helper for Mercurial
4 #
5 # This tries to find a way to do three-way merge on the current system.
6 # The result ought to end up in $1.
7
8 set -e # bail out quickly on failure
9
10 LOCAL=$1
11 BASE=$2
12 OTHER=$3
13
14 # Back up our file
15 cp $LOCAL $LOCAL.orig
16
17 # Attempt to do a non-interactive merge
18 if which merge > /dev/null ; then
19 if merge $LOCAL $BASE $OTHER 2> /dev/null; then
20 # success!
21 exit 0
22 fi
23 cp $LOCAL.orig $LOCAL
24 fi
25
26 # try using kdiff3, which is fairly nice
27 if which kdiff3 > /dev/null ; then
28 if kdiff3 --auto $BASE $LOCAL $OTHER -o $LOCAL ; then
29 exit 0
30 else
31 exit 1
32 fi
33 fi
34
35 # try using tkdiff, which is a bit less sophisticated
36 if which tkdiff > /dev/null ; then
37 if tkdiff $LOCAL $OTHER -a $BASE -o $LOCAL ; then
38 exit 0
39 else
40 exit 1
41 fi
42 fi
43
44 # Attempt to do a merge with $EDITOR
45 if which merge > /dev/null ; then
46 echo "conflicts detected in $LOCAL"
47 merge $LOCAL $BASE $OTHER 2>/dev/null || $EDITOR $LOCAL
48 fi
49
50 # attempt to manually merge with diff and patch
51 if which diff > /dev/null ; then
52 if which patch > /dev/null ; then
53 T=`mktemp`
54 diff -u $BASE $OTHER > $T
55 if patch $LOCAL < $T ; then
56 exit 0
57 else
58 $EDITOR $LOCAL $LOCAL.rej
59 fi
60 rm $T
61 exit 1
62 fi
63 fi
64
65 echo "hgmerge: unable to find merge, tkdiff, kdiff3, or diff+patch!"
66 exit 1
67
68
@@ -1,121 +1,117 b''
1 Setting up Mercurial:
1 Setting up Mercurial:
2
2
3 Note: some distributions fails to include bits of distutils by
3 Note: some distributions fails to include bits of distutils by
4 default, you'll need python-dev to install. You'll also need a C
4 default, you'll need python-dev to install. You'll also need a C
5 compiler and a 3-way merge tool like merge, tkdiff, or kdiff3.
5 compiler and a 3-way merge tool like merge, tkdiff, or kdiff3.
6
6
7 First, unpack the source:
7 First, unpack the source:
8
8
9 $ tar xvzf mercurial-<ver>.tar.gz
9 $ tar xvzf mercurial-<ver>.tar.gz
10 $ cd mercurial-<ver>
10 $ cd mercurial-<ver>
11
11
12 To install system-wide:
12 To install system-wide:
13
13
14 $ python setup.py install # change python to python2.3 if 2.2 is default
14 $ python setup.py install # change python to python2.3 if 2.2 is default
15
15
16 To install in your home directory (~/bin and ~/lib, actually), run:
16 To install in your home directory (~/bin and ~/lib, actually), run:
17
17
18 $ python2.3 setup.py install --home=~
18 $ python2.3 setup.py install --home=~
19 $ export PYTHONPATH=${HOME}/lib/python # add this to your .bashrc
19 $ export PYTHONPATH=${HOME}/lib/python # add this to your .bashrc
20 $ export PATH=${HOME}/bin:$PATH #
20 $ export PATH=${HOME}/bin:$PATH #
21
21
22 You'll also need to set up a tool to handle three-way merges:
23
24 $ export HGMERGE=tkmerge # customize this
25
26 And finally:
22 And finally:
27
23
28 $ hg # test installation, show help
24 $ hg # test installation, show help
29
25
30 If you get complaints about missing modules, you probably haven't set
26 If you get complaints about missing modules, you probably haven't set
31 PYTHONPATH correctly.
27 PYTHONPATH correctly.
32
28
33 Setting up a Mercurial project:
29 Setting up a Mercurial project:
34
30
35 $ cd linux/
31 $ cd linux/
36 $ hg init # creates .hg
32 $ hg init # creates .hg
37 $ hg status # show changes between repo and working dir
33 $ hg status # show changes between repo and working dir
38 $ hg diff # generate a unidiff
34 $ hg diff # generate a unidiff
39 $ hg addremove # add all unknown files and remove all missing files
35 $ hg addremove # add all unknown files and remove all missing files
40 $ hg commit # commit all changes, edit changelog entry
36 $ hg commit # commit all changes, edit changelog entry
41 $ hg export # export a changeset as a diff
37 $ hg export # export a changeset as a diff
42
38
43 Mercurial will look for a file named .hgignore in the root of your
39 Mercurial will look for a file named .hgignore in the root of your
44 repository contains a set of regular expressions to ignore in file
40 repository contains a set of regular expressions to ignore in file
45 paths.
41 paths.
46
42
47 Mercurial commands:
43 Mercurial commands:
48
44
49 $ hg history # show changesets
45 $ hg history # show changesets
50 $ hg log Makefile # show commits per file
46 $ hg log Makefile # show commits per file
51 $ hg checkout # check out the tip revision
47 $ hg checkout # check out the tip revision
52 $ hg checkout <id> # check out a specified changeset
48 $ hg checkout <id> # check out a specified changeset
53 # IDs can be tags, revision numbers, or unique
49 # IDs can be tags, revision numbers, or unique
54 # subsets of changeset hash numbers
50 # subsets of changeset hash numbers
55 $ hg add foo # add a new file for the next commit
51 $ hg add foo # add a new file for the next commit
56 $ hg remove bar # mark a file as removed
52 $ hg remove bar # mark a file as removed
57 $ hg verify # check repo integrity
53 $ hg verify # check repo integrity
58 $ hg tags # show current tags
54 $ hg tags # show current tags
59 $ hg annotate [files] # show changeset numbers for each file line
55 $ hg annotate [files] # show changeset numbers for each file line
60
56
61 Branching and merging:
57 Branching and merging:
62
58
63 $ cd ..
59 $ cd ..
64 $ mkdir linux-work
60 $ mkdir linux-work
65 $ cd linux-work
61 $ cd linux-work
66 $ hg branch ../linux # create a new branch
62 $ hg branch ../linux # create a new branch
67 $ hg checkout # populate the working directory
63 $ hg checkout # populate the working directory
68 $ <make changes>
64 $ <make changes>
69 $ hg commit
65 $ hg commit
70 $ cd ../linux
66 $ cd ../linux
71 $ hg merge ../linux-work # pull changesets from linux-work
67 $ hg merge ../linux-work # pull changesets from linux-work
72
68
73 Importing patches:
69 Importing patches:
74
70
75 Fast:
71 Fast:
76 $ patch < ../p/foo.patch
72 $ patch < ../p/foo.patch
77 $ hg addremove
73 $ hg addremove
78 $ hg commit
74 $ hg commit
79
75
80 Faster:
76 Faster:
81 $ patch < ../p/foo.patch
77 $ patch < ../p/foo.patch
82 $ hg commit `lsdiff -p1 ../p/foo.patch`
78 $ hg commit `lsdiff -p1 ../p/foo.patch`
83
79
84 Fastest:
80 Fastest:
85 $ cat ../p/patchlist | xargs hg import -p1 -b ../p
81 $ cat ../p/patchlist | xargs hg import -p1 -b ../p
86
82
87 Exporting a patch:
83 Exporting a patch:
88
84
89 (make changes)
85 (make changes)
90 $ hg commit
86 $ hg commit
91 $ hg tip
87 $ hg tip
92 28237:747a537bd090880c29eae861df4d81b245aa0190
88 28237:747a537bd090880c29eae861df4d81b245aa0190
93 $ hg export 28237 > foo.patch # export changeset 28237
89 $ hg export 28237 > foo.patch # export changeset 28237
94
90
95 Network support:
91 Network support:
96
92
97 # pull the self-hosting hg repo
93 # pull the self-hosting hg repo
98 foo$ hg init
94 foo$ hg init
99 foo$ hg merge http://selenic.com/hg/
95 foo$ hg merge http://selenic.com/hg/
100 foo$ hg checkout # hg co works too
96 foo$ hg checkout # hg co works too
101
97
102 # export your current repo via HTTP with browsable interface
98 # export your current repo via HTTP with browsable interface
103 foo$ hg serve -n "My repo" -p 80
99 foo$ hg serve -n "My repo" -p 80
104
100
105 # merge changes from a remote machine
101 # merge changes from a remote machine
106 bar$ hg merge http://foo/
102 bar$ hg merge http://foo/
107 bar$ hg co # checkout the result
103 bar$ hg co # checkout the result
108
104
109 # Set up a CGI server on your webserver
105 # Set up a CGI server on your webserver
110 foo$ cp hgweb.cgi ~/public_html/hg-linux/index.cgi
106 foo$ cp hgweb.cgi ~/public_html/hg-linux/index.cgi
111 foo$ emacs ~/public_html/hg-linux/index.cgi # adjust the defaults
107 foo$ emacs ~/public_html/hg-linux/index.cgi # adjust the defaults
112
108
113 Symbolic repository names:
109 Symbolic repository names:
114
110
115 Mercurial uses an optional file called ~/.hgpaths to track repo
111 Mercurial uses an optional file called ~/.hgpaths to track repo
116 locations symbolically. Simply add a line with the name, a space, and
112 locations symbolically. Simply add a line with the name, a space, and
117 a URL:
113 a URL:
118
114
119 foo$ echo "main http://selenic.com/hg/" >> ~/.hgpaths
115 foo$ echo "main http://selenic.com/hg/" >> ~/.hgpaths
120 foo$ hg merge main
116 foo$ hg merge main
121 foo$ hg co
117 foo$ hg co
@@ -1,1071 +1,1071 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, 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 resolve(self, node):
861 def resolve(self, node):
862 pl = self.dirstate.parents()
862 pl = self.dirstate.parents()
863 if pl[1] != nullid:
863 if pl[1] != nullid:
864 self.ui.warn("last merge not committed")
864 self.ui.warn("last merge not committed")
865 return
865 return
866
866
867 p1, p2 = pl[0], node
867 p1, p2 = pl[0], node
868 m1n = self.changelog.read(p1)[0]
868 m1n = self.changelog.read(p1)[0]
869 m2n = self.changelog.read(p2)[0]
869 m2n = self.changelog.read(p2)[0]
870 man = self.manifest.ancestor(m1n, m2n)
870 man = self.manifest.ancestor(m1n, m2n)
871 m1 = self.manifest.read(m1n)
871 m1 = self.manifest.read(m1n)
872 m2 = self.manifest.read(m2n)
872 m2 = self.manifest.read(m2n)
873 ma = self.manifest.read(man)
873 ma = self.manifest.read(man)
874
874
875 (c, a, d, u) = self.diffdir(self.root)
875 (c, a, d, u) = self.diffdir(self.root)
876
876
877 # resolve the manifest to determine which files
877 # resolve the manifest to determine which files
878 # we care about merging
878 # we care about merging
879 self.ui.status("resolving manifests\n")
879 self.ui.status("resolving manifests\n")
880 self.ui.debug(" ancestor %s local %s remote %s\n" %
880 self.ui.debug(" ancestor %s local %s remote %s\n" %
881 (short(man), short(m1n), short(m2n)))
881 (short(man), short(m1n), short(m2n)))
882
882
883 merge = {}
883 merge = {}
884 get = {}
884 get = {}
885 remove = []
885 remove = []
886
886
887 # construct a working dir manifest
887 # construct a working dir manifest
888 mw = m1.copy()
888 mw = m1.copy()
889 for f in a + c:
889 for f in a + c:
890 mw[f] = nullid
890 mw[f] = nullid
891 for f in d:
891 for f in d:
892 del mw[f]
892 del mw[f]
893
893
894 for f, n in mw.iteritems():
894 for f, n in mw.iteritems():
895 if f in m2:
895 if f in m2:
896 if n != m2[f]:
896 if n != m2[f]:
897 self.ui.debug(" %s versions differ, do resolve\n" % f)
897 self.ui.debug(" %s versions differ, do resolve\n" % f)
898 merge[f] = (m1.get(f, nullid), m2[f])
898 merge[f] = (m1.get(f, nullid), m2[f])
899 del m2[f]
899 del m2[f]
900 elif f in ma:
900 elif f in ma:
901 if n != ma[f]:
901 if n != ma[f]:
902 r = self.ui.prompt(
902 r = self.ui.prompt(
903 (" local changed %s which remote deleted\n" % f) +
903 (" local changed %s which remote deleted\n" % f) +
904 "(k)eep or (d)elete?", "[kd]", "k")
904 "(k)eep or (d)elete?", "[kd]", "k")
905 if r == "d":
905 if r == "d":
906 remove.append(f)
906 remove.append(f)
907 else:
907 else:
908 self.ui.debug("other deleted %s\n" % f)
908 self.ui.debug("other deleted %s\n" % f)
909 pass # other deleted it
909 pass # other deleted it
910 else:
910 else:
911 self.ui.debug("local created %s\n" %f)
911 self.ui.debug("local created %s\n" %f)
912
912
913 for f, n in m2.iteritems():
913 for f, n in m2.iteritems():
914 if f in ma:
914 if f in ma:
915 if n != ma[f]:
915 if n != ma[f]:
916 r = self.ui.prompt(
916 r = self.ui.prompt(
917 ("remote changed %s which local deleted\n" % f) +
917 ("remote changed %s which local deleted\n" % f) +
918 "(k)eep or (d)elete?", "[kd]", "k")
918 "(k)eep or (d)elete?", "[kd]", "k")
919 if r == "d": remove.append(f)
919 if r == "d": remove.append(f)
920 else:
920 else:
921 pass # probably safe
921 pass # probably safe
922 else:
922 else:
923 self.ui.debug("remote created %s, do resolve\n" % f)
923 self.ui.debug("remote created %s, do resolve\n" % f)
924 get[f] = n
924 get[f] = n
925
925
926 del mw, m1, m2, ma
926 del mw, m1, m2, ma
927
927
928 self.dirstate.setparents(p1, p2)
928 self.dirstate.setparents(p1, p2)
929
929
930 # get the files we don't need to change
930 # get the files we don't need to change
931 files = get.keys()
931 files = get.keys()
932 files.sort()
932 files.sort()
933 for f in files:
933 for f in files:
934 if f[0] == "/": continue
934 if f[0] == "/": continue
935 self.ui.note(f, "\n")
935 self.ui.note(f, "\n")
936 t = self.file(f).revision(get[f])
936 t = self.file(f).revision(get[f])
937 try:
937 try:
938 file(f, "w").write(t)
938 file(f, "w").write(t)
939 except IOError:
939 except IOError:
940 os.makedirs(os.path.dirname(f))
940 os.makedirs(os.path.dirname(f))
941 file(f, "w").write(t)
941 file(f, "w").write(t)
942
942
943 # we have to remember what files we needed to get/change
943 # we have to remember what files we needed to get/change
944 # because any file that's different from either one of its
944 # because any file that's different from either one of its
945 # parents must be in the changeset
945 # parents must be in the changeset
946 self.dirstate.update(files, 'm')
946 self.dirstate.update(files, 'm')
947
947
948 # merge the tricky bits
948 # merge the tricky bits
949 files = merge.keys()
949 files = merge.keys()
950 files.sort()
950 files.sort()
951 for f in files:
951 for f in files:
952 m, o = merge[f]
952 m, o = merge[f]
953 self.merge3(f, m, o)
953 self.merge3(f, m, o)
954
954
955 # same here
955 # same here
956 self.dirstate.update(files, 'm')
956 self.dirstate.update(files, 'm')
957
957
958 for f in remove:
958 for f in remove:
959 self.ui.note("removing %s\n" % f)
959 self.ui.note("removing %s\n" % f)
960 #os.unlink(f)
960 #os.unlink(f)
961 self.dirstate.update(remove, 'r')
961 self.dirstate.update(remove, 'r')
962
962
963 def merge3(self, fn, my, other):
963 def merge3(self, fn, my, other):
964 """perform a 3-way merge in the working directory"""
964 """perform a 3-way merge in the working directory"""
965
965
966 def temp(prefix, node):
966 def temp(prefix, node):
967 pre = "%s~%s." % (os.path.basename(fn), prefix)
967 pre = "%s~%s." % (os.path.basename(fn), prefix)
968 (fd, name) = tempfile.mkstemp("", pre)
968 (fd, name) = tempfile.mkstemp("", pre)
969 f = os.fdopen(fd, "w")
969 f = os.fdopen(fd, "w")
970 f.write(fl.revision(node))
970 f.write(fl.revision(node))
971 f.close()
971 f.close()
972 return name
972 return name
973
973
974 fl = self.file(fn)
974 fl = self.file(fn)
975 base = fl.ancestor(my, other)
975 base = fl.ancestor(my, other)
976 a = fn
976 a = fn
977 b = temp("other", other)
977 b = temp("other", other)
978 c = temp("base", base)
978 c = temp("base", base)
979
979
980 self.ui.note("resolving %s\n" % fn)
980 self.ui.note("resolving %s\n" % fn)
981 self.ui.debug("file %s: other %s ancestor %s\n" %
981 self.ui.debug("file %s: other %s ancestor %s\n" %
982 (fn, short(other), short(base)))
982 (fn, short(other), short(base)))
983
983
984 cmd = os.environ["HGMERGE"]
984 cmd = os.environ.get("HGMERGE", "hgmerge")
985 r = os.system("%s %s %s %s %s" % (cmd, a, b, c, fn))
985 r = os.system("%s %s %s %s" % (cmd, a, b, c))
986 if r:
986 if r:
987 self.ui.warn("merging %s failed!\n" % f)
987 self.ui.warn("merging %s failed!\n" % f)
988
988
989 os.unlink(b)
989 os.unlink(b)
990 os.unlink(c)
990 os.unlink(c)
991
991
992 class remoterepository:
992 class remoterepository:
993 def __init__(self, ui, path):
993 def __init__(self, ui, path):
994 self.url = path
994 self.url = path
995 self.ui = ui
995 self.ui = ui
996
996
997 def do_cmd(self, cmd, **args):
997 def do_cmd(self, cmd, **args):
998 self.ui.debug("sending %s command\n" % cmd)
998 self.ui.debug("sending %s command\n" % cmd)
999 q = {"cmd": cmd}
999 q = {"cmd": cmd}
1000 q.update(args)
1000 q.update(args)
1001 qs = urllib.urlencode(q)
1001 qs = urllib.urlencode(q)
1002 cu = "%s?%s" % (self.url, qs)
1002 cu = "%s?%s" % (self.url, qs)
1003 return urllib.urlopen(cu)
1003 return urllib.urlopen(cu)
1004
1004
1005 def heads(self):
1005 def heads(self):
1006 d = self.do_cmd("heads").read()
1006 d = self.do_cmd("heads").read()
1007 try:
1007 try:
1008 return map(bin, d[:-1].split(" "))
1008 return map(bin, d[:-1].split(" "))
1009 except:
1009 except:
1010 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1010 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1011 raise
1011 raise
1012
1012
1013 def branches(self, nodes):
1013 def branches(self, nodes):
1014 n = " ".join(map(hex, nodes))
1014 n = " ".join(map(hex, nodes))
1015 d = self.do_cmd("branches", nodes=n).read()
1015 d = self.do_cmd("branches", nodes=n).read()
1016 try:
1016 try:
1017 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1017 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
1018 return br
1018 return br
1019 except:
1019 except:
1020 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1020 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1021 raise
1021 raise
1022
1022
1023 def between(self, pairs):
1023 def between(self, pairs):
1024 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1024 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
1025 d = self.do_cmd("between", pairs=n).read()
1025 d = self.do_cmd("between", pairs=n).read()
1026 try:
1026 try:
1027 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() ]
1028 return p
1028 return p
1029 except:
1029 except:
1030 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1030 self.ui.warn("unexpected response:\n" + d[:400] + "\n...\n")
1031 raise
1031 raise
1032
1032
1033 def changegroup(self, nodes):
1033 def changegroup(self, nodes):
1034 n = " ".join(map(hex, nodes))
1034 n = " ".join(map(hex, nodes))
1035 zd = zlib.decompressobj()
1035 zd = zlib.decompressobj()
1036 f = self.do_cmd("changegroup", roots=n)
1036 f = self.do_cmd("changegroup", roots=n)
1037 bytes = 0
1037 bytes = 0
1038 while 1:
1038 while 1:
1039 d = f.read(4096)
1039 d = f.read(4096)
1040 bytes += len(d)
1040 bytes += len(d)
1041 if not d:
1041 if not d:
1042 yield zd.flush()
1042 yield zd.flush()
1043 break
1043 break
1044 yield zd.decompress(d)
1044 yield zd.decompress(d)
1045 self.ui.note("%d bytes of data transfered\n" % bytes)
1045 self.ui.note("%d bytes of data transfered\n" % bytes)
1046
1046
1047 def repository(ui, path=None, create=0):
1047 def repository(ui, path=None, create=0):
1048 if path and path[:7] == "http://":
1048 if path and path[:7] == "http://":
1049 return remoterepository(ui, path)
1049 return remoterepository(ui, path)
1050 if path and path[:5] == "hg://":
1050 if path and path[:5] == "hg://":
1051 return remoterepository(ui, path.replace("hg://", "http://"))
1051 return remoterepository(ui, path.replace("hg://", "http://"))
1052 if path and path[:11] == "old-http://":
1052 if path and path[:11] == "old-http://":
1053 return localrepository(ui, path.replace("old-http://", "http://"))
1053 return localrepository(ui, path.replace("old-http://", "http://"))
1054 else:
1054 else:
1055 return localrepository(ui, path, create)
1055 return localrepository(ui, path, create)
1056
1056
1057 class httprangereader:
1057 class httprangereader:
1058 def __init__(self, url):
1058 def __init__(self, url):
1059 self.url = url
1059 self.url = url
1060 self.pos = 0
1060 self.pos = 0
1061 def seek(self, pos):
1061 def seek(self, pos):
1062 self.pos = pos
1062 self.pos = pos
1063 def read(self, bytes=None):
1063 def read(self, bytes=None):
1064 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
1064 opener = urllib2.build_opener(byterange.HTTPRangeHandler())
1065 urllib2.install_opener(opener)
1065 urllib2.install_opener(opener)
1066 req = urllib2.Request(self.url)
1066 req = urllib2.Request(self.url)
1067 end = ''
1067 end = ''
1068 if bytes: end = self.pos + bytes
1068 if bytes: end = self.pos + bytes
1069 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
1069 req.add_header('Range', 'bytes=%d-%s' % (self.pos, end))
1070 f = urllib2.urlopen(req)
1070 f = urllib2.urlopen(req)
1071 return f.read()
1071 return f.read()
@@ -1,32 +1,32 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 # This is the mercurial setup script.
3 # This is the mercurial setup script.
4 #
4 #
5 # './setup.py install', or
5 # './setup.py install', or
6 # './setup.py --help' for more options
6 # './setup.py --help' for more options
7
7
8 import glob
8 import glob
9 from distutils.core import setup, Extension
9 from distutils.core import setup, Extension
10 from distutils.command.install_data import install_data
10 from distutils.command.install_data import install_data
11
11
12 class install_package_data(install_data):
12 class install_package_data(install_data):
13 def finalize_options(self):
13 def finalize_options(self):
14 self.set_undefined_options('install',
14 self.set_undefined_options('install',
15 ('install_lib', 'install_dir'))
15 ('install_lib', 'install_dir'))
16 install_data.finalize_options(self)
16 install_data.finalize_options(self)
17
17
18 setup(name='mercurial',
18 setup(name='mercurial',
19 version='0.5b',
19 version='0.5b',
20 author='Matt Mackall',
20 author='Matt Mackall',
21 author_email='mpm@selenic.com',
21 author_email='mpm@selenic.com',
22 url='http://selenic.com/mercurial',
22 url='http://selenic.com/mercurial',
23 description='scalable distributed SCM',
23 description='scalable distributed SCM',
24 license='GNU GPL',
24 license='GNU GPL',
25 packages=['mercurial'],
25 packages=['mercurial'],
26 ext_modules=[Extension('mercurial.mpatch', ['mercurial/mpatch.c'])],
26 ext_modules=[Extension('mercurial.mpatch', ['mercurial/mpatch.c'])],
27 data_files=[('mercurial/templates',
27 data_files=[('mercurial/templates',
28 ['templates/map'] +
28 ['templates/map'] +
29 glob.glob('templates/map-*') +
29 glob.glob('templates/map-*') +
30 glob.glob('templates/*.tmpl'))],
30 glob.glob('templates/*.tmpl'))],
31 cmdclass = { 'install_data' : install_package_data },
31 cmdclass = { 'install_data' : install_package_data },
32 scripts=['hg'])
32 scripts=['hg', 'hgmerge'])
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now