##// END OF EJS Templates
merge: use file size stored in revlog index...
Matt Mackall -
r2898:db397c38 default
parent child Browse files
Show More
@@ -1,117 +1,127
1 # filelog.py - file history class for mercurial
1 # filelog.py - file history class for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 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 from revlog import *
8 from revlog import *
9 from demandload import *
9 from demandload import *
10 demandload(globals(), "bdiff os")
10 demandload(globals(), "bdiff os")
11
11
12 class filelog(revlog):
12 class filelog(revlog):
13 def __init__(self, opener, path, defversion=REVLOG_DEFAULT_VERSION):
13 def __init__(self, opener, path, defversion=REVLOG_DEFAULT_VERSION):
14 revlog.__init__(self, opener,
14 revlog.__init__(self, opener,
15 os.path.join("data", self.encodedir(path + ".i")),
15 os.path.join("data", self.encodedir(path + ".i")),
16 os.path.join("data", self.encodedir(path + ".d")),
16 os.path.join("data", self.encodedir(path + ".d")),
17 defversion)
17 defversion)
18
18
19 # This avoids a collision between a file named foo and a dir named
19 # This avoids a collision between a file named foo and a dir named
20 # foo.i or foo.d
20 # foo.i or foo.d
21 def encodedir(self, path):
21 def encodedir(self, path):
22 return (path
22 return (path
23 .replace(".hg/", ".hg.hg/")
23 .replace(".hg/", ".hg.hg/")
24 .replace(".i/", ".i.hg/")
24 .replace(".i/", ".i.hg/")
25 .replace(".d/", ".d.hg/"))
25 .replace(".d/", ".d.hg/"))
26
26
27 def decodedir(self, path):
27 def decodedir(self, path):
28 return (path
28 return (path
29 .replace(".d.hg/", ".d/")
29 .replace(".d.hg/", ".d/")
30 .replace(".i.hg/", ".i/")
30 .replace(".i.hg/", ".i/")
31 .replace(".hg.hg/", ".hg/"))
31 .replace(".hg.hg/", ".hg/"))
32
32
33 def read(self, node):
33 def read(self, node):
34 t = self.revision(node)
34 t = self.revision(node)
35 if not t.startswith('\1\n'):
35 if not t.startswith('\1\n'):
36 return t
36 return t
37 s = t.index('\1\n', 2)
37 s = t.index('\1\n', 2)
38 return t[s+2:]
38 return t[s+2:]
39
39
40 def readmeta(self, node):
40 def readmeta(self, node):
41 t = self.revision(node)
41 t = self.revision(node)
42 if not t.startswith('\1\n'):
42 if not t.startswith('\1\n'):
43 return {}
43 return {}
44 s = t.index('\1\n', 2)
44 s = t.index('\1\n', 2)
45 mt = t[2:s]
45 mt = t[2:s]
46 m = {}
46 m = {}
47 for l in mt.splitlines():
47 for l in mt.splitlines():
48 k, v = l.split(": ", 1)
48 k, v = l.split(": ", 1)
49 m[k] = v
49 m[k] = v
50 return m
50 return m
51
51
52 def add(self, text, meta, transaction, link, p1=None, p2=None):
52 def add(self, text, meta, transaction, link, p1=None, p2=None):
53 if meta or text.startswith('\1\n'):
53 if meta or text.startswith('\1\n'):
54 mt = ""
54 mt = ""
55 if meta:
55 if meta:
56 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
56 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
57 text = "\1\n%s\1\n%s" % ("".join(mt), text)
57 text = "\1\n%s\1\n%s" % ("".join(mt), text)
58 return self.addrevision(text, transaction, link, p1, p2)
58 return self.addrevision(text, transaction, link, p1, p2)
59
59
60 def renamed(self, node):
60 def renamed(self, node):
61 if self.parents(node)[0] != nullid:
61 if self.parents(node)[0] != nullid:
62 return False
62 return False
63 m = self.readmeta(node)
63 m = self.readmeta(node)
64 if m and m.has_key("copy"):
64 if m and m.has_key("copy"):
65 return (m["copy"], bin(m["copyrev"]))
65 return (m["copy"], bin(m["copyrev"]))
66 return False
66 return False
67
67
68 def size(self, rev):
69 """return the size of a given revision"""
70
71 # for revisions with renames, we have to go the slow way
72 node = self.node(rev)
73 if self.renamed(node):
74 return len(self.read(node))
75
76 return revlog.size(self, rev)
77
68 def cmp(self, node, text):
78 def cmp(self, node, text):
69 """compare text with a given file revision"""
79 """compare text with a given file revision"""
70
80
71 # for renames, we have to go the slow way
81 # for renames, we have to go the slow way
72 if self.renamed(node):
82 if self.renamed(node):
73 t2 = self.read(node)
83 t2 = self.read(node)
74 return t2 != text
84 return t2 != text
75
85
76 return revlog.cmp(self, node, text)
86 return revlog.cmp(self, node, text)
77
87
78 def annotate(self, node):
88 def annotate(self, node):
79
89
80 def decorate(text, rev):
90 def decorate(text, rev):
81 return ([rev] * len(text.splitlines()), text)
91 return ([rev] * len(text.splitlines()), text)
82
92
83 def pair(parent, child):
93 def pair(parent, child):
84 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
94 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
85 child[0][b1:b2] = parent[0][a1:a2]
95 child[0][b1:b2] = parent[0][a1:a2]
86 return child
96 return child
87
97
88 # find all ancestors
98 # find all ancestors
89 needed = {node:1}
99 needed = {node:1}
90 visit = [node]
100 visit = [node]
91 while visit:
101 while visit:
92 n = visit.pop(0)
102 n = visit.pop(0)
93 for p in self.parents(n):
103 for p in self.parents(n):
94 if p not in needed:
104 if p not in needed:
95 needed[p] = 1
105 needed[p] = 1
96 visit.append(p)
106 visit.append(p)
97 else:
107 else:
98 # count how many times we'll use this
108 # count how many times we'll use this
99 needed[p] += 1
109 needed[p] += 1
100
110
101 # sort by revision which is a topological order
111 # sort by revision which is a topological order
102 visit = [ (self.rev(n), n) for n in needed.keys() ]
112 visit = [ (self.rev(n), n) for n in needed.keys() ]
103 visit.sort()
113 visit.sort()
104 hist = {}
114 hist = {}
105
115
106 for r,n in visit:
116 for r,n in visit:
107 curr = decorate(self.read(n), self.linkrev(n))
117 curr = decorate(self.read(n), self.linkrev(n))
108 for p in self.parents(n):
118 for p in self.parents(n):
109 if p != nullid:
119 if p != nullid:
110 curr = pair(hist[p], curr)
120 curr = pair(hist[p], curr)
111 # trim the history of unneeded revs
121 # trim the history of unneeded revs
112 needed[p] -= 1
122 needed[p] -= 1
113 if not needed[p]:
123 if not needed[p]:
114 del hist[p]
124 del hist[p]
115 hist[n] = curr
125 hist[n] = curr
116
126
117 return zip(hist[n][0], hist[n][1].splitlines(1))
127 return zip(hist[n][0], hist[n][1].splitlines(1))
@@ -1,332 +1,333
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 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 from node import *
8 from node import *
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "util os tempfile")
11 demandload(globals(), "util os tempfile")
12
12
13 def fmerge(f, local, other, ancestor):
13 def fmerge(f, local, other, ancestor):
14 """merge executable flags"""
14 """merge executable flags"""
15 a, b, c = ancestor.execf(f), local.execf(f), other.execf(f)
15 a, b, c = ancestor.execf(f), local.execf(f), other.execf(f)
16 return ((a^b) | (a^c)) ^ a
16 return ((a^b) | (a^c)) ^ a
17
17
18 def merge3(repo, fn, my, other, p1, p2):
18 def merge3(repo, fn, my, other, p1, p2):
19 """perform a 3-way merge in the working directory"""
19 """perform a 3-way merge in the working directory"""
20
20
21 def temp(prefix, node):
21 def temp(prefix, node):
22 pre = "%s~%s." % (os.path.basename(fn), prefix)
22 pre = "%s~%s." % (os.path.basename(fn), prefix)
23 (fd, name) = tempfile.mkstemp(prefix=pre)
23 (fd, name) = tempfile.mkstemp(prefix=pre)
24 f = os.fdopen(fd, "wb")
24 f = os.fdopen(fd, "wb")
25 repo.wwrite(fn, fl.read(node), f)
25 repo.wwrite(fn, fl.read(node), f)
26 f.close()
26 f.close()
27 return name
27 return name
28
28
29 fl = repo.file(fn)
29 fl = repo.file(fn)
30 base = fl.ancestor(my, other)
30 base = fl.ancestor(my, other)
31 a = repo.wjoin(fn)
31 a = repo.wjoin(fn)
32 b = temp("base", base)
32 b = temp("base", base)
33 c = temp("other", other)
33 c = temp("other", other)
34
34
35 repo.ui.note(_("resolving %s\n") % fn)
35 repo.ui.note(_("resolving %s\n") % fn)
36 repo.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
36 repo.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
37 (fn, short(my), short(other), short(base)))
37 (fn, short(my), short(other), short(base)))
38
38
39 cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge")
39 cmd = (os.environ.get("HGMERGE") or repo.ui.config("ui", "merge")
40 or "hgmerge")
40 or "hgmerge")
41 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root,
41 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=repo.root,
42 environ={'HG_FILE': fn,
42 environ={'HG_FILE': fn,
43 'HG_MY_NODE': p1,
43 'HG_MY_NODE': p1,
44 'HG_OTHER_NODE': p2,
44 'HG_OTHER_NODE': p2,
45 'HG_FILE_MY_NODE': hex(my),
45 'HG_FILE_MY_NODE': hex(my),
46 'HG_FILE_OTHER_NODE': hex(other),
46 'HG_FILE_OTHER_NODE': hex(other),
47 'HG_FILE_BASE_NODE': hex(base)})
47 'HG_FILE_BASE_NODE': hex(base)})
48 if r:
48 if r:
49 repo.ui.warn(_("merging %s failed!\n") % fn)
49 repo.ui.warn(_("merging %s failed!\n") % fn)
50
50
51 os.unlink(b)
51 os.unlink(b)
52 os.unlink(c)
52 os.unlink(c)
53 return r
53 return r
54
54
55 def update(repo, node, branchmerge=False, force=False, partial=None,
55 def update(repo, node, branchmerge=False, force=False, partial=None,
56 wlock=None, show_stats=True, remind=True):
56 wlock=None, show_stats=True, remind=True):
57
57
58 overwrite = force and not branchmerge
58 overwrite = force and not branchmerge
59 forcemerge = force and branchmerge
59 forcemerge = force and branchmerge
60
60
61 if not wlock:
61 if not wlock:
62 wlock = repo.wlock()
62 wlock = repo.wlock()
63
63
64 ### check phase
64 ### check phase
65
65
66 pl = repo.dirstate.parents()
66 pl = repo.dirstate.parents()
67 if not overwrite and pl[1] != nullid:
67 if not overwrite and pl[1] != nullid:
68 raise util.Abort(_("outstanding uncommitted merges"))
68 raise util.Abort(_("outstanding uncommitted merges"))
69
69
70 p1, p2 = pl[0], node
70 p1, p2 = pl[0], node
71 pa = repo.changelog.ancestor(p1, p2)
71 pa = repo.changelog.ancestor(p1, p2)
72
72
73 # is there a linear path from p1 to p2?
73 # is there a linear path from p1 to p2?
74 linear_path = (pa == p1 or pa == p2)
74 linear_path = (pa == p1 or pa == p2)
75 if branchmerge and linear_path:
75 if branchmerge and linear_path:
76 raise util.Abort(_("there is nothing to merge, just use "
76 raise util.Abort(_("there is nothing to merge, just use "
77 "'hg update' or look at 'hg heads'"))
77 "'hg update' or look at 'hg heads'"))
78
78
79 if not overwrite and not linear_path and not branchmerge:
79 if not overwrite and not linear_path and not branchmerge:
80 raise util.Abort(_("update spans branches, use 'hg merge' "
80 raise util.Abort(_("update spans branches, use 'hg merge' "
81 "or 'hg update -C' to lose changes"))
81 "or 'hg update -C' to lose changes"))
82
82
83 modified, added, removed, deleted, unknown = repo.status()[:5]
83 modified, added, removed, deleted, unknown = repo.status()[:5]
84 if branchmerge and not forcemerge:
84 if branchmerge and not forcemerge:
85 if modified or added or removed:
85 if modified or added or removed:
86 raise util.Abort(_("outstanding uncommitted changes"))
86 raise util.Abort(_("outstanding uncommitted changes"))
87
87
88 m1n = repo.changelog.read(p1)[0]
88 m1n = repo.changelog.read(p1)[0]
89 m2n = repo.changelog.read(p2)[0]
89 m2n = repo.changelog.read(p2)[0]
90 man = repo.manifest.ancestor(m1n, m2n)
90 man = repo.manifest.ancestor(m1n, m2n)
91 m1 = repo.manifest.read(m1n)
91 m1 = repo.manifest.read(m1n)
92 m2 = repo.manifest.read(m2n).copy()
92 m2 = repo.manifest.read(m2n).copy()
93 ma = repo.manifest.read(man)
93 ma = repo.manifest.read(man)
94
94
95 if not force:
95 if not force:
96 for f in unknown:
96 for f in unknown:
97 if f in m2:
97 if f in m2:
98 if repo.file(f).cmp(m2[f], repo.wread(f)):
98 if repo.file(f).cmp(m2[f], repo.wread(f)):
99 raise util.Abort(_("'%s' already exists in the working"
99 raise util.Abort(_("'%s' already exists in the working"
100 " dir and differs from remote") % f)
100 " dir and differs from remote") % f)
101
101
102 # resolve the manifest to determine which files
102 # resolve the manifest to determine which files
103 # we care about merging
103 # we care about merging
104 repo.ui.note(_("resolving manifests\n"))
104 repo.ui.note(_("resolving manifests\n"))
105 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s linear %s\n") %
105 repo.ui.debug(_(" overwrite %s branchmerge %s partial %s linear %s\n") %
106 (overwrite, branchmerge, bool(partial), linear_path))
106 (overwrite, branchmerge, bool(partial), linear_path))
107 repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
107 repo.ui.debug(_(" ancestor %s local %s remote %s\n") %
108 (short(man), short(m1n), short(m2n)))
108 (short(man), short(m1n), short(m2n)))
109
109
110 merge = {}
110 merge = {}
111 get = {}
111 get = {}
112 remove = []
112 remove = []
113 forget = []
113 forget = []
114
114
115 # construct a working dir manifest
115 # construct a working dir manifest
116 mw = m1.copy()
116 mw = m1.copy()
117 umap = dict.fromkeys(unknown)
117 umap = dict.fromkeys(unknown)
118
118
119 for f in added + modified + unknown:
119 for f in added + modified + unknown:
120 mw[f] = ""
120 mw[f] = ""
121 # is the wfile new and matches m2?
121 # is the wfile new and matches m2?
122 if (f not in m1 and f in m2 and
122 if (f not in m1 and f in m2 and
123 not repo.file(f).cmp(m2[f], repo.wread(f))):
123 not repo.file(f).cmp(m2[f], repo.wread(f))):
124 mw[f] = m2[f]
124 mw[f] = m2[f]
125
125
126 mw.set(f, util.is_exec(repo.wjoin(f), mw.execf(f)))
126 mw.set(f, util.is_exec(repo.wjoin(f), mw.execf(f)))
127
127
128 for f in deleted + removed:
128 for f in deleted + removed:
129 if f in mw:
129 if f in mw:
130 del mw[f]
130 del mw[f]
131
131
132 # If we're jumping between revisions (as opposed to merging),
132 # If we're jumping between revisions (as opposed to merging),
133 # and if neither the working directory nor the target rev has
133 # and if neither the working directory nor the target rev has
134 # the file, then we need to remove it from the dirstate, to
134 # the file, then we need to remove it from the dirstate, to
135 # prevent the dirstate from listing the file when it is no
135 # prevent the dirstate from listing the file when it is no
136 # longer in the manifest.
136 # longer in the manifest.
137 if linear_path and f not in m2:
137 if linear_path and f not in m2:
138 forget.append(f)
138 forget.append(f)
139
139
140 # Compare manifests
140 # Compare manifests
141 for f, n in mw.iteritems():
141 for f, n in mw.iteritems():
142 if partial and not partial(f):
142 if partial and not partial(f):
143 continue
143 continue
144 if f in m2:
144 if f in m2:
145 s = 0
145 s = 0
146
146
147 # are files different?
147 # are files different?
148 if n != m2[f]:
148 if n != m2[f]:
149 a = ma.get(f, nullid)
149 a = ma.get(f, nullid)
150 # are both different from the ancestor?
150 # are both different from the ancestor?
151 if n != a and m2[f] != a:
151 if n != a and m2[f] != a:
152 repo.ui.debug(_(" %s versions differ, resolve\n") % f)
152 repo.ui.debug(_(" %s versions differ, resolve\n") % f)
153 merge[f] = (fmerge(f, mw, m2, ma), m1.get(f, nullid), m2[f])
153 merge[f] = (fmerge(f, mw, m2, ma), m1.get(f, nullid), m2[f])
154 s = 1
154 s = 1
155 # are we clobbering?
155 # are we clobbering?
156 # is remote's version newer?
156 # is remote's version newer?
157 # or are we going back in time?
157 # or are we going back in time?
158 elif overwrite or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
158 elif overwrite or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
159 repo.ui.debug(_(" remote %s is newer, get\n") % f)
159 repo.ui.debug(_(" remote %s is newer, get\n") % f)
160 get[f] = (m2.execf(f), m2[f])
160 get[f] = (m2.execf(f), m2[f])
161 s = 1
161 s = 1
162 elif f in umap or f in added:
162 elif f in umap or f in added:
163 # this unknown file is the same as the checkout
163 # this unknown file is the same as the checkout
164 # we need to reset the dirstate if the file was added
164 # we need to reset the dirstate if the file was added
165 get[f] = (m2.execf(f), m2[f])
165 get[f] = (m2.execf(f), m2[f])
166
166
167 if not s and mw.execf(f) != m2.execf(f):
167 if not s and mw.execf(f) != m2.execf(f):
168 if overwrite:
168 if overwrite:
169 repo.ui.debug(_(" updating permissions for %s\n") % f)
169 repo.ui.debug(_(" updating permissions for %s\n") % f)
170 util.set_exec(repo.wjoin(f), m2.execf(f))
170 util.set_exec(repo.wjoin(f), m2.execf(f))
171 else:
171 else:
172 if fmerge(f, mw, m2, ma) != mw.execf(f):
172 if fmerge(f, mw, m2, ma) != mw.execf(f):
173 repo.ui.debug(_(" updating permissions for %s\n")
173 repo.ui.debug(_(" updating permissions for %s\n")
174 % f)
174 % f)
175 util.set_exec(repo.wjoin(f), mode)
175 util.set_exec(repo.wjoin(f), mode)
176 del m2[f]
176 del m2[f]
177 elif f in ma:
177 elif f in ma:
178 if n != ma[f]:
178 if n != ma[f]:
179 r = _("d")
179 r = _("d")
180 if not overwrite and (linear_path or branchmerge):
180 if not overwrite and (linear_path or branchmerge):
181 r = repo.ui.prompt(
181 r = repo.ui.prompt(
182 (_(" local changed %s which remote deleted\n") % f) +
182 (_(" local changed %s which remote deleted\n") % f) +
183 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
183 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
184 if r == _("d"):
184 if r == _("d"):
185 remove.append(f)
185 remove.append(f)
186 else:
186 else:
187 repo.ui.debug(_("other deleted %s\n") % f)
187 repo.ui.debug(_("other deleted %s\n") % f)
188 remove.append(f) # other deleted it
188 remove.append(f) # other deleted it
189 else:
189 else:
190 # file is created on branch or in working directory
190 # file is created on branch or in working directory
191 if overwrite and f not in umap:
191 if overwrite and f not in umap:
192 repo.ui.debug(_("remote deleted %s, clobbering\n") % f)
192 repo.ui.debug(_("remote deleted %s, clobbering\n") % f)
193 remove.append(f)
193 remove.append(f)
194 elif n == m1.get(f, nullid): # same as parent
194 elif n == m1.get(f, nullid): # same as parent
195 if p2 == pa: # going backwards?
195 if p2 == pa: # going backwards?
196 repo.ui.debug(_("remote deleted %s\n") % f)
196 repo.ui.debug(_("remote deleted %s\n") % f)
197 remove.append(f)
197 remove.append(f)
198 else:
198 else:
199 repo.ui.debug(_("local modified %s, keeping\n") % f)
199 repo.ui.debug(_("local modified %s, keeping\n") % f)
200 else:
200 else:
201 repo.ui.debug(_("working dir created %s, keeping\n") % f)
201 repo.ui.debug(_("working dir created %s, keeping\n") % f)
202
202
203 for f, n in m2.iteritems():
203 for f, n in m2.iteritems():
204 if partial and not partial(f):
204 if partial and not partial(f):
205 continue
205 continue
206 if f[0] == "/":
206 if f[0] == "/":
207 continue
207 continue
208 if f in ma and n != ma[f]:
208 if f in ma and n != ma[f]:
209 r = _("k")
209 r = _("k")
210 if not overwrite and (linear_path or branchmerge):
210 if not overwrite and (linear_path or branchmerge):
211 r = repo.ui.prompt(
211 r = repo.ui.prompt(
212 (_("remote changed %s which local deleted\n") % f) +
212 (_("remote changed %s which local deleted\n") % f) +
213 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
213 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
214 if r == _("k"):
214 if r == _("k"):
215 get[f] = (m2.execf(f), n)
215 get[f] = (m2.execf(f), n)
216 elif f not in ma:
216 elif f not in ma:
217 repo.ui.debug(_("remote created %s\n") % f)
217 repo.ui.debug(_("remote created %s\n") % f)
218 get[f] = (m2.execf(f), n)
218 get[f] = (m2.execf(f), n)
219 else:
219 else:
220 if overwrite or p2 == pa: # going backwards?
220 if overwrite or p2 == pa: # going backwards?
221 repo.ui.debug(_("local deleted %s, recreating\n") % f)
221 repo.ui.debug(_("local deleted %s, recreating\n") % f)
222 get[f] = (m2.execf(f), n)
222 get[f] = (m2.execf(f), n)
223 else:
223 else:
224 repo.ui.debug(_("local deleted %s\n") % f)
224 repo.ui.debug(_("local deleted %s\n") % f)
225
225
226 del mw, m1, m2, ma
226 del mw, m1, m2, ma
227
227
228 ### apply phase
228 ### apply phase
229
229
230 if overwrite:
230 if overwrite:
231 for f in merge:
231 for f in merge:
232 get[f] = merge[f][:2]
232 get[f] = merge[f][:2]
233 merge = {}
233 merge = {}
234
234
235 if linear_path or overwrite:
235 if linear_path or overwrite:
236 # we don't need to do any magic, just jump to the new rev
236 # we don't need to do any magic, just jump to the new rev
237 p1, p2 = p2, nullid
237 p1, p2 = p2, nullid
238
238
239 xp1 = hex(p1)
239 xp1 = hex(p1)
240 xp2 = hex(p2)
240 xp2 = hex(p2)
241 if p2 == nullid: xxp2 = ''
241 if p2 == nullid: xxp2 = ''
242 else: xxp2 = xp2
242 else: xxp2 = xp2
243
243
244 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2)
244 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2)
245
245
246 # get the files we don't need to change
246 # get the files we don't need to change
247 files = get.keys()
247 files = get.keys()
248 files.sort()
248 files.sort()
249 for f in files:
249 for f in files:
250 flag, node = get[f]
250 flag, node = get[f]
251 if f[0] == "/":
251 if f[0] == "/":
252 continue
252 continue
253 repo.ui.note(_("getting %s\n") % f)
253 repo.ui.note(_("getting %s\n") % f)
254 t = repo.file(f).read(node)
254 t = repo.file(f).read(node)
255 repo.wwrite(f, t)
255 repo.wwrite(f, t)
256 util.set_exec(repo.wjoin(f), flag)
256 util.set_exec(repo.wjoin(f), flag)
257 if not partial:
257 if not partial:
258 if branchmerge:
258 if branchmerge:
259 repo.dirstate.update([f], 'n', st_mtime=-1)
259 repo.dirstate.update([f], 'n', st_mtime=-1)
260 else:
260 else:
261 repo.dirstate.update([f], 'n')
261 repo.dirstate.update([f], 'n')
262
262
263 # merge the tricky bits
263 # merge the tricky bits
264 unresolved = []
264 unresolved = []
265 files = merge.keys()
265 files = merge.keys()
266 files.sort()
266 files.sort()
267 for f in files:
267 for f in files:
268 repo.ui.status(_("merging %s\n") % f)
268 repo.ui.status(_("merging %s\n") % f)
269 flag, my, other = merge[f]
269 flag, my, other = merge[f]
270 ret = merge3(repo, f, my, other, xp1, xp2)
270 ret = merge3(repo, f, my, other, xp1, xp2)
271 if ret:
271 if ret:
272 unresolved.append(f)
272 unresolved.append(f)
273 util.set_exec(repo.wjoin(f), flag)
273 util.set_exec(repo.wjoin(f), flag)
274 if not partial:
274 if not partial:
275 if branchmerge:
275 if branchmerge:
276 # We've done a branch merge, mark this file as merged
276 # We've done a branch merge, mark this file as merged
277 # so that we properly record the merger later
277 # so that we properly record the merger later
278 repo.dirstate.update([f], 'm')
278 repo.dirstate.update([f], 'm')
279 else:
279 else:
280 # We've update-merged a locally modified file, so
280 # We've update-merged a locally modified file, so
281 # we set the dirstate to emulate a normal checkout
281 # we set the dirstate to emulate a normal checkout
282 # of that file some time in the past. Thus our
282 # of that file some time in the past. Thus our
283 # merge will appear as a normal local file
283 # merge will appear as a normal local file
284 # modification.
284 # modification.
285 f_len = len(repo.file(f).read(other))
285 fl = repo.file(f)
286 f_len = fl.size(fl.rev(other))
286 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
287 repo.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
287
288
288 remove.sort()
289 remove.sort()
289 for f in remove:
290 for f in remove:
290 repo.ui.note(_("removing %s\n") % f)
291 repo.ui.note(_("removing %s\n") % f)
291 util.audit_path(f)
292 util.audit_path(f)
292 try:
293 try:
293 util.unlink(repo.wjoin(f))
294 util.unlink(repo.wjoin(f))
294 except OSError, inst:
295 except OSError, inst:
295 if inst.errno != errno.ENOENT:
296 if inst.errno != errno.ENOENT:
296 repo.ui.warn(_("update failed to remove %s: %s!\n") %
297 repo.ui.warn(_("update failed to remove %s: %s!\n") %
297 (f, inst.strerror))
298 (f, inst.strerror))
298 if not partial:
299 if not partial:
299 if branchmerge:
300 if branchmerge:
300 repo.dirstate.update(remove, 'r')
301 repo.dirstate.update(remove, 'r')
301 else:
302 else:
302 repo.dirstate.forget(remove)
303 repo.dirstate.forget(remove)
303
304
304 if not partial:
305 if not partial:
305 repo.dirstate.setparents(p1, p2)
306 repo.dirstate.setparents(p1, p2)
306 repo.dirstate.forget(forget)
307 repo.dirstate.forget(forget)
307
308
308 if show_stats:
309 if show_stats:
309 stats = ((len(get), _("updated")),
310 stats = ((len(get), _("updated")),
310 (len(merge) - len(unresolved), _("merged")),
311 (len(merge) - len(unresolved), _("merged")),
311 (len(remove), _("removed")),
312 (len(remove), _("removed")),
312 (len(unresolved), _("unresolved")))
313 (len(unresolved), _("unresolved")))
313 note = ", ".join([_("%d files %s") % s for s in stats])
314 note = ", ".join([_("%d files %s") % s for s in stats])
314 repo.ui.status("%s\n" % note)
315 repo.ui.status("%s\n" % note)
315 if not partial:
316 if not partial:
316 if branchmerge:
317 if branchmerge:
317 if unresolved:
318 if unresolved:
318 repo.ui.status(_("There are unresolved merges,"
319 repo.ui.status(_("There are unresolved merges,"
319 " you can redo the full merge using:\n"
320 " you can redo the full merge using:\n"
320 " hg update -C %s\n"
321 " hg update -C %s\n"
321 " hg merge %s\n"
322 " hg merge %s\n"
322 % (repo.changelog.rev(p1),
323 % (repo.changelog.rev(p1),
323 repo.changelog.rev(p2))))
324 repo.changelog.rev(p2))))
324 elif remind:
325 elif remind:
325 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
326 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
326 elif unresolved:
327 elif unresolved:
327 repo.ui.status(_("There are unresolved merges with"
328 repo.ui.status(_("There are unresolved merges with"
328 " locally modified files.\n"))
329 " locally modified files.\n"))
329
330
330 repo.hook('update', parent1=xp1, parent2=xxp2, error=len(unresolved))
331 repo.hook('update', parent1=xp1, parent2=xxp2, error=len(unresolved))
331 return len(unresolved)
332 return len(unresolved)
332
333
General Comments 0
You need to be logged in to leave comments. Login now