##// END OF EJS Templates
merge: give hint as to how to discover uncommitted changes...
Steve Borho -
r8545:3682a19b default
parent child Browse files
Show More
@@ -1,503 +1,504 b''
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, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import nullid, nullrev, hex, bin
8 from node import nullid, nullrev, hex, bin
9 from i18n import _
9 from i18n import _
10 import util, filemerge, copies
10 import util, filemerge, copies
11 import errno, os, shutil
11 import errno, os, shutil
12
12
13 class mergestate(object):
13 class mergestate(object):
14 '''track 3-way merge state of individual files'''
14 '''track 3-way merge state of individual files'''
15 def __init__(self, repo):
15 def __init__(self, repo):
16 self._repo = repo
16 self._repo = repo
17 self._read()
17 self._read()
18 def reset(self, node=None):
18 def reset(self, node=None):
19 self._state = {}
19 self._state = {}
20 if node:
20 if node:
21 self._local = node
21 self._local = node
22 shutil.rmtree(self._repo.join("merge"), True)
22 shutil.rmtree(self._repo.join("merge"), True)
23 def _read(self):
23 def _read(self):
24 self._state = {}
24 self._state = {}
25 try:
25 try:
26 localnode = None
26 localnode = None
27 f = self._repo.opener("merge/state")
27 f = self._repo.opener("merge/state")
28 for i, l in enumerate(f):
28 for i, l in enumerate(f):
29 if i == 0:
29 if i == 0:
30 localnode = l[:-1]
30 localnode = l[:-1]
31 else:
31 else:
32 bits = l[:-1].split("\0")
32 bits = l[:-1].split("\0")
33 self._state[bits[0]] = bits[1:]
33 self._state[bits[0]] = bits[1:]
34 self._local = bin(localnode)
34 self._local = bin(localnode)
35 except IOError, err:
35 except IOError, err:
36 if err.errno != errno.ENOENT:
36 if err.errno != errno.ENOENT:
37 raise
37 raise
38 def _write(self):
38 def _write(self):
39 f = self._repo.opener("merge/state", "w")
39 f = self._repo.opener("merge/state", "w")
40 f.write(hex(self._local) + "\n")
40 f.write(hex(self._local) + "\n")
41 for d, v in self._state.iteritems():
41 for d, v in self._state.iteritems():
42 f.write("\0".join([d] + v) + "\n")
42 f.write("\0".join([d] + v) + "\n")
43 def add(self, fcl, fco, fca, fd, flags):
43 def add(self, fcl, fco, fca, fd, flags):
44 hash = util.sha1(fcl.path()).hexdigest()
44 hash = util.sha1(fcl.path()).hexdigest()
45 self._repo.opener("merge/" + hash, "w").write(fcl.data())
45 self._repo.opener("merge/" + hash, "w").write(fcl.data())
46 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
46 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
47 hex(fca.filenode()), fco.path(), flags]
47 hex(fca.filenode()), fco.path(), flags]
48 self._write()
48 self._write()
49 def __contains__(self, dfile):
49 def __contains__(self, dfile):
50 return dfile in self._state
50 return dfile in self._state
51 def __getitem__(self, dfile):
51 def __getitem__(self, dfile):
52 return self._state[dfile][0]
52 return self._state[dfile][0]
53 def __iter__(self):
53 def __iter__(self):
54 l = self._state.keys()
54 l = self._state.keys()
55 l.sort()
55 l.sort()
56 for f in l:
56 for f in l:
57 yield f
57 yield f
58 def mark(self, dfile, state):
58 def mark(self, dfile, state):
59 self._state[dfile][0] = state
59 self._state[dfile][0] = state
60 self._write()
60 self._write()
61 def resolve(self, dfile, wctx, octx):
61 def resolve(self, dfile, wctx, octx):
62 if self[dfile] == 'r':
62 if self[dfile] == 'r':
63 return 0
63 return 0
64 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
64 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
65 f = self._repo.opener("merge/" + hash)
65 f = self._repo.opener("merge/" + hash)
66 self._repo.wwrite(dfile, f.read(), flags)
66 self._repo.wwrite(dfile, f.read(), flags)
67 fcd = wctx[dfile]
67 fcd = wctx[dfile]
68 fco = octx[ofile]
68 fco = octx[ofile]
69 fca = self._repo.filectx(afile, fileid=anode)
69 fca = self._repo.filectx(afile, fileid=anode)
70 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
70 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
71 if not r:
71 if not r:
72 self.mark(dfile, 'r')
72 self.mark(dfile, 'r')
73 return r
73 return r
74
74
75 def _checkunknown(wctx, mctx):
75 def _checkunknown(wctx, mctx):
76 "check for collisions between unknown files and files in mctx"
76 "check for collisions between unknown files and files in mctx"
77 for f in wctx.unknown():
77 for f in wctx.unknown():
78 if f in mctx and mctx[f].cmp(wctx[f].data()):
78 if f in mctx and mctx[f].cmp(wctx[f].data()):
79 raise util.Abort(_("untracked file in working directory differs"
79 raise util.Abort(_("untracked file in working directory differs"
80 " from file in requested revision: '%s'") % f)
80 " from file in requested revision: '%s'") % f)
81
81
82 def _checkcollision(mctx):
82 def _checkcollision(mctx):
83 "check for case folding collisions in the destination context"
83 "check for case folding collisions in the destination context"
84 folded = {}
84 folded = {}
85 for fn in mctx:
85 for fn in mctx:
86 fold = fn.lower()
86 fold = fn.lower()
87 if fold in folded:
87 if fold in folded:
88 raise util.Abort(_("case-folding collision between %s and %s")
88 raise util.Abort(_("case-folding collision between %s and %s")
89 % (fn, folded[fold]))
89 % (fn, folded[fold]))
90 folded[fold] = fn
90 folded[fold] = fn
91
91
92 def _forgetremoved(wctx, mctx, branchmerge):
92 def _forgetremoved(wctx, mctx, branchmerge):
93 """
93 """
94 Forget removed files
94 Forget removed files
95
95
96 If we're jumping between revisions (as opposed to merging), and if
96 If we're jumping between revisions (as opposed to merging), and if
97 neither the working directory nor the target rev has the file,
97 neither the working directory nor the target rev has the file,
98 then we need to remove it from the dirstate, to prevent the
98 then we need to remove it from the dirstate, to prevent the
99 dirstate from listing the file when it is no longer in the
99 dirstate from listing the file when it is no longer in the
100 manifest.
100 manifest.
101
101
102 If we're merging, and the other revision has removed a file
102 If we're merging, and the other revision has removed a file
103 that is not present in the working directory, we need to mark it
103 that is not present in the working directory, we need to mark it
104 as removed.
104 as removed.
105 """
105 """
106
106
107 action = []
107 action = []
108 state = branchmerge and 'r' or 'f'
108 state = branchmerge and 'r' or 'f'
109 for f in wctx.deleted():
109 for f in wctx.deleted():
110 if f not in mctx:
110 if f not in mctx:
111 action.append((f, state))
111 action.append((f, state))
112
112
113 if not branchmerge:
113 if not branchmerge:
114 for f in wctx.removed():
114 for f in wctx.removed():
115 if f not in mctx:
115 if f not in mctx:
116 action.append((f, "f"))
116 action.append((f, "f"))
117
117
118 return action
118 return action
119
119
120 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
120 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
121 """
121 """
122 Merge p1 and p2 with ancestor ma and generate merge action list
122 Merge p1 and p2 with ancestor ma and generate merge action list
123
123
124 overwrite = whether we clobber working files
124 overwrite = whether we clobber working files
125 partial = function to filter file lists
125 partial = function to filter file lists
126 """
126 """
127
127
128 repo.ui.note(_("resolving manifests\n"))
128 repo.ui.note(_("resolving manifests\n"))
129 repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
129 repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
130 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))
130 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))
131
131
132 m1 = p1.manifest()
132 m1 = p1.manifest()
133 m2 = p2.manifest()
133 m2 = p2.manifest()
134 ma = pa.manifest()
134 ma = pa.manifest()
135 backwards = (pa == p2)
135 backwards = (pa == p2)
136 action = []
136 action = []
137 copy, copied, diverge = {}, {}, {}
137 copy, copied, diverge = {}, {}, {}
138
138
139 def fmerge(f, f2=None, fa=None):
139 def fmerge(f, f2=None, fa=None):
140 """merge flags"""
140 """merge flags"""
141 if not f2:
141 if not f2:
142 f2 = f
142 f2 = f
143 fa = f
143 fa = f
144 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
144 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
145 if m == n: # flags agree
145 if m == n: # flags agree
146 return m # unchanged
146 return m # unchanged
147 if m and n: # flags are set but don't agree
147 if m and n: # flags are set but don't agree
148 if not a: # both differ from parent
148 if not a: # both differ from parent
149 r = repo.ui.prompt(
149 r = repo.ui.prompt(
150 _(" conflicting flags for %s\n"
150 _(" conflicting flags for %s\n"
151 "(n)one, e(x)ec or sym(l)ink?") % f,
151 "(n)one, e(x)ec or sym(l)ink?") % f,
152 (_("&None"), _("E&xec"), _("Sym&link")), _("n"))
152 (_("&None"), _("E&xec"), _("Sym&link")), _("n"))
153 return r != _("n") and r or ''
153 return r != _("n") and r or ''
154 if m == a:
154 if m == a:
155 return n # changed from m to n
155 return n # changed from m to n
156 return m # changed from n to m
156 return m # changed from n to m
157 if m and m != a: # changed from a to m
157 if m and m != a: # changed from a to m
158 return m
158 return m
159 if n and n != a: # changed from a to n
159 if n and n != a: # changed from a to n
160 return n
160 return n
161 return '' # flag was cleared
161 return '' # flag was cleared
162
162
163 def act(msg, m, f, *args):
163 def act(msg, m, f, *args):
164 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
164 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
165 action.append((f, m) + args)
165 action.append((f, m) + args)
166
166
167 if pa and not (backwards or overwrite):
167 if pa and not (backwards or overwrite):
168 if repo.ui.configbool("merge", "followcopies", True):
168 if repo.ui.configbool("merge", "followcopies", True):
169 dirs = repo.ui.configbool("merge", "followdirs", True)
169 dirs = repo.ui.configbool("merge", "followdirs", True)
170 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
170 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
171 copied = set(copy.values())
171 copied = set(copy.values())
172 for of, fl in diverge.iteritems():
172 for of, fl in diverge.iteritems():
173 act("divergent renames", "dr", of, fl)
173 act("divergent renames", "dr", of, fl)
174
174
175 # Compare manifests
175 # Compare manifests
176 for f, n in m1.iteritems():
176 for f, n in m1.iteritems():
177 if partial and not partial(f):
177 if partial and not partial(f):
178 continue
178 continue
179 if f in m2:
179 if f in m2:
180 if overwrite or backwards:
180 if overwrite or backwards:
181 rflags = m2.flags(f)
181 rflags = m2.flags(f)
182 else:
182 else:
183 rflags = fmerge(f)
183 rflags = fmerge(f)
184 # are files different?
184 # are files different?
185 if n != m2[f]:
185 if n != m2[f]:
186 a = ma.get(f, nullid)
186 a = ma.get(f, nullid)
187 # are we clobbering?
187 # are we clobbering?
188 if overwrite:
188 if overwrite:
189 act("clobbering", "g", f, rflags)
189 act("clobbering", "g", f, rflags)
190 # or are we going back in time and clean?
190 # or are we going back in time and clean?
191 elif backwards:
191 elif backwards:
192 if not n[20:] or not p2[f].cmp(p1[f].data()):
192 if not n[20:] or not p2[f].cmp(p1[f].data()):
193 act("reverting", "g", f, rflags)
193 act("reverting", "g", f, rflags)
194 # are both different from the ancestor?
194 # are both different from the ancestor?
195 elif n != a and m2[f] != a:
195 elif n != a and m2[f] != a:
196 act("versions differ", "m", f, f, f, rflags, False)
196 act("versions differ", "m", f, f, f, rflags, False)
197 # is remote's version newer?
197 # is remote's version newer?
198 elif m2[f] != a:
198 elif m2[f] != a:
199 act("remote is newer", "g", f, rflags)
199 act("remote is newer", "g", f, rflags)
200 # local is newer, not overwrite, check mode bits
200 # local is newer, not overwrite, check mode bits
201 elif m1.flags(f) != rflags:
201 elif m1.flags(f) != rflags:
202 act("update permissions", "e", f, rflags)
202 act("update permissions", "e", f, rflags)
203 # contents same, check mode bits
203 # contents same, check mode bits
204 elif m1.flags(f) != rflags:
204 elif m1.flags(f) != rflags:
205 act("update permissions", "e", f, rflags)
205 act("update permissions", "e", f, rflags)
206 elif f in copied:
206 elif f in copied:
207 continue
207 continue
208 elif f in copy:
208 elif f in copy:
209 f2 = copy[f]
209 f2 = copy[f]
210 if f2 not in m2: # directory rename
210 if f2 not in m2: # directory rename
211 act("remote renamed directory to " + f2, "d",
211 act("remote renamed directory to " + f2, "d",
212 f, None, f2, m1.flags(f))
212 f, None, f2, m1.flags(f))
213 elif f2 in m1: # case 2 A,B/B/B
213 elif f2 in m1: # case 2 A,B/B/B
214 act("local copied to " + f2, "m",
214 act("local copied to " + f2, "m",
215 f, f2, f, fmerge(f, f2, f2), False)
215 f, f2, f, fmerge(f, f2, f2), False)
216 else: # case 4,21 A/B/B
216 else: # case 4,21 A/B/B
217 act("local moved to " + f2, "m",
217 act("local moved to " + f2, "m",
218 f, f2, f, fmerge(f, f2, f2), False)
218 f, f2, f, fmerge(f, f2, f2), False)
219 elif f in ma:
219 elif f in ma:
220 if n != ma[f] and not overwrite:
220 if n != ma[f] and not overwrite:
221 if repo.ui.prompt(
221 if repo.ui.prompt(
222 _(" local changed %s which remote deleted\n"
222 _(" local changed %s which remote deleted\n"
223 "use (c)hanged version or (d)elete?") % f,
223 "use (c)hanged version or (d)elete?") % f,
224 (_("&Changed"), _("&Delete")), _("c")) == _("d"):
224 (_("&Changed"), _("&Delete")), _("c")) == _("d"):
225 act("prompt delete", "r", f)
225 act("prompt delete", "r", f)
226 act("prompt keep", "a", f)
226 act("prompt keep", "a", f)
227 else:
227 else:
228 act("other deleted", "r", f)
228 act("other deleted", "r", f)
229 elif overwrite and n[20:] == "a": # do not erase the working copy
229 elif overwrite and n[20:] == "a": # do not erase the working copy
230 act("remote deleted", "f", f)
230 act("remote deleted", "f", f)
231 else:
231 else:
232 # file is created on branch or in working directory
232 # file is created on branch or in working directory
233 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
233 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
234 act("remote deleted", "r", f)
234 act("remote deleted", "r", f)
235
235
236 for f, n in m2.iteritems():
236 for f, n in m2.iteritems():
237 if partial and not partial(f):
237 if partial and not partial(f):
238 continue
238 continue
239 if f in m1:
239 if f in m1:
240 continue
240 continue
241 if f in copied:
241 if f in copied:
242 continue
242 continue
243 if f in copy:
243 if f in copy:
244 f2 = copy[f]
244 f2 = copy[f]
245 if f2 not in m1: # directory rename
245 if f2 not in m1: # directory rename
246 act("local renamed directory to " + f2, "d",
246 act("local renamed directory to " + f2, "d",
247 None, f, f2, m2.flags(f))
247 None, f, f2, m2.flags(f))
248 elif f2 in m2: # rename case 1, A/A,B/A
248 elif f2 in m2: # rename case 1, A/A,B/A
249 act("remote copied to " + f, "m",
249 act("remote copied to " + f, "m",
250 f2, f, f, fmerge(f2, f, f2), False)
250 f2, f, f, fmerge(f2, f, f2), False)
251 else: # case 3,20 A/B/A
251 else: # case 3,20 A/B/A
252 act("remote moved to " + f, "m",
252 act("remote moved to " + f, "m",
253 f2, f, f, fmerge(f2, f, f2), True)
253 f2, f, f, fmerge(f2, f, f2), True)
254 elif f in ma:
254 elif f in ma:
255 if overwrite or backwards:
255 if overwrite or backwards:
256 act("recreating", "g", f, m2.flags(f))
256 act("recreating", "g", f, m2.flags(f))
257 elif n != ma[f]:
257 elif n != ma[f]:
258 if repo.ui.prompt(
258 if repo.ui.prompt(
259 _("remote changed %s which local deleted\n"
259 _("remote changed %s which local deleted\n"
260 "use (c)hanged version or leave (d)eleted?") % f,
260 "use (c)hanged version or leave (d)eleted?") % f,
261 (_("&Changed"), _("&Deleted")), _("c")) == _("c"):
261 (_("&Changed"), _("&Deleted")), _("c")) == _("c"):
262 act("prompt recreating", "g", f, m2.flags(f))
262 act("prompt recreating", "g", f, m2.flags(f))
263 else:
263 else:
264 act("remote created", "g", f, m2.flags(f))
264 act("remote created", "g", f, m2.flags(f))
265
265
266 return action
266 return action
267
267
268 def actionkey(a):
268 def actionkey(a):
269 return a[1] == 'r' and -1 or 0, a
269 return a[1] == 'r' and -1 or 0, a
270
270
271 def applyupdates(repo, action, wctx, mctx):
271 def applyupdates(repo, action, wctx, mctx):
272 "apply the merge action list to the working directory"
272 "apply the merge action list to the working directory"
273
273
274 updated, merged, removed, unresolved = 0, 0, 0, 0
274 updated, merged, removed, unresolved = 0, 0, 0, 0
275 ms = mergestate(repo)
275 ms = mergestate(repo)
276 ms.reset(wctx.parents()[0].node())
276 ms.reset(wctx.parents()[0].node())
277 moves = []
277 moves = []
278 action.sort(key=actionkey)
278 action.sort(key=actionkey)
279
279
280 # prescan for merges
280 # prescan for merges
281 for a in action:
281 for a in action:
282 f, m = a[:2]
282 f, m = a[:2]
283 if m == 'm': # merge
283 if m == 'm': # merge
284 f2, fd, flags, move = a[2:]
284 f2, fd, flags, move = a[2:]
285 repo.ui.debug(_("preserving %s for resolve of %s\n") % (f, fd))
285 repo.ui.debug(_("preserving %s for resolve of %s\n") % (f, fd))
286 fcl = wctx[f]
286 fcl = wctx[f]
287 fco = mctx[f2]
287 fco = mctx[f2]
288 fca = fcl.ancestor(fco) or repo.filectx(f, fileid=nullrev)
288 fca = fcl.ancestor(fco) or repo.filectx(f, fileid=nullrev)
289 ms.add(fcl, fco, fca, fd, flags)
289 ms.add(fcl, fco, fca, fd, flags)
290 if f != fd and move:
290 if f != fd and move:
291 moves.append(f)
291 moves.append(f)
292
292
293 # remove renamed files after safely stored
293 # remove renamed files after safely stored
294 for f in moves:
294 for f in moves:
295 if util.lexists(repo.wjoin(f)):
295 if util.lexists(repo.wjoin(f)):
296 repo.ui.debug(_("removing %s\n") % f)
296 repo.ui.debug(_("removing %s\n") % f)
297 os.unlink(repo.wjoin(f))
297 os.unlink(repo.wjoin(f))
298
298
299 audit_path = util.path_auditor(repo.root)
299 audit_path = util.path_auditor(repo.root)
300
300
301 for a in action:
301 for a in action:
302 f, m = a[:2]
302 f, m = a[:2]
303 if f and f[0] == "/":
303 if f and f[0] == "/":
304 continue
304 continue
305 if m == "r": # remove
305 if m == "r": # remove
306 repo.ui.note(_("removing %s\n") % f)
306 repo.ui.note(_("removing %s\n") % f)
307 audit_path(f)
307 audit_path(f)
308 try:
308 try:
309 util.unlink(repo.wjoin(f))
309 util.unlink(repo.wjoin(f))
310 except OSError, inst:
310 except OSError, inst:
311 if inst.errno != errno.ENOENT:
311 if inst.errno != errno.ENOENT:
312 repo.ui.warn(_("update failed to remove %s: %s!\n") %
312 repo.ui.warn(_("update failed to remove %s: %s!\n") %
313 (f, inst.strerror))
313 (f, inst.strerror))
314 removed += 1
314 removed += 1
315 elif m == "m": # merge
315 elif m == "m": # merge
316 f2, fd, flags, move = a[2:]
316 f2, fd, flags, move = a[2:]
317 r = ms.resolve(fd, wctx, mctx)
317 r = ms.resolve(fd, wctx, mctx)
318 if r > 0:
318 if r > 0:
319 unresolved += 1
319 unresolved += 1
320 else:
320 else:
321 if r is None:
321 if r is None:
322 updated += 1
322 updated += 1
323 else:
323 else:
324 merged += 1
324 merged += 1
325 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags)
325 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags)
326 if f != fd and move and util.lexists(repo.wjoin(f)):
326 if f != fd and move and util.lexists(repo.wjoin(f)):
327 repo.ui.debug(_("removing %s\n") % f)
327 repo.ui.debug(_("removing %s\n") % f)
328 os.unlink(repo.wjoin(f))
328 os.unlink(repo.wjoin(f))
329 elif m == "g": # get
329 elif m == "g": # get
330 flags = a[2]
330 flags = a[2]
331 repo.ui.note(_("getting %s\n") % f)
331 repo.ui.note(_("getting %s\n") % f)
332 t = mctx.filectx(f).data()
332 t = mctx.filectx(f).data()
333 repo.wwrite(f, t, flags)
333 repo.wwrite(f, t, flags)
334 updated += 1
334 updated += 1
335 elif m == "d": # directory rename
335 elif m == "d": # directory rename
336 f2, fd, flags = a[2:]
336 f2, fd, flags = a[2:]
337 if f:
337 if f:
338 repo.ui.note(_("moving %s to %s\n") % (f, fd))
338 repo.ui.note(_("moving %s to %s\n") % (f, fd))
339 t = wctx.filectx(f).data()
339 t = wctx.filectx(f).data()
340 repo.wwrite(fd, t, flags)
340 repo.wwrite(fd, t, flags)
341 util.unlink(repo.wjoin(f))
341 util.unlink(repo.wjoin(f))
342 if f2:
342 if f2:
343 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
343 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
344 t = mctx.filectx(f2).data()
344 t = mctx.filectx(f2).data()
345 repo.wwrite(fd, t, flags)
345 repo.wwrite(fd, t, flags)
346 updated += 1
346 updated += 1
347 elif m == "dr": # divergent renames
347 elif m == "dr": # divergent renames
348 fl = a[2]
348 fl = a[2]
349 repo.ui.warn(_("warning: detected divergent renames of %s to:\n") % f)
349 repo.ui.warn(_("warning: detected divergent renames of %s to:\n") % f)
350 for nf in fl:
350 for nf in fl:
351 repo.ui.warn(" %s\n" % nf)
351 repo.ui.warn(" %s\n" % nf)
352 elif m == "e": # exec
352 elif m == "e": # exec
353 flags = a[2]
353 flags = a[2]
354 util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags)
354 util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags)
355
355
356 return updated, merged, removed, unresolved
356 return updated, merged, removed, unresolved
357
357
358 def recordupdates(repo, action, branchmerge):
358 def recordupdates(repo, action, branchmerge):
359 "record merge actions to the dirstate"
359 "record merge actions to the dirstate"
360
360
361 for a in action:
361 for a in action:
362 f, m = a[:2]
362 f, m = a[:2]
363 if m == "r": # remove
363 if m == "r": # remove
364 if branchmerge:
364 if branchmerge:
365 repo.dirstate.remove(f)
365 repo.dirstate.remove(f)
366 else:
366 else:
367 repo.dirstate.forget(f)
367 repo.dirstate.forget(f)
368 elif m == "a": # re-add
368 elif m == "a": # re-add
369 if not branchmerge:
369 if not branchmerge:
370 repo.dirstate.add(f)
370 repo.dirstate.add(f)
371 elif m == "f": # forget
371 elif m == "f": # forget
372 repo.dirstate.forget(f)
372 repo.dirstate.forget(f)
373 elif m == "e": # exec change
373 elif m == "e": # exec change
374 repo.dirstate.normallookup(f)
374 repo.dirstate.normallookup(f)
375 elif m == "g": # get
375 elif m == "g": # get
376 if branchmerge:
376 if branchmerge:
377 repo.dirstate.normaldirty(f)
377 repo.dirstate.normaldirty(f)
378 else:
378 else:
379 repo.dirstate.normal(f)
379 repo.dirstate.normal(f)
380 elif m == "m": # merge
380 elif m == "m": # merge
381 f2, fd, flag, move = a[2:]
381 f2, fd, flag, move = a[2:]
382 if branchmerge:
382 if branchmerge:
383 # We've done a branch merge, mark this file as merged
383 # We've done a branch merge, mark this file as merged
384 # so that we properly record the merger later
384 # so that we properly record the merger later
385 repo.dirstate.merge(fd)
385 repo.dirstate.merge(fd)
386 if f != f2: # copy/rename
386 if f != f2: # copy/rename
387 if move:
387 if move:
388 repo.dirstate.remove(f)
388 repo.dirstate.remove(f)
389 if f != fd:
389 if f != fd:
390 repo.dirstate.copy(f, fd)
390 repo.dirstate.copy(f, fd)
391 else:
391 else:
392 repo.dirstate.copy(f2, fd)
392 repo.dirstate.copy(f2, fd)
393 else:
393 else:
394 # We've update-merged a locally modified file, so
394 # We've update-merged a locally modified file, so
395 # we set the dirstate to emulate a normal checkout
395 # we set the dirstate to emulate a normal checkout
396 # of that file some time in the past. Thus our
396 # of that file some time in the past. Thus our
397 # merge will appear as a normal local file
397 # merge will appear as a normal local file
398 # modification.
398 # modification.
399 repo.dirstate.normallookup(fd)
399 repo.dirstate.normallookup(fd)
400 if move:
400 if move:
401 repo.dirstate.forget(f)
401 repo.dirstate.forget(f)
402 elif m == "d": # directory rename
402 elif m == "d": # directory rename
403 f2, fd, flag = a[2:]
403 f2, fd, flag = a[2:]
404 if not f2 and f not in repo.dirstate:
404 if not f2 and f not in repo.dirstate:
405 # untracked file moved
405 # untracked file moved
406 continue
406 continue
407 if branchmerge:
407 if branchmerge:
408 repo.dirstate.add(fd)
408 repo.dirstate.add(fd)
409 if f:
409 if f:
410 repo.dirstate.remove(f)
410 repo.dirstate.remove(f)
411 repo.dirstate.copy(f, fd)
411 repo.dirstate.copy(f, fd)
412 if f2:
412 if f2:
413 repo.dirstate.copy(f2, fd)
413 repo.dirstate.copy(f2, fd)
414 else:
414 else:
415 repo.dirstate.normal(fd)
415 repo.dirstate.normal(fd)
416 if f:
416 if f:
417 repo.dirstate.forget(f)
417 repo.dirstate.forget(f)
418
418
419 def update(repo, node, branchmerge, force, partial):
419 def update(repo, node, branchmerge, force, partial):
420 """
420 """
421 Perform a merge between the working directory and the given node
421 Perform a merge between the working directory and the given node
422
422
423 branchmerge = whether to merge between branches
423 branchmerge = whether to merge between branches
424 force = whether to force branch merging or file overwriting
424 force = whether to force branch merging or file overwriting
425 partial = a function to filter file lists (dirstate not updated)
425 partial = a function to filter file lists (dirstate not updated)
426 """
426 """
427
427
428 wlock = repo.wlock()
428 wlock = repo.wlock()
429 try:
429 try:
430 wc = repo[None]
430 wc = repo[None]
431 if node is None:
431 if node is None:
432 # tip of current branch
432 # tip of current branch
433 try:
433 try:
434 node = repo.branchtags()[wc.branch()]
434 node = repo.branchtags()[wc.branch()]
435 except KeyError:
435 except KeyError:
436 if wc.branch() == "default": # no default branch!
436 if wc.branch() == "default": # no default branch!
437 node = repo.lookup("tip") # update to tip
437 node = repo.lookup("tip") # update to tip
438 else:
438 else:
439 raise util.Abort(_("branch %s not found") % wc.branch())
439 raise util.Abort(_("branch %s not found") % wc.branch())
440 overwrite = force and not branchmerge
440 overwrite = force and not branchmerge
441 pl = wc.parents()
441 pl = wc.parents()
442 p1, p2 = pl[0], repo[node]
442 p1, p2 = pl[0], repo[node]
443 pa = p1.ancestor(p2)
443 pa = p1.ancestor(p2)
444 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
444 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
445 fastforward = False
445 fastforward = False
446
446
447 ### check phase
447 ### check phase
448 if not overwrite and len(pl) > 1:
448 if not overwrite and len(pl) > 1:
449 raise util.Abort(_("outstanding uncommitted merges"))
449 raise util.Abort(_("outstanding uncommitted merges"))
450 if branchmerge:
450 if branchmerge:
451 if pa == p2:
451 if pa == p2:
452 raise util.Abort(_("can't merge with ancestor"))
452 raise util.Abort(_("can't merge with ancestor"))
453 elif pa == p1:
453 elif pa == p1:
454 if p1.branch() != p2.branch():
454 if p1.branch() != p2.branch():
455 fastforward = True
455 fastforward = True
456 else:
456 else:
457 raise util.Abort(_("nothing to merge (use 'hg update'"
457 raise util.Abort(_("nothing to merge (use 'hg update'"
458 " or check 'hg heads')"))
458 " or check 'hg heads')"))
459 if not force and (wc.files() or wc.deleted()):
459 if not force and (wc.files() or wc.deleted()):
460 raise util.Abort(_("outstanding uncommitted changes"))
460 raise util.Abort(_("outstanding uncommitted changes "
461 "(use 'hg status' to list changes)"))
461 elif not overwrite:
462 elif not overwrite:
462 if pa == p1 or pa == p2: # linear
463 if pa == p1 or pa == p2: # linear
463 pass # all good
464 pass # all good
464 elif p1.branch() == p2.branch():
465 elif p1.branch() == p2.branch():
465 if wc.files() or wc.deleted():
466 if wc.files() or wc.deleted():
466 raise util.Abort(_("crosses branches (use 'hg merge' or "
467 raise util.Abort(_("crosses branches (use 'hg merge' or "
467 "'hg update -C' to discard changes)"))
468 "'hg update -C' to discard changes)"))
468 raise util.Abort(_("crosses branches (use 'hg merge' "
469 raise util.Abort(_("crosses branches (use 'hg merge' "
469 "or 'hg update -C')"))
470 "or 'hg update -C')"))
470 elif wc.files() or wc.deleted():
471 elif wc.files() or wc.deleted():
471 raise util.Abort(_("crosses named branches (use "
472 raise util.Abort(_("crosses named branches (use "
472 "'hg update -C' to discard changes)"))
473 "'hg update -C' to discard changes)"))
473 else:
474 else:
474 # Allow jumping branches if there are no changes
475 # Allow jumping branches if there are no changes
475 overwrite = True
476 overwrite = True
476
477
477 ### calculate phase
478 ### calculate phase
478 action = []
479 action = []
479 if not force:
480 if not force:
480 _checkunknown(wc, p2)
481 _checkunknown(wc, p2)
481 if not util.checkcase(repo.path):
482 if not util.checkcase(repo.path):
482 _checkcollision(p2)
483 _checkcollision(p2)
483 action += _forgetremoved(wc, p2, branchmerge)
484 action += _forgetremoved(wc, p2, branchmerge)
484 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
485 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
485
486
486 ### apply phase
487 ### apply phase
487 if not branchmerge: # just jump to the new rev
488 if not branchmerge: # just jump to the new rev
488 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
489 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
489 if not partial:
490 if not partial:
490 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
491 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
491
492
492 stats = applyupdates(repo, action, wc, p2)
493 stats = applyupdates(repo, action, wc, p2)
493
494
494 if not partial:
495 if not partial:
495 recordupdates(repo, action, branchmerge)
496 recordupdates(repo, action, branchmerge)
496 repo.dirstate.setparents(fp1, fp2)
497 repo.dirstate.setparents(fp1, fp2)
497 if not branchmerge and not fastforward:
498 if not branchmerge and not fastforward:
498 repo.dirstate.setbranch(p2.branch())
499 repo.dirstate.setbranch(p2.branch())
499 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
500 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
500
501
501 return stats
502 return stats
502 finally:
503 finally:
503 wlock.release()
504 wlock.release()
@@ -1,11 +1,11 b''
1 created new head
1 created new head
2 % local deleted a file, remote removed
2 % local deleted a file, remote removed
3 abort: outstanding uncommitted changes
3 abort: outstanding uncommitted changes (use 'hg status' to list changes)
4 resolving manifests
4 resolving manifests
5 removing a
5 removing a
6 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
6 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
7 (branch merge, don't forget to commit)
7 (branch merge, don't forget to commit)
8 % should show a as removed
8 % should show a as removed
9 R a
9 R a
10 % manifest. should not have a:
10 % manifest. should not have a:
11 b
11 b
@@ -1,69 +1,69 b''
1 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2 created new head
2 created new head
3 %% no merges expected
3 %% no merges expected
4 changeset: 0:98e00378acd0
4 changeset: 0:98e00378acd0
5 user: test
5 user: test
6 date: Mon Jan 12 13:46:40 1970 +0000
6 date: Mon Jan 12 13:46:40 1970 +0000
7 summary: commit #0
7 summary: commit #0
8
8
9 changeset: 1:4ee19afe4659
9 changeset: 1:4ee19afe4659
10 user: test
10 user: test
11 date: Mon Jan 12 13:46:40 1970 +0000
11 date: Mon Jan 12 13:46:40 1970 +0000
12 summary: commit #1
12 summary: commit #1
13
13
14 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
14 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
15 (branch merge, don't forget to commit)
15 (branch merge, don't forget to commit)
16 diff -r d9e5953b9dec b
16 diff -r d9e5953b9dec b
17 --- /dev/null
17 --- /dev/null
18 +++ b/b
18 +++ b/b
19 @@ -0,0 +1,1 @@
19 @@ -0,0 +1,1 @@
20 +This is file b1
20 +This is file b1
21 M b
21 M b
22 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
22 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
23 created new head
23 created new head
24 %% merge should fail
24 %% merge should fail
25 abort: untracked file in working directory differs from file in requested revision: 'b'
25 abort: untracked file in working directory differs from file in requested revision: 'b'
26 %% merge of b expected
26 %% merge of b expected
27 merging for b
27 merging for b
28 merging b
28 merging b
29 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
29 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
30 (branch merge, don't forget to commit)
30 (branch merge, don't forget to commit)
31 diff -r d9e5953b9dec b
31 diff -r d9e5953b9dec b
32 --- /dev/null
32 --- /dev/null
33 +++ b/b
33 +++ b/b
34 @@ -0,0 +1,1 @@
34 @@ -0,0 +1,1 @@
35 +This is file b2
35 +This is file b2
36 M b
36 M b
37 %%
37 %%
38 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
38 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
39 created new head
39 created new head
40 Contents of b should be "this is file b1"
40 Contents of b should be "this is file b1"
41 This is file b1
41 This is file b1
42 %% merge fails
42 %% merge fails
43 abort: outstanding uncommitted changes
43 abort: outstanding uncommitted changes (use 'hg status' to list changes)
44 %% merge expected!
44 %% merge expected!
45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 (branch merge, don't forget to commit)
46 (branch merge, don't forget to commit)
47 diff -r c1dd73cbf59f b
47 diff -r c1dd73cbf59f b
48 --- a/b
48 --- a/b
49 +++ b/b
49 +++ b/b
50 @@ -1,1 +1,1 @@
50 @@ -1,1 +1,1 @@
51 -This is file b1
51 -This is file b1
52 +This is file b22
52 +This is file b22
53 M b
53 M b
54 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
54 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 created new head
55 created new head
56 %% merge of b should fail
56 %% merge of b should fail
57 abort: outstanding uncommitted changes
57 abort: outstanding uncommitted changes (use 'hg status' to list changes)
58 %% merge of b expected
58 %% merge of b expected
59 merging for b
59 merging for b
60 merging b
60 merging b
61 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
61 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
62 (branch merge, don't forget to commit)
62 (branch merge, don't forget to commit)
63 diff -r c1dd73cbf59f b
63 diff -r c1dd73cbf59f b
64 --- a/b
64 --- a/b
65 +++ b/b
65 +++ b/b
66 @@ -1,1 +1,1 @@
66 @@ -1,1 +1,1 @@
67 -This is file b1
67 -This is file b1
68 +This is file b33
68 +This is file b33
69 M b
69 M b
@@ -1,153 +1,153 b''
1 adding a
1 adding a
2 updating working directory
2 updating working directory
3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
5 diff -r 33aaa84a386b a
5 diff -r 33aaa84a386b a
6 --- a/a
6 --- a/a
7 +++ b/a
7 +++ b/a
8 @@ -1,1 +1,1 @@
8 @@ -1,1 +1,1 @@
9 -a
9 -a
10 +abc
10 +abc
11 adding b
11 adding b
12 M a
12 M a
13 changeset: 0:33aaa84a386b
13 changeset: 0:33aaa84a386b
14 user: test
14 user: test
15 date: Mon Jan 12 13:46:40 1970 +0000
15 date: Mon Jan 12 13:46:40 1970 +0000
16 summary: 1
16 summary: 1
17
17
18 resolving manifests
18 resolving manifests
19 overwrite False partial False
19 overwrite False partial False
20 ancestor 33aaa84a386b local 33aaa84a386b+ remote 802f095af299
20 ancestor 33aaa84a386b local 33aaa84a386b+ remote 802f095af299
21 searching for copies back to rev 1
21 searching for copies back to rev 1
22 unmatched files in other:
22 unmatched files in other:
23 b
23 b
24 a: versions differ -> m
24 a: versions differ -> m
25 b: remote created -> g
25 b: remote created -> g
26 preserving a for resolve of a
26 preserving a for resolve of a
27 picked tool 'true' for a (binary False symlink False)
27 picked tool 'true' for a (binary False symlink False)
28 merging a
28 merging a
29 my a@33aaa84a386b+ other a@802f095af299 ancestor a@33aaa84a386b
29 my a@33aaa84a386b+ other a@802f095af299 ancestor a@33aaa84a386b
30 getting b
30 getting b
31 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
31 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
32 changeset: 1:802f095af299
32 changeset: 1:802f095af299
33 tag: tip
33 tag: tip
34 user: test
34 user: test
35 date: Mon Jan 12 13:46:40 1970 +0000
35 date: Mon Jan 12 13:46:40 1970 +0000
36 summary: 2
36 summary: 2
37
37
38 resolving manifests
38 resolving manifests
39 overwrite False partial False
39 overwrite False partial False
40 ancestor 33aaa84a386b local 802f095af299+ remote 33aaa84a386b
40 ancestor 33aaa84a386b local 802f095af299+ remote 33aaa84a386b
41 b: remote deleted -> r
41 b: remote deleted -> r
42 removing b
42 removing b
43 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
43 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
44 changeset: 0:33aaa84a386b
44 changeset: 0:33aaa84a386b
45 user: test
45 user: test
46 date: Mon Jan 12 13:46:40 1970 +0000
46 date: Mon Jan 12 13:46:40 1970 +0000
47 summary: 1
47 summary: 1
48
48
49 abort: there is nothing to merge - use "hg update" instead
49 abort: there is nothing to merge - use "hg update" instead
50 failed
50 failed
51 changeset: 0:33aaa84a386b
51 changeset: 0:33aaa84a386b
52 user: test
52 user: test
53 date: Mon Jan 12 13:46:40 1970 +0000
53 date: Mon Jan 12 13:46:40 1970 +0000
54 summary: 1
54 summary: 1
55
55
56 resolving manifests
56 resolving manifests
57 overwrite False partial False
57 overwrite False partial False
58 ancestor 33aaa84a386b local 33aaa84a386b+ remote 802f095af299
58 ancestor 33aaa84a386b local 33aaa84a386b+ remote 802f095af299
59 searching for copies back to rev 1
59 searching for copies back to rev 1
60 unmatched files in other:
60 unmatched files in other:
61 b
61 b
62 a: versions differ -> m
62 a: versions differ -> m
63 b: remote created -> g
63 b: remote created -> g
64 preserving a for resolve of a
64 preserving a for resolve of a
65 picked tool 'true' for a (binary False symlink False)
65 picked tool 'true' for a (binary False symlink False)
66 merging a
66 merging a
67 my a@33aaa84a386b+ other a@802f095af299 ancestor a@33aaa84a386b
67 my a@33aaa84a386b+ other a@802f095af299 ancestor a@33aaa84a386b
68 getting b
68 getting b
69 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
69 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
70 changeset: 1:802f095af299
70 changeset: 1:802f095af299
71 tag: tip
71 tag: tip
72 user: test
72 user: test
73 date: Mon Jan 12 13:46:40 1970 +0000
73 date: Mon Jan 12 13:46:40 1970 +0000
74 summary: 2
74 summary: 2
75
75
76 changeset: 1:802f095af299
76 changeset: 1:802f095af299
77 tag: tip
77 tag: tip
78 user: test
78 user: test
79 date: Mon Jan 12 13:46:40 1970 +0000
79 date: Mon Jan 12 13:46:40 1970 +0000
80 files: a b
80 files: a b
81 description:
81 description:
82 2
82 2
83
83
84
84
85 changeset: 0:33aaa84a386b
85 changeset: 0:33aaa84a386b
86 user: test
86 user: test
87 date: Mon Jan 12 13:46:40 1970 +0000
87 date: Mon Jan 12 13:46:40 1970 +0000
88 files: a
88 files: a
89 description:
89 description:
90 1
90 1
91
91
92
92
93 diff -r 802f095af299 a
93 diff -r 802f095af299 a
94 --- a/a
94 --- a/a
95 +++ b/a
95 +++ b/a
96 @@ -1,1 +1,1 @@
96 @@ -1,1 +1,1 @@
97 -a2
97 -a2
98 +abc
98 +abc
99 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
99 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
100 adding b
100 adding b
101 created new head
101 created new head
102 M a
102 M a
103 changeset: 1:802f095af299
103 changeset: 1:802f095af299
104 user: test
104 user: test
105 date: Mon Jan 12 13:46:40 1970 +0000
105 date: Mon Jan 12 13:46:40 1970 +0000
106 summary: 2
106 summary: 2
107
107
108 abort: crosses branches (use 'hg merge' or 'hg update -C' to discard changes)
108 abort: crosses branches (use 'hg merge' or 'hg update -C' to discard changes)
109 failed
109 failed
110 abort: outstanding uncommitted changes
110 abort: outstanding uncommitted changes (use 'hg status' to list changes)
111 failed
111 failed
112 resolving manifests
112 resolving manifests
113 overwrite False partial False
113 overwrite False partial False
114 ancestor 33aaa84a386b local 802f095af299+ remote 030602aee63d
114 ancestor 33aaa84a386b local 802f095af299+ remote 030602aee63d
115 searching for copies back to rev 1
115 searching for copies back to rev 1
116 a: versions differ -> m
116 a: versions differ -> m
117 b: versions differ -> m
117 b: versions differ -> m
118 preserving a for resolve of a
118 preserving a for resolve of a
119 preserving b for resolve of b
119 preserving b for resolve of b
120 picked tool 'true' for a (binary False symlink False)
120 picked tool 'true' for a (binary False symlink False)
121 merging a
121 merging a
122 my a@802f095af299+ other a@030602aee63d ancestor a@33aaa84a386b
122 my a@802f095af299+ other a@030602aee63d ancestor a@33aaa84a386b
123 picked tool 'true' for b (binary False symlink False)
123 picked tool 'true' for b (binary False symlink False)
124 merging b
124 merging b
125 my b@802f095af299+ other b@030602aee63d ancestor b@000000000000
125 my b@802f095af299+ other b@030602aee63d ancestor b@000000000000
126 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
126 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
127 (branch merge, don't forget to commit)
127 (branch merge, don't forget to commit)
128 changeset: 1:802f095af299
128 changeset: 1:802f095af299
129 user: test
129 user: test
130 date: Mon Jan 12 13:46:40 1970 +0000
130 date: Mon Jan 12 13:46:40 1970 +0000
131 summary: 2
131 summary: 2
132
132
133 changeset: 2:030602aee63d
133 changeset: 2:030602aee63d
134 tag: tip
134 tag: tip
135 parent: 0:33aaa84a386b
135 parent: 0:33aaa84a386b
136 user: test
136 user: test
137 date: Mon Jan 12 13:46:40 1970 +0000
137 date: Mon Jan 12 13:46:40 1970 +0000
138 summary: 3
138 summary: 3
139
139
140 diff -r 802f095af299 a
140 diff -r 802f095af299 a
141 --- a/a
141 --- a/a
142 +++ b/a
142 +++ b/a
143 @@ -1,1 +1,1 @@
143 @@ -1,1 +1,1 @@
144 -a2
144 -a2
145 +abc
145 +abc
146 adding a
146 adding a
147 pulling from ../a
147 pulling from ../a
148 requesting all changes
148 requesting all changes
149 adding changesets
149 adding changesets
150 adding manifests
150 adding manifests
151 adding file changes
151 adding file changes
152 added 1 changesets with 1 changes to 1 files
152 added 1 changesets with 1 changes to 1 files
153 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
153 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
General Comments 0
You need to be logged in to leave comments. Login now