##// END OF EJS Templates
amend: fix amending rename commit with diverged topologies (issue4405)...
Ryan McElroy -
r23071:652ab726 stable
parent child Browse files
Show More
@@ -1,441 +1,469 b''
1 # copies.py - copy detection for Mercurial
1 # copies.py - copy detection for Mercurial
2 #
2 #
3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2008 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 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import util
8 import util
9 import heapq
9 import heapq
10
10
11 def _nonoverlap(d1, d2, d3):
11 def _nonoverlap(d1, d2, d3):
12 "Return list of elements in d1 not in d2 or d3"
12 "Return list of elements in d1 not in d2 or d3"
13 return sorted([d for d in d1 if d not in d3 and d not in d2])
13 return sorted([d for d in d1 if d not in d3 and d not in d2])
14
14
15 def _dirname(f):
15 def _dirname(f):
16 s = f.rfind("/")
16 s = f.rfind("/")
17 if s == -1:
17 if s == -1:
18 return ""
18 return ""
19 return f[:s]
19 return f[:s]
20
20
21 def _findlimit(repo, a, b):
21 def _findlimit(repo, a, b):
22 """Find the earliest revision that's an ancestor of a or b but not both,
22 """
23 Find the last revision that needs to be checked to ensure that a full
24 transitive closure for file copies can be properly calculated.
25 Generally, this means finding the earliest revision number that's an
26 ancestor of a or b but not both, except when a or b is a direct descendent
27 of the other, in which case we can return the minimum revnum of a and b.
23 None if no such revision exists.
28 None if no such revision exists.
24 """
29 """
30
25 # basic idea:
31 # basic idea:
26 # - mark a and b with different sides
32 # - mark a and b with different sides
27 # - if a parent's children are all on the same side, the parent is
33 # - if a parent's children are all on the same side, the parent is
28 # on that side, otherwise it is on no side
34 # on that side, otherwise it is on no side
29 # - walk the graph in topological order with the help of a heap;
35 # - walk the graph in topological order with the help of a heap;
30 # - add unseen parents to side map
36 # - add unseen parents to side map
31 # - clear side of any parent that has children on different sides
37 # - clear side of any parent that has children on different sides
32 # - track number of interesting revs that might still be on a side
38 # - track number of interesting revs that might still be on a side
33 # - track the lowest interesting rev seen
39 # - track the lowest interesting rev seen
34 # - quit when interesting revs is zero
40 # - quit when interesting revs is zero
35
41
36 cl = repo.changelog
42 cl = repo.changelog
37 working = len(cl) # pseudo rev for the working directory
43 working = len(cl) # pseudo rev for the working directory
38 if a is None:
44 if a is None:
39 a = working
45 a = working
40 if b is None:
46 if b is None:
41 b = working
47 b = working
42
48
43 side = {a: -1, b: 1}
49 side = {a: -1, b: 1}
44 visit = [-a, -b]
50 visit = [-a, -b]
45 heapq.heapify(visit)
51 heapq.heapify(visit)
46 interesting = len(visit)
52 interesting = len(visit)
47 hascommonancestor = False
53 hascommonancestor = False
48 limit = working
54 limit = working
49
55
50 while interesting:
56 while interesting:
51 r = -heapq.heappop(visit)
57 r = -heapq.heappop(visit)
52 if r == working:
58 if r == working:
53 parents = [cl.rev(p) for p in repo.dirstate.parents()]
59 parents = [cl.rev(p) for p in repo.dirstate.parents()]
54 else:
60 else:
55 parents = cl.parentrevs(r)
61 parents = cl.parentrevs(r)
56 for p in parents:
62 for p in parents:
57 if p < 0:
63 if p < 0:
58 continue
64 continue
59 if p not in side:
65 if p not in side:
60 # first time we see p; add it to visit
66 # first time we see p; add it to visit
61 side[p] = side[r]
67 side[p] = side[r]
62 if side[p]:
68 if side[p]:
63 interesting += 1
69 interesting += 1
64 heapq.heappush(visit, -p)
70 heapq.heappush(visit, -p)
65 elif side[p] and side[p] != side[r]:
71 elif side[p] and side[p] != side[r]:
66 # p was interesting but now we know better
72 # p was interesting but now we know better
67 side[p] = 0
73 side[p] = 0
68 interesting -= 1
74 interesting -= 1
69 hascommonancestor = True
75 hascommonancestor = True
70 if side[r]:
76 if side[r]:
71 limit = r # lowest rev visited
77 limit = r # lowest rev visited
72 interesting -= 1
78 interesting -= 1
73
79
74 if not hascommonancestor:
80 if not hascommonancestor:
75 return None
81 return None
76 return limit
82
83 # Consider the following flow (see test-commit-amend.t under issue4405):
84 # 1/ File 'a0' committed
85 # 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1')
86 # 3/ Move back to first commit
87 # 4/ Create a new commit via revert to contents of 'a1' (call it 'a1-amend')
88 # 5/ Rename file from 'a1' to 'a2' and commit --amend 'a1-msg'
89 #
90 # During the amend in step five, we will be in this state:
91 #
92 # @ 3 temporary amend commit for a1-amend
93 # |
94 # o 2 a1-amend
95 # |
96 # | o 1 a1
97 # |/
98 # o 0 a0
99 #
100 # When findlimit is called, a and b are revs 3 and 0, so limit will be 2,
101 # yet the filelog has the copy information in rev 1 and we will not look
102 # back far enough unless we also look at the a and b as candidates.
103 # This only occurs when a is a descendent of b or visa-versa.
104 return min(limit, a, b)
77
105
78 def _chain(src, dst, a, b):
106 def _chain(src, dst, a, b):
79 '''chain two sets of copies a->b'''
107 '''chain two sets of copies a->b'''
80 t = a.copy()
108 t = a.copy()
81 for k, v in b.iteritems():
109 for k, v in b.iteritems():
82 if v in t:
110 if v in t:
83 # found a chain
111 # found a chain
84 if t[v] != k:
112 if t[v] != k:
85 # file wasn't renamed back to itself
113 # file wasn't renamed back to itself
86 t[k] = t[v]
114 t[k] = t[v]
87 if v not in dst:
115 if v not in dst:
88 # chain was a rename, not a copy
116 # chain was a rename, not a copy
89 del t[v]
117 del t[v]
90 if v in src:
118 if v in src:
91 # file is a copy of an existing file
119 # file is a copy of an existing file
92 t[k] = v
120 t[k] = v
93
121
94 # remove criss-crossed copies
122 # remove criss-crossed copies
95 for k, v in t.items():
123 for k, v in t.items():
96 if k in src and v in dst:
124 if k in src and v in dst:
97 del t[k]
125 del t[k]
98
126
99 return t
127 return t
100
128
101 def _tracefile(fctx, am, limit=-1):
129 def _tracefile(fctx, am, limit=-1):
102 '''return file context that is the ancestor of fctx present in ancestor
130 '''return file context that is the ancestor of fctx present in ancestor
103 manifest am, stopping after the first ancestor lower than limit'''
131 manifest am, stopping after the first ancestor lower than limit'''
104
132
105 for f in fctx.ancestors():
133 for f in fctx.ancestors():
106 if am.get(f.path(), None) == f.filenode():
134 if am.get(f.path(), None) == f.filenode():
107 return f
135 return f
108 if f.rev() < limit:
136 if f.rev() < limit:
109 return None
137 return None
110
138
111 def _dirstatecopies(d):
139 def _dirstatecopies(d):
112 ds = d._repo.dirstate
140 ds = d._repo.dirstate
113 c = ds.copies().copy()
141 c = ds.copies().copy()
114 for k in c.keys():
142 for k in c.keys():
115 if ds[k] not in 'anm':
143 if ds[k] not in 'anm':
116 del c[k]
144 del c[k]
117 return c
145 return c
118
146
119 def _forwardcopies(a, b):
147 def _forwardcopies(a, b):
120 '''find {dst@b: src@a} copy mapping where a is an ancestor of b'''
148 '''find {dst@b: src@a} copy mapping where a is an ancestor of b'''
121
149
122 # check for working copy
150 # check for working copy
123 w = None
151 w = None
124 if b.rev() is None:
152 if b.rev() is None:
125 w = b
153 w = b
126 b = w.p1()
154 b = w.p1()
127 if a == b:
155 if a == b:
128 # short-circuit to avoid issues with merge states
156 # short-circuit to avoid issues with merge states
129 return _dirstatecopies(w)
157 return _dirstatecopies(w)
130
158
131 # files might have to be traced back to the fctx parent of the last
159 # files might have to be traced back to the fctx parent of the last
132 # one-side-only changeset, but not further back than that
160 # one-side-only changeset, but not further back than that
133 limit = _findlimit(a._repo, a.rev(), b.rev())
161 limit = _findlimit(a._repo, a.rev(), b.rev())
134 if limit is None:
162 if limit is None:
135 limit = -1
163 limit = -1
136 am = a.manifest()
164 am = a.manifest()
137
165
138 # find where new files came from
166 # find where new files came from
139 # we currently don't try to find where old files went, too expensive
167 # we currently don't try to find where old files went, too expensive
140 # this means we can miss a case like 'hg rm b; hg cp a b'
168 # this means we can miss a case like 'hg rm b; hg cp a b'
141 cm = {}
169 cm = {}
142 missing = set(b.manifest().iterkeys())
170 missing = set(b.manifest().iterkeys())
143 missing.difference_update(a.manifest().iterkeys())
171 missing.difference_update(a.manifest().iterkeys())
144
172
145 for f in missing:
173 for f in missing:
146 ofctx = _tracefile(b[f], am, limit)
174 ofctx = _tracefile(b[f], am, limit)
147 if ofctx:
175 if ofctx:
148 cm[f] = ofctx.path()
176 cm[f] = ofctx.path()
149
177
150 # combine copies from dirstate if necessary
178 # combine copies from dirstate if necessary
151 if w is not None:
179 if w is not None:
152 cm = _chain(a, w, cm, _dirstatecopies(w))
180 cm = _chain(a, w, cm, _dirstatecopies(w))
153
181
154 return cm
182 return cm
155
183
156 def _backwardrenames(a, b):
184 def _backwardrenames(a, b):
157 # Even though we're not taking copies into account, 1:n rename situations
185 # Even though we're not taking copies into account, 1:n rename situations
158 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
186 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
159 # arbitrarily pick one of the renames.
187 # arbitrarily pick one of the renames.
160 f = _forwardcopies(b, a)
188 f = _forwardcopies(b, a)
161 r = {}
189 r = {}
162 for k, v in sorted(f.iteritems()):
190 for k, v in sorted(f.iteritems()):
163 # remove copies
191 # remove copies
164 if v in a:
192 if v in a:
165 continue
193 continue
166 r[v] = k
194 r[v] = k
167 return r
195 return r
168
196
169 def pathcopies(x, y):
197 def pathcopies(x, y):
170 '''find {dst@y: src@x} copy mapping for directed compare'''
198 '''find {dst@y: src@x} copy mapping for directed compare'''
171 if x == y or not x or not y:
199 if x == y or not x or not y:
172 return {}
200 return {}
173 a = y.ancestor(x)
201 a = y.ancestor(x)
174 if a == x:
202 if a == x:
175 return _forwardcopies(x, y)
203 return _forwardcopies(x, y)
176 if a == y:
204 if a == y:
177 return _backwardrenames(x, y)
205 return _backwardrenames(x, y)
178 return _chain(x, y, _backwardrenames(x, a), _forwardcopies(a, y))
206 return _chain(x, y, _backwardrenames(x, a), _forwardcopies(a, y))
179
207
180 def mergecopies(repo, c1, c2, ca):
208 def mergecopies(repo, c1, c2, ca):
181 """
209 """
182 Find moves and copies between context c1 and c2 that are relevant
210 Find moves and copies between context c1 and c2 that are relevant
183 for merging.
211 for merging.
184
212
185 Returns four dicts: "copy", "movewithdir", "diverge", and
213 Returns four dicts: "copy", "movewithdir", "diverge", and
186 "renamedelete".
214 "renamedelete".
187
215
188 "copy" is a mapping from destination name -> source name,
216 "copy" is a mapping from destination name -> source name,
189 where source is in c1 and destination is in c2 or vice-versa.
217 where source is in c1 and destination is in c2 or vice-versa.
190
218
191 "movewithdir" is a mapping from source name -> destination name,
219 "movewithdir" is a mapping from source name -> destination name,
192 where the file at source present in one context but not the other
220 where the file at source present in one context but not the other
193 needs to be moved to destination by the merge process, because the
221 needs to be moved to destination by the merge process, because the
194 other context moved the directory it is in.
222 other context moved the directory it is in.
195
223
196 "diverge" is a mapping of source name -> list of destination names
224 "diverge" is a mapping of source name -> list of destination names
197 for divergent renames.
225 for divergent renames.
198
226
199 "renamedelete" is a mapping of source name -> list of destination
227 "renamedelete" is a mapping of source name -> list of destination
200 names for files deleted in c1 that were renamed in c2 or vice-versa.
228 names for files deleted in c1 that were renamed in c2 or vice-versa.
201 """
229 """
202 # avoid silly behavior for update from empty dir
230 # avoid silly behavior for update from empty dir
203 if not c1 or not c2 or c1 == c2:
231 if not c1 or not c2 or c1 == c2:
204 return {}, {}, {}, {}
232 return {}, {}, {}, {}
205
233
206 # avoid silly behavior for parent -> working dir
234 # avoid silly behavior for parent -> working dir
207 if c2.node() is None and c1.node() == repo.dirstate.p1():
235 if c2.node() is None and c1.node() == repo.dirstate.p1():
208 return repo.dirstate.copies(), {}, {}, {}
236 return repo.dirstate.copies(), {}, {}, {}
209
237
210 limit = _findlimit(repo, c1.rev(), c2.rev())
238 limit = _findlimit(repo, c1.rev(), c2.rev())
211 if limit is None:
239 if limit is None:
212 # no common ancestor, no copies
240 # no common ancestor, no copies
213 return {}, {}, {}, {}
241 return {}, {}, {}, {}
214 m1 = c1.manifest()
242 m1 = c1.manifest()
215 m2 = c2.manifest()
243 m2 = c2.manifest()
216 ma = ca.manifest()
244 ma = ca.manifest()
217
245
218 def makectx(f, n):
246 def makectx(f, n):
219 if len(n) != 20: # in a working context?
247 if len(n) != 20: # in a working context?
220 if c1.rev() is None:
248 if c1.rev() is None:
221 return c1.filectx(f)
249 return c1.filectx(f)
222 return c2.filectx(f)
250 return c2.filectx(f)
223 return repo.filectx(f, fileid=n)
251 return repo.filectx(f, fileid=n)
224
252
225 ctx = util.lrucachefunc(makectx)
253 ctx = util.lrucachefunc(makectx)
226 copy = {}
254 copy = {}
227 movewithdir = {}
255 movewithdir = {}
228 fullcopy = {}
256 fullcopy = {}
229 diverge = {}
257 diverge = {}
230
258
231 repo.ui.debug(" searching for copies back to rev %d\n" % limit)
259 repo.ui.debug(" searching for copies back to rev %d\n" % limit)
232
260
233 u1 = _nonoverlap(m1, m2, ma)
261 u1 = _nonoverlap(m1, m2, ma)
234 u2 = _nonoverlap(m2, m1, ma)
262 u2 = _nonoverlap(m2, m1, ma)
235
263
236 if u1:
264 if u1:
237 repo.ui.debug(" unmatched files in local:\n %s\n"
265 repo.ui.debug(" unmatched files in local:\n %s\n"
238 % "\n ".join(u1))
266 % "\n ".join(u1))
239 if u2:
267 if u2:
240 repo.ui.debug(" unmatched files in other:\n %s\n"
268 repo.ui.debug(" unmatched files in other:\n %s\n"
241 % "\n ".join(u2))
269 % "\n ".join(u2))
242
270
243 for f in u1:
271 for f in u1:
244 checkcopies(ctx, f, m1, m2, ca, limit, diverge, copy, fullcopy)
272 checkcopies(ctx, f, m1, m2, ca, limit, diverge, copy, fullcopy)
245
273
246 for f in u2:
274 for f in u2:
247 checkcopies(ctx, f, m2, m1, ca, limit, diverge, copy, fullcopy)
275 checkcopies(ctx, f, m2, m1, ca, limit, diverge, copy, fullcopy)
248
276
249 renamedelete = {}
277 renamedelete = {}
250 renamedelete2 = set()
278 renamedelete2 = set()
251 diverge2 = set()
279 diverge2 = set()
252 for of, fl in diverge.items():
280 for of, fl in diverge.items():
253 if len(fl) == 1 or of in c1 or of in c2:
281 if len(fl) == 1 or of in c1 or of in c2:
254 del diverge[of] # not actually divergent, or not a rename
282 del diverge[of] # not actually divergent, or not a rename
255 if of not in c1 and of not in c2:
283 if of not in c1 and of not in c2:
256 # renamed on one side, deleted on the other side, but filter
284 # renamed on one side, deleted on the other side, but filter
257 # out files that have been renamed and then deleted
285 # out files that have been renamed and then deleted
258 renamedelete[of] = [f for f in fl if f in c1 or f in c2]
286 renamedelete[of] = [f for f in fl if f in c1 or f in c2]
259 renamedelete2.update(fl) # reverse map for below
287 renamedelete2.update(fl) # reverse map for below
260 else:
288 else:
261 diverge2.update(fl) # reverse map for below
289 diverge2.update(fl) # reverse map for below
262
290
263 bothnew = sorted([d for d in m1 if d in m2 and d not in ma])
291 bothnew = sorted([d for d in m1 if d in m2 and d not in ma])
264 if bothnew:
292 if bothnew:
265 repo.ui.debug(" unmatched files new in both:\n %s\n"
293 repo.ui.debug(" unmatched files new in both:\n %s\n"
266 % "\n ".join(bothnew))
294 % "\n ".join(bothnew))
267 bothdiverge, _copy, _fullcopy = {}, {}, {}
295 bothdiverge, _copy, _fullcopy = {}, {}, {}
268 for f in bothnew:
296 for f in bothnew:
269 checkcopies(ctx, f, m1, m2, ca, limit, bothdiverge, _copy, _fullcopy)
297 checkcopies(ctx, f, m1, m2, ca, limit, bothdiverge, _copy, _fullcopy)
270 checkcopies(ctx, f, m2, m1, ca, limit, bothdiverge, _copy, _fullcopy)
298 checkcopies(ctx, f, m2, m1, ca, limit, bothdiverge, _copy, _fullcopy)
271 for of, fl in bothdiverge.items():
299 for of, fl in bothdiverge.items():
272 if len(fl) == 2 and fl[0] == fl[1]:
300 if len(fl) == 2 and fl[0] == fl[1]:
273 copy[fl[0]] = of # not actually divergent, just matching renames
301 copy[fl[0]] = of # not actually divergent, just matching renames
274
302
275 if fullcopy and repo.ui.debugflag:
303 if fullcopy and repo.ui.debugflag:
276 repo.ui.debug(" all copies found (* = to merge, ! = divergent, "
304 repo.ui.debug(" all copies found (* = to merge, ! = divergent, "
277 "% = renamed and deleted):\n")
305 "% = renamed and deleted):\n")
278 for f in sorted(fullcopy):
306 for f in sorted(fullcopy):
279 note = ""
307 note = ""
280 if f in copy:
308 if f in copy:
281 note += "*"
309 note += "*"
282 if f in diverge2:
310 if f in diverge2:
283 note += "!"
311 note += "!"
284 if f in renamedelete2:
312 if f in renamedelete2:
285 note += "%"
313 note += "%"
286 repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f,
314 repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f,
287 note))
315 note))
288 del diverge2
316 del diverge2
289
317
290 if not fullcopy:
318 if not fullcopy:
291 return copy, movewithdir, diverge, renamedelete
319 return copy, movewithdir, diverge, renamedelete
292
320
293 repo.ui.debug(" checking for directory renames\n")
321 repo.ui.debug(" checking for directory renames\n")
294
322
295 # generate a directory move map
323 # generate a directory move map
296 d1, d2 = c1.dirs(), c2.dirs()
324 d1, d2 = c1.dirs(), c2.dirs()
297 d1.addpath('/')
325 d1.addpath('/')
298 d2.addpath('/')
326 d2.addpath('/')
299 invalid = set()
327 invalid = set()
300 dirmove = {}
328 dirmove = {}
301
329
302 # examine each file copy for a potential directory move, which is
330 # examine each file copy for a potential directory move, which is
303 # when all the files in a directory are moved to a new directory
331 # when all the files in a directory are moved to a new directory
304 for dst, src in fullcopy.iteritems():
332 for dst, src in fullcopy.iteritems():
305 dsrc, ddst = _dirname(src), _dirname(dst)
333 dsrc, ddst = _dirname(src), _dirname(dst)
306 if dsrc in invalid:
334 if dsrc in invalid:
307 # already seen to be uninteresting
335 # already seen to be uninteresting
308 continue
336 continue
309 elif dsrc in d1 and ddst in d1:
337 elif dsrc in d1 and ddst in d1:
310 # directory wasn't entirely moved locally
338 # directory wasn't entirely moved locally
311 invalid.add(dsrc)
339 invalid.add(dsrc)
312 elif dsrc in d2 and ddst in d2:
340 elif dsrc in d2 and ddst in d2:
313 # directory wasn't entirely moved remotely
341 # directory wasn't entirely moved remotely
314 invalid.add(dsrc)
342 invalid.add(dsrc)
315 elif dsrc in dirmove and dirmove[dsrc] != ddst:
343 elif dsrc in dirmove and dirmove[dsrc] != ddst:
316 # files from the same directory moved to two different places
344 # files from the same directory moved to two different places
317 invalid.add(dsrc)
345 invalid.add(dsrc)
318 else:
346 else:
319 # looks good so far
347 # looks good so far
320 dirmove[dsrc + "/"] = ddst + "/"
348 dirmove[dsrc + "/"] = ddst + "/"
321
349
322 for i in invalid:
350 for i in invalid:
323 if i in dirmove:
351 if i in dirmove:
324 del dirmove[i]
352 del dirmove[i]
325 del d1, d2, invalid
353 del d1, d2, invalid
326
354
327 if not dirmove:
355 if not dirmove:
328 return copy, movewithdir, diverge, renamedelete
356 return copy, movewithdir, diverge, renamedelete
329
357
330 for d in dirmove:
358 for d in dirmove:
331 repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" %
359 repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" %
332 (d, dirmove[d]))
360 (d, dirmove[d]))
333
361
334 # check unaccounted nonoverlapping files against directory moves
362 # check unaccounted nonoverlapping files against directory moves
335 for f in u1 + u2:
363 for f in u1 + u2:
336 if f not in fullcopy:
364 if f not in fullcopy:
337 for d in dirmove:
365 for d in dirmove:
338 if f.startswith(d):
366 if f.startswith(d):
339 # new file added in a directory that was moved, move it
367 # new file added in a directory that was moved, move it
340 df = dirmove[d] + f[len(d):]
368 df = dirmove[d] + f[len(d):]
341 if df not in copy:
369 if df not in copy:
342 movewithdir[f] = df
370 movewithdir[f] = df
343 repo.ui.debug((" pending file src: '%s' -> "
371 repo.ui.debug((" pending file src: '%s' -> "
344 "dst: '%s'\n") % (f, df))
372 "dst: '%s'\n") % (f, df))
345 break
373 break
346
374
347 return copy, movewithdir, diverge, renamedelete
375 return copy, movewithdir, diverge, renamedelete
348
376
349 def checkcopies(ctx, f, m1, m2, ca, limit, diverge, copy, fullcopy):
377 def checkcopies(ctx, f, m1, m2, ca, limit, diverge, copy, fullcopy):
350 """
378 """
351 check possible copies of f from m1 to m2
379 check possible copies of f from m1 to m2
352
380
353 ctx = function accepting (filename, node) that returns a filectx.
381 ctx = function accepting (filename, node) that returns a filectx.
354 f = the filename to check
382 f = the filename to check
355 m1 = the source manifest
383 m1 = the source manifest
356 m2 = the destination manifest
384 m2 = the destination manifest
357 ca = the changectx of the common ancestor
385 ca = the changectx of the common ancestor
358 limit = the rev number to not search beyond
386 limit = the rev number to not search beyond
359 diverge = record all diverges in this dict
387 diverge = record all diverges in this dict
360 copy = record all non-divergent copies in this dict
388 copy = record all non-divergent copies in this dict
361 fullcopy = record all copies in this dict
389 fullcopy = record all copies in this dict
362 """
390 """
363
391
364 ma = ca.manifest()
392 ma = ca.manifest()
365
393
366 def _related(f1, f2, limit):
394 def _related(f1, f2, limit):
367 # Walk back to common ancestor to see if the two files originate
395 # Walk back to common ancestor to see if the two files originate
368 # from the same file. Since workingfilectx's rev() is None it messes
396 # from the same file. Since workingfilectx's rev() is None it messes
369 # up the integer comparison logic, hence the pre-step check for
397 # up the integer comparison logic, hence the pre-step check for
370 # None (f1 and f2 can only be workingfilectx's initially).
398 # None (f1 and f2 can only be workingfilectx's initially).
371
399
372 if f1 == f2:
400 if f1 == f2:
373 return f1 # a match
401 return f1 # a match
374
402
375 g1, g2 = f1.ancestors(), f2.ancestors()
403 g1, g2 = f1.ancestors(), f2.ancestors()
376 try:
404 try:
377 f1r, f2r = f1.rev(), f2.rev()
405 f1r, f2r = f1.rev(), f2.rev()
378
406
379 if f1r is None:
407 if f1r is None:
380 f1 = g1.next()
408 f1 = g1.next()
381 if f2r is None:
409 if f2r is None:
382 f2 = g2.next()
410 f2 = g2.next()
383
411
384 while True:
412 while True:
385 f1r, f2r = f1.rev(), f2.rev()
413 f1r, f2r = f1.rev(), f2.rev()
386 if f1r > f2r:
414 if f1r > f2r:
387 f1 = g1.next()
415 f1 = g1.next()
388 elif f2r > f1r:
416 elif f2r > f1r:
389 f2 = g2.next()
417 f2 = g2.next()
390 elif f1 == f2:
418 elif f1 == f2:
391 return f1 # a match
419 return f1 # a match
392 elif f1r == f2r or f1r < limit or f2r < limit:
420 elif f1r == f2r or f1r < limit or f2r < limit:
393 return False # copy no longer relevant
421 return False # copy no longer relevant
394 except StopIteration:
422 except StopIteration:
395 return False
423 return False
396
424
397 of = None
425 of = None
398 seen = set([f])
426 seen = set([f])
399 for oc in ctx(f, m1[f]).ancestors():
427 for oc in ctx(f, m1[f]).ancestors():
400 ocr = oc.rev()
428 ocr = oc.rev()
401 of = oc.path()
429 of = oc.path()
402 if of in seen:
430 if of in seen:
403 # check limit late - grab last rename before
431 # check limit late - grab last rename before
404 if ocr < limit:
432 if ocr < limit:
405 break
433 break
406 continue
434 continue
407 seen.add(of)
435 seen.add(of)
408
436
409 fullcopy[f] = of # remember for dir rename detection
437 fullcopy[f] = of # remember for dir rename detection
410 if of not in m2:
438 if of not in m2:
411 continue # no match, keep looking
439 continue # no match, keep looking
412 if m2[of] == ma.get(of):
440 if m2[of] == ma.get(of):
413 break # no merge needed, quit early
441 break # no merge needed, quit early
414 c2 = ctx(of, m2[of])
442 c2 = ctx(of, m2[of])
415 cr = _related(oc, c2, ca.rev())
443 cr = _related(oc, c2, ca.rev())
416 if cr and (of == f or of == c2.path()): # non-divergent
444 if cr and (of == f or of == c2.path()): # non-divergent
417 copy[f] = of
445 copy[f] = of
418 of = None
446 of = None
419 break
447 break
420
448
421 if of in ma:
449 if of in ma:
422 diverge.setdefault(of, []).append(f)
450 diverge.setdefault(of, []).append(f)
423
451
424 def duplicatecopies(repo, rev, fromrev, skiprev=None):
452 def duplicatecopies(repo, rev, fromrev, skiprev=None):
425 '''reproduce copies from fromrev to rev in the dirstate
453 '''reproduce copies from fromrev to rev in the dirstate
426
454
427 If skiprev is specified, it's a revision that should be used to
455 If skiprev is specified, it's a revision that should be used to
428 filter copy records. Any copies that occur between fromrev and
456 filter copy records. Any copies that occur between fromrev and
429 skiprev will not be duplicated, even if they appear in the set of
457 skiprev will not be duplicated, even if they appear in the set of
430 copies between fromrev and rev.
458 copies between fromrev and rev.
431 '''
459 '''
432 exclude = {}
460 exclude = {}
433 if skiprev is not None:
461 if skiprev is not None:
434 exclude = pathcopies(repo[fromrev], repo[skiprev])
462 exclude = pathcopies(repo[fromrev], repo[skiprev])
435 for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems():
463 for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems():
436 # copies.pathcopies returns backward renames, so dst might not
464 # copies.pathcopies returns backward renames, so dst might not
437 # actually be in the dirstate
465 # actually be in the dirstate
438 if dst in exclude:
466 if dst in exclude:
439 continue
467 continue
440 if repo.dirstate[dst] in "nma":
468 if repo.dirstate[dst] in "nma":
441 repo.dirstate.copy(src, dst)
469 repo.dirstate.copy(src, dst)
@@ -1,856 +1,914 b''
1 $ hg init
1 $ hg init
2
2
3 Setup:
3 Setup:
4
4
5 $ echo a >> a
5 $ echo a >> a
6 $ hg ci -Am 'base'
6 $ hg ci -Am 'base'
7 adding a
7 adding a
8
8
9 Refuse to amend public csets:
9 Refuse to amend public csets:
10
10
11 $ hg phase -r . -p
11 $ hg phase -r . -p
12 $ hg ci --amend
12 $ hg ci --amend
13 abort: cannot amend public changesets
13 abort: cannot amend public changesets
14 [255]
14 [255]
15 $ hg phase -r . -f -d
15 $ hg phase -r . -f -d
16
16
17 $ echo a >> a
17 $ echo a >> a
18 $ hg ci -Am 'base1'
18 $ hg ci -Am 'base1'
19
19
20 Nothing to amend:
20 Nothing to amend:
21
21
22 $ hg ci --amend
22 $ hg ci --amend
23 nothing changed
23 nothing changed
24 [1]
24 [1]
25
25
26 $ cat >> $HGRCPATH <<EOF
26 $ cat >> $HGRCPATH <<EOF
27 > [hooks]
27 > [hooks]
28 > pretxncommit.foo = sh -c "echo \\"pretxncommit \$HG_NODE\\"; hg id -r \$HG_NODE"
28 > pretxncommit.foo = sh -c "echo \\"pretxncommit \$HG_NODE\\"; hg id -r \$HG_NODE"
29 > EOF
29 > EOF
30
30
31 Amending changeset with changes in working dir:
31 Amending changeset with changes in working dir:
32 (and check that --message does not trigger an editor)
32 (and check that --message does not trigger an editor)
33
33
34 $ echo a >> a
34 $ echo a >> a
35 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -m 'amend base1'
35 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -m 'amend base1'
36 pretxncommit 43f1ba15f28a50abf0aae529cf8a16bfced7b149
36 pretxncommit 43f1ba15f28a50abf0aae529cf8a16bfced7b149
37 43f1ba15f28a tip
37 43f1ba15f28a tip
38 saved backup bundle to $TESTTMP/.hg/strip-backup/489edb5b847d-amend-backup.hg (glob)
38 saved backup bundle to $TESTTMP/.hg/strip-backup/489edb5b847d-amend-backup.hg (glob)
39 $ echo 'pretxncommit.foo = ' >> $HGRCPATH
39 $ echo 'pretxncommit.foo = ' >> $HGRCPATH
40 $ hg diff -c .
40 $ hg diff -c .
41 diff -r ad120869acf0 -r 43f1ba15f28a a
41 diff -r ad120869acf0 -r 43f1ba15f28a a
42 --- a/a Thu Jan 01 00:00:00 1970 +0000
42 --- a/a Thu Jan 01 00:00:00 1970 +0000
43 +++ b/a Thu Jan 01 00:00:00 1970 +0000
43 +++ b/a Thu Jan 01 00:00:00 1970 +0000
44 @@ -1,1 +1,3 @@
44 @@ -1,1 +1,3 @@
45 a
45 a
46 +a
46 +a
47 +a
47 +a
48 $ hg log
48 $ hg log
49 changeset: 1:43f1ba15f28a
49 changeset: 1:43f1ba15f28a
50 tag: tip
50 tag: tip
51 user: test
51 user: test
52 date: Thu Jan 01 00:00:00 1970 +0000
52 date: Thu Jan 01 00:00:00 1970 +0000
53 summary: amend base1
53 summary: amend base1
54
54
55 changeset: 0:ad120869acf0
55 changeset: 0:ad120869acf0
56 user: test
56 user: test
57 date: Thu Jan 01 00:00:00 1970 +0000
57 date: Thu Jan 01 00:00:00 1970 +0000
58 summary: base
58 summary: base
59
59
60
60
61 Check proper abort for empty message
61 Check proper abort for empty message
62
62
63 $ cat > editor.sh << '__EOF__'
63 $ cat > editor.sh << '__EOF__'
64 > #!/bin/sh
64 > #!/bin/sh
65 > echo "" > "$1"
65 > echo "" > "$1"
66 > __EOF__
66 > __EOF__
67 $ echo b > b
67 $ echo b > b
68 $ hg add b
68 $ hg add b
69 $ hg summary
69 $ hg summary
70 parent: 1:43f1ba15f28a tip
70 parent: 1:43f1ba15f28a tip
71 amend base1
71 amend base1
72 branch: default
72 branch: default
73 commit: 1 added, 1 unknown
73 commit: 1 added, 1 unknown
74 update: (current)
74 update: (current)
75 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend
75 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend
76 transaction abort!
76 transaction abort!
77 rollback completed
77 rollback completed
78 abort: empty commit message
78 abort: empty commit message
79 [255]
79 [255]
80 $ hg summary
80 $ hg summary
81 parent: 1:43f1ba15f28a tip
81 parent: 1:43f1ba15f28a tip
82 amend base1
82 amend base1
83 branch: default
83 branch: default
84 commit: 1 added, 1 unknown
84 commit: 1 added, 1 unknown
85 update: (current)
85 update: (current)
86
86
87 Add new file:
87 Add new file:
88 $ hg ci --amend -m 'amend base1 new file'
88 $ hg ci --amend -m 'amend base1 new file'
89 saved backup bundle to $TESTTMP/.hg/strip-backup/43f1ba15f28a-amend-backup.hg (glob)
89 saved backup bundle to $TESTTMP/.hg/strip-backup/43f1ba15f28a-amend-backup.hg (glob)
90
90
91 Remove file that was added in amended commit:
91 Remove file that was added in amended commit:
92 (and test logfile option)
92 (and test logfile option)
93 (and test that logfile option do not trigger an editor)
93 (and test that logfile option do not trigger an editor)
94
94
95 $ hg rm b
95 $ hg rm b
96 $ echo 'amend base1 remove new file' > ../logfile
96 $ echo 'amend base1 remove new file' > ../logfile
97 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg ci --amend --logfile ../logfile
97 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg ci --amend --logfile ../logfile
98 saved backup bundle to $TESTTMP/.hg/strip-backup/b8e3cb2b3882-amend-backup.hg (glob)
98 saved backup bundle to $TESTTMP/.hg/strip-backup/b8e3cb2b3882-amend-backup.hg (glob)
99
99
100 $ hg cat b
100 $ hg cat b
101 b: no such file in rev 74609c7f506e
101 b: no such file in rev 74609c7f506e
102 [1]
102 [1]
103
103
104 No changes, just a different message:
104 No changes, just a different message:
105
105
106 $ hg ci -v --amend -m 'no changes, new message'
106 $ hg ci -v --amend -m 'no changes, new message'
107 amending changeset 74609c7f506e
107 amending changeset 74609c7f506e
108 copying changeset 74609c7f506e to ad120869acf0
108 copying changeset 74609c7f506e to ad120869acf0
109 a
109 a
110 stripping amended changeset 74609c7f506e
110 stripping amended changeset 74609c7f506e
111 1 changesets found
111 1 changesets found
112 saved backup bundle to $TESTTMP/.hg/strip-backup/74609c7f506e-amend-backup.hg (glob)
112 saved backup bundle to $TESTTMP/.hg/strip-backup/74609c7f506e-amend-backup.hg (glob)
113 1 changesets found
113 1 changesets found
114 adding branch
114 adding branch
115 adding changesets
115 adding changesets
116 adding manifests
116 adding manifests
117 adding file changes
117 adding file changes
118 added 1 changesets with 1 changes to 1 files
118 added 1 changesets with 1 changes to 1 files
119 committed changeset 1:1cd866679df8
119 committed changeset 1:1cd866679df8
120 $ hg diff -c .
120 $ hg diff -c .
121 diff -r ad120869acf0 -r 1cd866679df8 a
121 diff -r ad120869acf0 -r 1cd866679df8 a
122 --- a/a Thu Jan 01 00:00:00 1970 +0000
122 --- a/a Thu Jan 01 00:00:00 1970 +0000
123 +++ b/a Thu Jan 01 00:00:00 1970 +0000
123 +++ b/a Thu Jan 01 00:00:00 1970 +0000
124 @@ -1,1 +1,3 @@
124 @@ -1,1 +1,3 @@
125 a
125 a
126 +a
126 +a
127 +a
127 +a
128 $ hg log
128 $ hg log
129 changeset: 1:1cd866679df8
129 changeset: 1:1cd866679df8
130 tag: tip
130 tag: tip
131 user: test
131 user: test
132 date: Thu Jan 01 00:00:00 1970 +0000
132 date: Thu Jan 01 00:00:00 1970 +0000
133 summary: no changes, new message
133 summary: no changes, new message
134
134
135 changeset: 0:ad120869acf0
135 changeset: 0:ad120869acf0
136 user: test
136 user: test
137 date: Thu Jan 01 00:00:00 1970 +0000
137 date: Thu Jan 01 00:00:00 1970 +0000
138 summary: base
138 summary: base
139
139
140
140
141 Disable default date on commit so when -d isn't given, the old date is preserved:
141 Disable default date on commit so when -d isn't given, the old date is preserved:
142
142
143 $ echo '[defaults]' >> $HGRCPATH
143 $ echo '[defaults]' >> $HGRCPATH
144 $ echo 'commit=' >> $HGRCPATH
144 $ echo 'commit=' >> $HGRCPATH
145
145
146 Test -u/-d:
146 Test -u/-d:
147
147
148 $ cat > .hg/checkeditform.sh <<EOF
148 $ cat > .hg/checkeditform.sh <<EOF
149 > env | grep HGEDITFORM
149 > env | grep HGEDITFORM
150 > true
150 > true
151 > EOF
151 > EOF
152 $ HGEDITOR="sh .hg/checkeditform.sh" hg ci --amend -u foo -d '1 0'
152 $ HGEDITOR="sh .hg/checkeditform.sh" hg ci --amend -u foo -d '1 0'
153 HGEDITFORM=commit.amend.normal
153 HGEDITFORM=commit.amend.normal
154 saved backup bundle to $TESTTMP/.hg/strip-backup/1cd866679df8-amend-backup.hg (glob)
154 saved backup bundle to $TESTTMP/.hg/strip-backup/1cd866679df8-amend-backup.hg (glob)
155 $ echo a >> a
155 $ echo a >> a
156 $ hg ci --amend -u foo -d '1 0'
156 $ hg ci --amend -u foo -d '1 0'
157 saved backup bundle to $TESTTMP/.hg/strip-backup/780e6f23e03d-amend-backup.hg (glob)
157 saved backup bundle to $TESTTMP/.hg/strip-backup/780e6f23e03d-amend-backup.hg (glob)
158 $ hg log -r .
158 $ hg log -r .
159 changeset: 1:5f357c7560ab
159 changeset: 1:5f357c7560ab
160 tag: tip
160 tag: tip
161 user: foo
161 user: foo
162 date: Thu Jan 01 00:00:01 1970 +0000
162 date: Thu Jan 01 00:00:01 1970 +0000
163 summary: no changes, new message
163 summary: no changes, new message
164
164
165
165
166 Open editor with old commit message if a message isn't given otherwise:
166 Open editor with old commit message if a message isn't given otherwise:
167
167
168 $ cat > editor.sh << '__EOF__'
168 $ cat > editor.sh << '__EOF__'
169 > #!/bin/sh
169 > #!/bin/sh
170 > cat $1
170 > cat $1
171 > echo "another precious commit message" > "$1"
171 > echo "another precious commit message" > "$1"
172 > __EOF__
172 > __EOF__
173
173
174 at first, test saving last-message.txt
174 at first, test saving last-message.txt
175
175
176 $ cat > .hg/hgrc << '__EOF__'
176 $ cat > .hg/hgrc << '__EOF__'
177 > [hooks]
177 > [hooks]
178 > pretxncommit.test-saving-last-message = false
178 > pretxncommit.test-saving-last-message = false
179 > __EOF__
179 > __EOF__
180
180
181 $ rm -f .hg/last-message.txt
181 $ rm -f .hg/last-message.txt
182 $ hg commit --amend -v -m "message given from command line"
182 $ hg commit --amend -v -m "message given from command line"
183 amending changeset 5f357c7560ab
183 amending changeset 5f357c7560ab
184 copying changeset 5f357c7560ab to ad120869acf0
184 copying changeset 5f357c7560ab to ad120869acf0
185 a
185 a
186 running hook pretxncommit.test-saving-last-message: false
186 running hook pretxncommit.test-saving-last-message: false
187 transaction abort!
187 transaction abort!
188 rollback completed
188 rollback completed
189 abort: pretxncommit.test-saving-last-message hook exited with status 1
189 abort: pretxncommit.test-saving-last-message hook exited with status 1
190 [255]
190 [255]
191 $ cat .hg/last-message.txt
191 $ cat .hg/last-message.txt
192 message given from command line (no-eol)
192 message given from command line (no-eol)
193
193
194 $ rm -f .hg/last-message.txt
194 $ rm -f .hg/last-message.txt
195 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
195 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
196 amending changeset 5f357c7560ab
196 amending changeset 5f357c7560ab
197 copying changeset 5f357c7560ab to ad120869acf0
197 copying changeset 5f357c7560ab to ad120869acf0
198 no changes, new message
198 no changes, new message
199
199
200
200
201 HG: Enter commit message. Lines beginning with 'HG:' are removed.
201 HG: Enter commit message. Lines beginning with 'HG:' are removed.
202 HG: Leave message empty to abort commit.
202 HG: Leave message empty to abort commit.
203 HG: --
203 HG: --
204 HG: user: foo
204 HG: user: foo
205 HG: branch 'default'
205 HG: branch 'default'
206 HG: changed a
206 HG: changed a
207 a
207 a
208 running hook pretxncommit.test-saving-last-message: false
208 running hook pretxncommit.test-saving-last-message: false
209 transaction abort!
209 transaction abort!
210 rollback completed
210 rollback completed
211 abort: pretxncommit.test-saving-last-message hook exited with status 1
211 abort: pretxncommit.test-saving-last-message hook exited with status 1
212 [255]
212 [255]
213
213
214 $ cat .hg/last-message.txt
214 $ cat .hg/last-message.txt
215 another precious commit message
215 another precious commit message
216
216
217 $ cat > .hg/hgrc << '__EOF__'
217 $ cat > .hg/hgrc << '__EOF__'
218 > [hooks]
218 > [hooks]
219 > pretxncommit.test-saving-last-message =
219 > pretxncommit.test-saving-last-message =
220 > __EOF__
220 > __EOF__
221
221
222 then, test editing custom commit message
222 then, test editing custom commit message
223
223
224 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
224 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
225 amending changeset 5f357c7560ab
225 amending changeset 5f357c7560ab
226 copying changeset 5f357c7560ab to ad120869acf0
226 copying changeset 5f357c7560ab to ad120869acf0
227 no changes, new message
227 no changes, new message
228
228
229
229
230 HG: Enter commit message. Lines beginning with 'HG:' are removed.
230 HG: Enter commit message. Lines beginning with 'HG:' are removed.
231 HG: Leave message empty to abort commit.
231 HG: Leave message empty to abort commit.
232 HG: --
232 HG: --
233 HG: user: foo
233 HG: user: foo
234 HG: branch 'default'
234 HG: branch 'default'
235 HG: changed a
235 HG: changed a
236 a
236 a
237 stripping amended changeset 5f357c7560ab
237 stripping amended changeset 5f357c7560ab
238 1 changesets found
238 1 changesets found
239 saved backup bundle to $TESTTMP/.hg/strip-backup/5f357c7560ab-amend-backup.hg (glob)
239 saved backup bundle to $TESTTMP/.hg/strip-backup/5f357c7560ab-amend-backup.hg (glob)
240 1 changesets found
240 1 changesets found
241 adding branch
241 adding branch
242 adding changesets
242 adding changesets
243 adding manifests
243 adding manifests
244 adding file changes
244 adding file changes
245 added 1 changesets with 1 changes to 1 files
245 added 1 changesets with 1 changes to 1 files
246 committed changeset 1:7ab3bf440b54
246 committed changeset 1:7ab3bf440b54
247
247
248 Same, but with changes in working dir (different code path):
248 Same, but with changes in working dir (different code path):
249
249
250 $ echo a >> a
250 $ echo a >> a
251 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
251 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend -v
252 amending changeset 7ab3bf440b54
252 amending changeset 7ab3bf440b54
253 a
253 a
254 copying changeset a0ea9b1a4c8c to ad120869acf0
254 copying changeset a0ea9b1a4c8c to ad120869acf0
255 another precious commit message
255 another precious commit message
256
256
257
257
258 HG: Enter commit message. Lines beginning with 'HG:' are removed.
258 HG: Enter commit message. Lines beginning with 'HG:' are removed.
259 HG: Leave message empty to abort commit.
259 HG: Leave message empty to abort commit.
260 HG: --
260 HG: --
261 HG: user: foo
261 HG: user: foo
262 HG: branch 'default'
262 HG: branch 'default'
263 HG: changed a
263 HG: changed a
264 a
264 a
265 stripping intermediate changeset a0ea9b1a4c8c
265 stripping intermediate changeset a0ea9b1a4c8c
266 stripping amended changeset 7ab3bf440b54
266 stripping amended changeset 7ab3bf440b54
267 2 changesets found
267 2 changesets found
268 saved backup bundle to $TESTTMP/.hg/strip-backup/7ab3bf440b54-amend-backup.hg (glob)
268 saved backup bundle to $TESTTMP/.hg/strip-backup/7ab3bf440b54-amend-backup.hg (glob)
269 1 changesets found
269 1 changesets found
270 adding branch
270 adding branch
271 adding changesets
271 adding changesets
272 adding manifests
272 adding manifests
273 adding file changes
273 adding file changes
274 added 1 changesets with 1 changes to 1 files
274 added 1 changesets with 1 changes to 1 files
275 committed changeset 1:ea22a388757c
275 committed changeset 1:ea22a388757c
276
276
277 $ rm editor.sh
277 $ rm editor.sh
278 $ hg log -r .
278 $ hg log -r .
279 changeset: 1:ea22a388757c
279 changeset: 1:ea22a388757c
280 tag: tip
280 tag: tip
281 user: foo
281 user: foo
282 date: Thu Jan 01 00:00:01 1970 +0000
282 date: Thu Jan 01 00:00:01 1970 +0000
283 summary: another precious commit message
283 summary: another precious commit message
284
284
285
285
286 Moving bookmarks, preserve active bookmark:
286 Moving bookmarks, preserve active bookmark:
287
287
288 $ hg book book1
288 $ hg book book1
289 $ hg book book2
289 $ hg book book2
290 $ hg ci --amend -m 'move bookmarks'
290 $ hg ci --amend -m 'move bookmarks'
291 saved backup bundle to $TESTTMP/.hg/strip-backup/ea22a388757c-amend-backup.hg (glob)
291 saved backup bundle to $TESTTMP/.hg/strip-backup/ea22a388757c-amend-backup.hg (glob)
292 $ hg book
292 $ hg book
293 book1 1:6cec5aa930e2
293 book1 1:6cec5aa930e2
294 * book2 1:6cec5aa930e2
294 * book2 1:6cec5aa930e2
295 $ echo a >> a
295 $ echo a >> a
296 $ hg ci --amend -m 'move bookmarks'
296 $ hg ci --amend -m 'move bookmarks'
297 saved backup bundle to $TESTTMP/.hg/strip-backup/6cec5aa930e2-amend-backup.hg (glob)
297 saved backup bundle to $TESTTMP/.hg/strip-backup/6cec5aa930e2-amend-backup.hg (glob)
298 $ hg book
298 $ hg book
299 book1 1:48bb6e53a15f
299 book1 1:48bb6e53a15f
300 * book2 1:48bb6e53a15f
300 * book2 1:48bb6e53a15f
301
301
302 abort does not loose bookmarks
302 abort does not loose bookmarks
303
303
304 $ cat > editor.sh << '__EOF__'
304 $ cat > editor.sh << '__EOF__'
305 > #!/bin/sh
305 > #!/bin/sh
306 > echo "" > "$1"
306 > echo "" > "$1"
307 > __EOF__
307 > __EOF__
308 $ echo a >> a
308 $ echo a >> a
309 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend
309 $ HGEDITOR="\"sh\" \"`pwd`/editor.sh\"" hg commit --amend
310 transaction abort!
310 transaction abort!
311 rollback completed
311 rollback completed
312 abort: empty commit message
312 abort: empty commit message
313 [255]
313 [255]
314 $ hg book
314 $ hg book
315 book1 1:48bb6e53a15f
315 book1 1:48bb6e53a15f
316 * book2 1:48bb6e53a15f
316 * book2 1:48bb6e53a15f
317 $ hg revert -Caq
317 $ hg revert -Caq
318 $ rm editor.sh
318 $ rm editor.sh
319
319
320 $ echo '[defaults]' >> $HGRCPATH
320 $ echo '[defaults]' >> $HGRCPATH
321 $ echo "commit=-d '0 0'" >> $HGRCPATH
321 $ echo "commit=-d '0 0'" >> $HGRCPATH
322
322
323 Moving branches:
323 Moving branches:
324
324
325 $ hg branch foo
325 $ hg branch foo
326 marked working directory as branch foo
326 marked working directory as branch foo
327 (branches are permanent and global, did you want a bookmark?)
327 (branches are permanent and global, did you want a bookmark?)
328 $ echo a >> a
328 $ echo a >> a
329 $ hg ci -m 'branch foo'
329 $ hg ci -m 'branch foo'
330 $ hg branch default -f
330 $ hg branch default -f
331 marked working directory as branch default
331 marked working directory as branch default
332 (branches are permanent and global, did you want a bookmark?)
332 (branches are permanent and global, did you want a bookmark?)
333 $ hg ci --amend -m 'back to default'
333 $ hg ci --amend -m 'back to default'
334 saved backup bundle to $TESTTMP/.hg/strip-backup/8ac881fbf49d-amend-backup.hg (glob)
334 saved backup bundle to $TESTTMP/.hg/strip-backup/8ac881fbf49d-amend-backup.hg (glob)
335 $ hg branches
335 $ hg branches
336 default 2:ce12b0b57d46
336 default 2:ce12b0b57d46
337
337
338 Close branch:
338 Close branch:
339
339
340 $ hg up -q 0
340 $ hg up -q 0
341 $ echo b >> b
341 $ echo b >> b
342 $ hg branch foo
342 $ hg branch foo
343 marked working directory as branch foo
343 marked working directory as branch foo
344 (branches are permanent and global, did you want a bookmark?)
344 (branches are permanent and global, did you want a bookmark?)
345 $ hg ci -Am 'fork'
345 $ hg ci -Am 'fork'
346 adding b
346 adding b
347 $ echo b >> b
347 $ echo b >> b
348 $ hg ci -mb
348 $ hg ci -mb
349 $ hg ci --amend --close-branch -m 'closing branch foo'
349 $ hg ci --amend --close-branch -m 'closing branch foo'
350 saved backup bundle to $TESTTMP/.hg/strip-backup/c962248fa264-amend-backup.hg (glob)
350 saved backup bundle to $TESTTMP/.hg/strip-backup/c962248fa264-amend-backup.hg (glob)
351
351
352 Same thing, different code path:
352 Same thing, different code path:
353
353
354 $ echo b >> b
354 $ echo b >> b
355 $ hg ci -m 'reopen branch'
355 $ hg ci -m 'reopen branch'
356 reopening closed branch head 4
356 reopening closed branch head 4
357 $ echo b >> b
357 $ echo b >> b
358 $ hg ci --amend --close-branch
358 $ hg ci --amend --close-branch
359 saved backup bundle to $TESTTMP/.hg/strip-backup/027371728205-amend-backup.hg (glob)
359 saved backup bundle to $TESTTMP/.hg/strip-backup/027371728205-amend-backup.hg (glob)
360 $ hg branches
360 $ hg branches
361 default 2:ce12b0b57d46
361 default 2:ce12b0b57d46
362
362
363 Refuse to amend during a merge:
363 Refuse to amend during a merge:
364
364
365 $ hg up -q default
365 $ hg up -q default
366 $ hg merge foo
366 $ hg merge foo
367 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 (branch merge, don't forget to commit)
368 (branch merge, don't forget to commit)
369 $ hg ci --amend
369 $ hg ci --amend
370 abort: cannot amend while merging
370 abort: cannot amend while merging
371 [255]
371 [255]
372 $ hg ci -m 'merge'
372 $ hg ci -m 'merge'
373
373
374 Follow copies/renames:
374 Follow copies/renames:
375
375
376 $ hg mv b c
376 $ hg mv b c
377 $ hg ci -m 'b -> c'
377 $ hg ci -m 'b -> c'
378 $ hg mv c d
378 $ hg mv c d
379 $ hg ci --amend -m 'b -> d'
379 $ hg ci --amend -m 'b -> d'
380 saved backup bundle to $TESTTMP/.hg/strip-backup/b8c6eac7f12e-amend-backup.hg (glob)
380 saved backup bundle to $TESTTMP/.hg/strip-backup/b8c6eac7f12e-amend-backup.hg (glob)
381 $ hg st --rev '.^' --copies d
381 $ hg st --rev '.^' --copies d
382 A d
382 A d
383 b
383 b
384 $ hg cp d e
384 $ hg cp d e
385 $ hg ci -m 'e = d'
385 $ hg ci -m 'e = d'
386 $ hg cp e f
386 $ hg cp e f
387 $ hg ci --amend -m 'f = d'
387 $ hg ci --amend -m 'f = d'
388 saved backup bundle to $TESTTMP/.hg/strip-backup/7f9761d65613-amend-backup.hg (glob)
388 saved backup bundle to $TESTTMP/.hg/strip-backup/7f9761d65613-amend-backup.hg (glob)
389 $ hg st --rev '.^' --copies f
389 $ hg st --rev '.^' --copies f
390 A f
390 A f
391 d
391 d
392
392
393 $ mv f f.orig
393 $ mv f f.orig
394 $ hg rm -A f
394 $ hg rm -A f
395 $ hg ci -m removef
395 $ hg ci -m removef
396 $ hg cp a f
396 $ hg cp a f
397 $ mv f.orig f
397 $ mv f.orig f
398 $ hg ci --amend -m replacef
398 $ hg ci --amend -m replacef
399 saved backup bundle to $TESTTMP/.hg/strip-backup/9e8c5f7e3d95-amend-backup.hg (glob)
399 saved backup bundle to $TESTTMP/.hg/strip-backup/9e8c5f7e3d95-amend-backup.hg (glob)
400 $ hg st --change . --copies
400 $ hg st --change . --copies
401 $ hg log -r . --template "{file_copies}\n"
401 $ hg log -r . --template "{file_copies}\n"
402
402
403
403
404 Move added file (issue3410):
404 Move added file (issue3410):
405
405
406 $ echo g >> g
406 $ echo g >> g
407 $ hg ci -Am g
407 $ hg ci -Am g
408 adding g
408 adding g
409 $ hg mv g h
409 $ hg mv g h
410 $ hg ci --amend
410 $ hg ci --amend
411 saved backup bundle to $TESTTMP/.hg/strip-backup/24aa8eacce2b-amend-backup.hg (glob)
411 saved backup bundle to $TESTTMP/.hg/strip-backup/24aa8eacce2b-amend-backup.hg (glob)
412 $ hg st --change . --copies h
412 $ hg st --change . --copies h
413 A h
413 A h
414 $ hg log -r . --template "{file_copies}\n"
414 $ hg log -r . --template "{file_copies}\n"
415
415
416
416
417 Can't rollback an amend:
417 Can't rollback an amend:
418
418
419 $ hg rollback
419 $ hg rollback
420 no rollback information available
420 no rollback information available
421 [1]
421 [1]
422
422
423 Preserve extra dict (issue3430):
423 Preserve extra dict (issue3430):
424
424
425 $ hg branch a
425 $ hg branch a
426 marked working directory as branch a
426 marked working directory as branch a
427 (branches are permanent and global, did you want a bookmark?)
427 (branches are permanent and global, did you want a bookmark?)
428 $ echo a >> a
428 $ echo a >> a
429 $ hg ci -ma
429 $ hg ci -ma
430 $ hg ci --amend -m "a'"
430 $ hg ci --amend -m "a'"
431 saved backup bundle to $TESTTMP/.hg/strip-backup/3837aa2a2fdb-amend-backup.hg (glob)
431 saved backup bundle to $TESTTMP/.hg/strip-backup/3837aa2a2fdb-amend-backup.hg (glob)
432 $ hg log -r . --template "{branch}\n"
432 $ hg log -r . --template "{branch}\n"
433 a
433 a
434 $ hg ci --amend -m "a''"
434 $ hg ci --amend -m "a''"
435 saved backup bundle to $TESTTMP/.hg/strip-backup/c05c06be7514-amend-backup.hg (glob)
435 saved backup bundle to $TESTTMP/.hg/strip-backup/c05c06be7514-amend-backup.hg (glob)
436 $ hg log -r . --template "{branch}\n"
436 $ hg log -r . --template "{branch}\n"
437 a
437 a
438
438
439 Also preserve other entries in the dict that are in the old commit,
439 Also preserve other entries in the dict that are in the old commit,
440 first graft something so there's an additional entry:
440 first graft something so there's an additional entry:
441
441
442 $ hg up 0 -q
442 $ hg up 0 -q
443 $ echo z > z
443 $ echo z > z
444 $ hg ci -Am 'fork'
444 $ hg ci -Am 'fork'
445 adding z
445 adding z
446 created new head
446 created new head
447 $ hg up 11
447 $ hg up 11
448 5 files updated, 0 files merged, 1 files removed, 0 files unresolved
448 5 files updated, 0 files merged, 1 files removed, 0 files unresolved
449 $ hg graft 12
449 $ hg graft 12
450 grafting revision 12
450 grafting revision 12
451 $ hg ci --amend -m 'graft amend'
451 $ hg ci --amend -m 'graft amend'
452 saved backup bundle to $TESTTMP/.hg/strip-backup/bd010aea3f39-amend-backup.hg (glob)
452 saved backup bundle to $TESTTMP/.hg/strip-backup/bd010aea3f39-amend-backup.hg (glob)
453 $ hg log -r . --debug | grep extra
453 $ hg log -r . --debug | grep extra
454 extra: amend_source=bd010aea3f39f3fb2a2f884b9ccb0471cd77398e
454 extra: amend_source=bd010aea3f39f3fb2a2f884b9ccb0471cd77398e
455 extra: branch=a
455 extra: branch=a
456 extra: source=2647734878ef0236dda712fae9c1651cf694ea8a
456 extra: source=2647734878ef0236dda712fae9c1651cf694ea8a
457
457
458 Preserve phase
458 Preserve phase
459
459
460 $ hg phase '.^::.'
460 $ hg phase '.^::.'
461 11: draft
461 11: draft
462 13: draft
462 13: draft
463 $ hg phase --secret --force .
463 $ hg phase --secret --force .
464 $ hg phase '.^::.'
464 $ hg phase '.^::.'
465 11: draft
465 11: draft
466 13: secret
466 13: secret
467 $ hg commit --amend -m 'amend for phase' -q
467 $ hg commit --amend -m 'amend for phase' -q
468 $ hg phase '.^::.'
468 $ hg phase '.^::.'
469 11: draft
469 11: draft
470 13: secret
470 13: secret
471
471
472 Test amend with obsolete
472 Test amend with obsolete
473 ---------------------------
473 ---------------------------
474
474
475 Enable obsolete
475 Enable obsolete
476
476
477 $ cat >> $HGRCPATH << EOF
477 $ cat >> $HGRCPATH << EOF
478 > [experimental]
478 > [experimental]
479 > evolution=createmarkers,allowunstable
479 > evolution=createmarkers,allowunstable
480 > EOF
480 > EOF
481
481
482 Amend with no files changes
482 Amend with no files changes
483
483
484 $ hg id -n
484 $ hg id -n
485 13
485 13
486 $ hg ci --amend -m 'babar'
486 $ hg ci --amend -m 'babar'
487 $ hg id -n
487 $ hg id -n
488 14
488 14
489 $ hg log -Gl 3 --style=compact
489 $ hg log -Gl 3 --style=compact
490 @ 14[tip]:11 b650e6ee8614 1970-01-01 00:00 +0000 test
490 @ 14[tip]:11 b650e6ee8614 1970-01-01 00:00 +0000 test
491 | babar
491 | babar
492 |
492 |
493 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
493 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
494 | | fork
494 | | fork
495 | |
495 | |
496 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
496 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
497 | | a''
497 | | a''
498 | |
498 | |
499 $ hg log -Gl 4 --hidden --style=compact
499 $ hg log -Gl 4 --hidden --style=compact
500 @ 14[tip]:11 b650e6ee8614 1970-01-01 00:00 +0000 test
500 @ 14[tip]:11 b650e6ee8614 1970-01-01 00:00 +0000 test
501 | babar
501 | babar
502 |
502 |
503 | x 13:11 68ff8ff97044 1970-01-01 00:00 +0000 test
503 | x 13:11 68ff8ff97044 1970-01-01 00:00 +0000 test
504 |/ amend for phase
504 |/ amend for phase
505 |
505 |
506 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
506 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
507 | | fork
507 | | fork
508 | |
508 | |
509 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
509 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
510 | | a''
510 | | a''
511 | |
511 | |
512
512
513 Amend with files changes
513 Amend with files changes
514
514
515 (note: the extra commit over 15 is a temporary junk I would be happy to get
515 (note: the extra commit over 15 is a temporary junk I would be happy to get
516 ride of)
516 ride of)
517
517
518 $ echo 'babar' >> a
518 $ echo 'babar' >> a
519 $ hg commit --amend
519 $ hg commit --amend
520 $ hg log -Gl 6 --hidden --style=compact
520 $ hg log -Gl 6 --hidden --style=compact
521 @ 16[tip]:11 9f9e9bccf56c 1970-01-01 00:00 +0000 test
521 @ 16[tip]:11 9f9e9bccf56c 1970-01-01 00:00 +0000 test
522 | babar
522 | babar
523 |
523 |
524 | x 15 90fef497c56f 1970-01-01 00:00 +0000 test
524 | x 15 90fef497c56f 1970-01-01 00:00 +0000 test
525 | | temporary amend commit for b650e6ee8614
525 | | temporary amend commit for b650e6ee8614
526 | |
526 | |
527 | x 14:11 b650e6ee8614 1970-01-01 00:00 +0000 test
527 | x 14:11 b650e6ee8614 1970-01-01 00:00 +0000 test
528 |/ babar
528 |/ babar
529 |
529 |
530 | x 13:11 68ff8ff97044 1970-01-01 00:00 +0000 test
530 | x 13:11 68ff8ff97044 1970-01-01 00:00 +0000 test
531 |/ amend for phase
531 |/ amend for phase
532 |
532 |
533 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
533 | o 12:0 2647734878ef 1970-01-01 00:00 +0000 test
534 | | fork
534 | | fork
535 | |
535 | |
536 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
536 o | 11 3334b7925910 1970-01-01 00:00 +0000 test
537 | | a''
537 | | a''
538 | |
538 | |
539
539
540
540
541 Test that amend does not make it easy to create obsolescence cycle
541 Test that amend does not make it easy to create obsolescence cycle
542 ---------------------------------------------------------------------
542 ---------------------------------------------------------------------
543
543
544 $ hg id -r 14 --hidden
544 $ hg id -r 14 --hidden
545 b650e6ee8614 (a)
545 b650e6ee8614 (a)
546 $ hg revert -ar 14 --hidden
546 $ hg revert -ar 14 --hidden
547 reverting a
547 reverting a
548 $ hg commit --amend
548 $ hg commit --amend
549 $ hg id
549 $ hg id
550 b99e5df575f7 (a) tip
550 b99e5df575f7 (a) tip
551
551
552 Test that rewriting leaving instability behind is allowed
552 Test that rewriting leaving instability behind is allowed
553 ---------------------------------------------------------------------
553 ---------------------------------------------------------------------
554
554
555 $ hg up '.^'
555 $ hg up '.^'
556 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
556 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
557 $ echo 'b' >> a
557 $ echo 'b' >> a
558 $ hg log --style compact -r 'children(.)'
558 $ hg log --style compact -r 'children(.)'
559 18[tip]:11 b99e5df575f7 1970-01-01 00:00 +0000 test
559 18[tip]:11 b99e5df575f7 1970-01-01 00:00 +0000 test
560 babar
560 babar
561
561
562 $ hg commit --amend
562 $ hg commit --amend
563 $ hg log -r 'unstable()'
563 $ hg log -r 'unstable()'
564 changeset: 18:b99e5df575f7
564 changeset: 18:b99e5df575f7
565 branch: a
565 branch: a
566 parent: 11:3334b7925910
566 parent: 11:3334b7925910
567 user: test
567 user: test
568 date: Thu Jan 01 00:00:00 1970 +0000
568 date: Thu Jan 01 00:00:00 1970 +0000
569 summary: babar
569 summary: babar
570
570
571
571
572 Amend a merge changeset (with renames and conflicts from the second parent):
572 Amend a merge changeset (with renames and conflicts from the second parent):
573
573
574 $ hg up -q default
574 $ hg up -q default
575 $ hg branch -q bar
575 $ hg branch -q bar
576 $ hg cp a aa
576 $ hg cp a aa
577 $ hg mv z zz
577 $ hg mv z zz
578 $ echo cc > cc
578 $ echo cc > cc
579 $ hg add cc
579 $ hg add cc
580 $ hg ci -m aazzcc
580 $ hg ci -m aazzcc
581 $ hg up -q default
581 $ hg up -q default
582 $ echo a >> a
582 $ echo a >> a
583 $ echo dd > cc
583 $ echo dd > cc
584 $ hg add cc
584 $ hg add cc
585 $ hg ci -m aa
585 $ hg ci -m aa
586 $ hg merge -q bar
586 $ hg merge -q bar
587 warning: conflicts during merge.
587 warning: conflicts during merge.
588 merging cc incomplete! (edit conflicts, then use 'hg resolve --mark')
588 merging cc incomplete! (edit conflicts, then use 'hg resolve --mark')
589 [1]
589 [1]
590 $ hg resolve -m cc
590 $ hg resolve -m cc
591 (no more unresolved files)
591 (no more unresolved files)
592 $ hg ci -m 'merge bar'
592 $ hg ci -m 'merge bar'
593 $ hg log --config diff.git=1 -pr .
593 $ hg log --config diff.git=1 -pr .
594 changeset: 23:93cd4445f720
594 changeset: 23:93cd4445f720
595 tag: tip
595 tag: tip
596 parent: 22:30d96aeaf27b
596 parent: 22:30d96aeaf27b
597 parent: 21:1aa437659d19
597 parent: 21:1aa437659d19
598 user: test
598 user: test
599 date: Thu Jan 01 00:00:00 1970 +0000
599 date: Thu Jan 01 00:00:00 1970 +0000
600 summary: merge bar
600 summary: merge bar
601
601
602 diff --git a/a b/aa
602 diff --git a/a b/aa
603 copy from a
603 copy from a
604 copy to aa
604 copy to aa
605 diff --git a/cc b/cc
605 diff --git a/cc b/cc
606 --- a/cc
606 --- a/cc
607 +++ b/cc
607 +++ b/cc
608 @@ -1,1 +1,5 @@
608 @@ -1,1 +1,5 @@
609 +<<<<<<< local: 30d96aeaf27b - test: aa
609 +<<<<<<< local: 30d96aeaf27b - test: aa
610 dd
610 dd
611 +=======
611 +=======
612 +cc
612 +cc
613 +>>>>>>> other: 1aa437659d19 bar - test: aazzcc
613 +>>>>>>> other: 1aa437659d19 bar - test: aazzcc
614 diff --git a/z b/zz
614 diff --git a/z b/zz
615 rename from z
615 rename from z
616 rename to zz
616 rename to zz
617
617
618 $ hg debugrename aa
618 $ hg debugrename aa
619 aa renamed from a:a80d06849b333b8a3d5c445f8ba3142010dcdc9e
619 aa renamed from a:a80d06849b333b8a3d5c445f8ba3142010dcdc9e
620 $ hg debugrename zz
620 $ hg debugrename zz
621 zz renamed from z:69a1b67522704ec122181c0890bd16e9d3e7516a
621 zz renamed from z:69a1b67522704ec122181c0890bd16e9d3e7516a
622 $ hg debugrename cc
622 $ hg debugrename cc
623 cc not renamed
623 cc not renamed
624 $ HGEDITOR="sh .hg/checkeditform.sh" hg ci --amend -m 'merge bar (amend message)' --edit
624 $ HGEDITOR="sh .hg/checkeditform.sh" hg ci --amend -m 'merge bar (amend message)' --edit
625 HGEDITFORM=commit.amend.merge
625 HGEDITFORM=commit.amend.merge
626 $ hg log --config diff.git=1 -pr .
626 $ hg log --config diff.git=1 -pr .
627 changeset: 24:832b50f2c271
627 changeset: 24:832b50f2c271
628 tag: tip
628 tag: tip
629 parent: 22:30d96aeaf27b
629 parent: 22:30d96aeaf27b
630 parent: 21:1aa437659d19
630 parent: 21:1aa437659d19
631 user: test
631 user: test
632 date: Thu Jan 01 00:00:00 1970 +0000
632 date: Thu Jan 01 00:00:00 1970 +0000
633 summary: merge bar (amend message)
633 summary: merge bar (amend message)
634
634
635 diff --git a/a b/aa
635 diff --git a/a b/aa
636 copy from a
636 copy from a
637 copy to aa
637 copy to aa
638 diff --git a/cc b/cc
638 diff --git a/cc b/cc
639 --- a/cc
639 --- a/cc
640 +++ b/cc
640 +++ b/cc
641 @@ -1,1 +1,5 @@
641 @@ -1,1 +1,5 @@
642 +<<<<<<< local: 30d96aeaf27b - test: aa
642 +<<<<<<< local: 30d96aeaf27b - test: aa
643 dd
643 dd
644 +=======
644 +=======
645 +cc
645 +cc
646 +>>>>>>> other: 1aa437659d19 bar - test: aazzcc
646 +>>>>>>> other: 1aa437659d19 bar - test: aazzcc
647 diff --git a/z b/zz
647 diff --git a/z b/zz
648 rename from z
648 rename from z
649 rename to zz
649 rename to zz
650
650
651 $ hg debugrename aa
651 $ hg debugrename aa
652 aa renamed from a:a80d06849b333b8a3d5c445f8ba3142010dcdc9e
652 aa renamed from a:a80d06849b333b8a3d5c445f8ba3142010dcdc9e
653 $ hg debugrename zz
653 $ hg debugrename zz
654 zz renamed from z:69a1b67522704ec122181c0890bd16e9d3e7516a
654 zz renamed from z:69a1b67522704ec122181c0890bd16e9d3e7516a
655 $ hg debugrename cc
655 $ hg debugrename cc
656 cc not renamed
656 cc not renamed
657 $ hg mv zz z
657 $ hg mv zz z
658 $ hg ci --amend -m 'merge bar (undo rename)'
658 $ hg ci --amend -m 'merge bar (undo rename)'
659 $ hg log --config diff.git=1 -pr .
659 $ hg log --config diff.git=1 -pr .
660 changeset: 26:bdafc5c72f74
660 changeset: 26:bdafc5c72f74
661 tag: tip
661 tag: tip
662 parent: 22:30d96aeaf27b
662 parent: 22:30d96aeaf27b
663 parent: 21:1aa437659d19
663 parent: 21:1aa437659d19
664 user: test
664 user: test
665 date: Thu Jan 01 00:00:00 1970 +0000
665 date: Thu Jan 01 00:00:00 1970 +0000
666 summary: merge bar (undo rename)
666 summary: merge bar (undo rename)
667
667
668 diff --git a/a b/aa
668 diff --git a/a b/aa
669 copy from a
669 copy from a
670 copy to aa
670 copy to aa
671 diff --git a/cc b/cc
671 diff --git a/cc b/cc
672 --- a/cc
672 --- a/cc
673 +++ b/cc
673 +++ b/cc
674 @@ -1,1 +1,5 @@
674 @@ -1,1 +1,5 @@
675 +<<<<<<< local: 30d96aeaf27b - test: aa
675 +<<<<<<< local: 30d96aeaf27b - test: aa
676 dd
676 dd
677 +=======
677 +=======
678 +cc
678 +cc
679 +>>>>>>> other: 1aa437659d19 bar - test: aazzcc
679 +>>>>>>> other: 1aa437659d19 bar - test: aazzcc
680
680
681 $ hg debugrename z
681 $ hg debugrename z
682 z not renamed
682 z not renamed
683
683
684 Amend a merge changeset (with renames during the merge):
684 Amend a merge changeset (with renames during the merge):
685
685
686 $ hg up -q bar
686 $ hg up -q bar
687 $ echo x > x
687 $ echo x > x
688 $ hg add x
688 $ hg add x
689 $ hg ci -m x
689 $ hg ci -m x
690 $ hg up -q default
690 $ hg up -q default
691 $ hg merge -q bar
691 $ hg merge -q bar
692 $ hg mv aa aaa
692 $ hg mv aa aaa
693 $ echo aa >> aaa
693 $ echo aa >> aaa
694 $ hg ci -m 'merge bar again'
694 $ hg ci -m 'merge bar again'
695 $ hg log --config diff.git=1 -pr .
695 $ hg log --config diff.git=1 -pr .
696 changeset: 28:32f19415b634
696 changeset: 28:32f19415b634
697 tag: tip
697 tag: tip
698 parent: 26:bdafc5c72f74
698 parent: 26:bdafc5c72f74
699 parent: 27:4c94d5bc65f5
699 parent: 27:4c94d5bc65f5
700 user: test
700 user: test
701 date: Thu Jan 01 00:00:00 1970 +0000
701 date: Thu Jan 01 00:00:00 1970 +0000
702 summary: merge bar again
702 summary: merge bar again
703
703
704 diff --git a/aa b/aa
704 diff --git a/aa b/aa
705 deleted file mode 100644
705 deleted file mode 100644
706 --- a/aa
706 --- a/aa
707 +++ /dev/null
707 +++ /dev/null
708 @@ -1,2 +0,0 @@
708 @@ -1,2 +0,0 @@
709 -a
709 -a
710 -a
710 -a
711 diff --git a/aaa b/aaa
711 diff --git a/aaa b/aaa
712 new file mode 100644
712 new file mode 100644
713 --- /dev/null
713 --- /dev/null
714 +++ b/aaa
714 +++ b/aaa
715 @@ -0,0 +1,3 @@
715 @@ -0,0 +1,3 @@
716 +a
716 +a
717 +a
717 +a
718 +aa
718 +aa
719 diff --git a/x b/x
719 diff --git a/x b/x
720 new file mode 100644
720 new file mode 100644
721 --- /dev/null
721 --- /dev/null
722 +++ b/x
722 +++ b/x
723 @@ -0,0 +1,1 @@
723 @@ -0,0 +1,1 @@
724 +x
724 +x
725
725
726 $ hg debugrename aaa
726 $ hg debugrename aaa
727 aaa renamed from aa:37d9b5d994eab34eda9c16b195ace52c7b129980
727 aaa renamed from aa:37d9b5d994eab34eda9c16b195ace52c7b129980
728 $ hg mv aaa aa
728 $ hg mv aaa aa
729 $ hg ci --amend -m 'merge bar again (undo rename)'
729 $ hg ci --amend -m 'merge bar again (undo rename)'
730 $ hg log --config diff.git=1 -pr .
730 $ hg log --config diff.git=1 -pr .
731 changeset: 30:1e2a06b3d312
731 changeset: 30:1e2a06b3d312
732 tag: tip
732 tag: tip
733 parent: 26:bdafc5c72f74
733 parent: 26:bdafc5c72f74
734 parent: 27:4c94d5bc65f5
734 parent: 27:4c94d5bc65f5
735 user: test
735 user: test
736 date: Thu Jan 01 00:00:00 1970 +0000
736 date: Thu Jan 01 00:00:00 1970 +0000
737 summary: merge bar again (undo rename)
737 summary: merge bar again (undo rename)
738
738
739 diff --git a/aa b/aa
739 diff --git a/aa b/aa
740 --- a/aa
740 --- a/aa
741 +++ b/aa
741 +++ b/aa
742 @@ -1,2 +1,3 @@
742 @@ -1,2 +1,3 @@
743 a
743 a
744 a
744 a
745 +aa
745 +aa
746 diff --git a/x b/x
746 diff --git a/x b/x
747 new file mode 100644
747 new file mode 100644
748 --- /dev/null
748 --- /dev/null
749 +++ b/x
749 +++ b/x
750 @@ -0,0 +1,1 @@
750 @@ -0,0 +1,1 @@
751 +x
751 +x
752
752
753 $ hg debugrename aa
753 $ hg debugrename aa
754 aa not renamed
754 aa not renamed
755 $ hg debugrename -r '.^' aa
755 $ hg debugrename -r '.^' aa
756 aa renamed from a:a80d06849b333b8a3d5c445f8ba3142010dcdc9e
756 aa renamed from a:a80d06849b333b8a3d5c445f8ba3142010dcdc9e
757
757
758 Amend a merge changeset (with manifest-level conflicts):
758 Amend a merge changeset (with manifest-level conflicts):
759
759
760 $ hg up -q bar
760 $ hg up -q bar
761 $ hg rm aa
761 $ hg rm aa
762 $ hg ci -m 'rm aa'
762 $ hg ci -m 'rm aa'
763 $ hg up -q default
763 $ hg up -q default
764 $ echo aa >> aa
764 $ echo aa >> aa
765 $ hg ci -m aa
765 $ hg ci -m aa
766 $ hg merge -q bar
766 $ hg merge -q bar
767 local changed aa which remote deleted
767 local changed aa which remote deleted
768 use (c)hanged version or (d)elete? c
768 use (c)hanged version or (d)elete? c
769 $ hg ci -m 'merge bar (with conflicts)'
769 $ hg ci -m 'merge bar (with conflicts)'
770 $ hg log --config diff.git=1 -pr .
770 $ hg log --config diff.git=1 -pr .
771 changeset: 33:97a298b0c59f
771 changeset: 33:97a298b0c59f
772 tag: tip
772 tag: tip
773 parent: 32:3d78ce4226b8
773 parent: 32:3d78ce4226b8
774 parent: 31:67db8847a540
774 parent: 31:67db8847a540
775 user: test
775 user: test
776 date: Thu Jan 01 00:00:00 1970 +0000
776 date: Thu Jan 01 00:00:00 1970 +0000
777 summary: merge bar (with conflicts)
777 summary: merge bar (with conflicts)
778
778
779
779
780 $ hg rm aa
780 $ hg rm aa
781 $ hg ci --amend -m 'merge bar (with conflicts, amended)'
781 $ hg ci --amend -m 'merge bar (with conflicts, amended)'
782 $ hg log --config diff.git=1 -pr .
782 $ hg log --config diff.git=1 -pr .
783 changeset: 35:6de0c1bde1c8
783 changeset: 35:6de0c1bde1c8
784 tag: tip
784 tag: tip
785 parent: 32:3d78ce4226b8
785 parent: 32:3d78ce4226b8
786 parent: 31:67db8847a540
786 parent: 31:67db8847a540
787 user: test
787 user: test
788 date: Thu Jan 01 00:00:00 1970 +0000
788 date: Thu Jan 01 00:00:00 1970 +0000
789 summary: merge bar (with conflicts, amended)
789 summary: merge bar (with conflicts, amended)
790
790
791 diff --git a/aa b/aa
791 diff --git a/aa b/aa
792 deleted file mode 100644
792 deleted file mode 100644
793 --- a/aa
793 --- a/aa
794 +++ /dev/null
794 +++ /dev/null
795 @@ -1,4 +0,0 @@
795 @@ -1,4 +0,0 @@
796 -a
796 -a
797 -a
797 -a
798 -aa
798 -aa
799 -aa
799 -aa
800
800
801 Issue 3445: amending with --close-branch a commit that created a new head should fail
801 Issue 3445: amending with --close-branch a commit that created a new head should fail
802 This shouldn't be possible:
802 This shouldn't be possible:
803
803
804 $ hg up -q default
804 $ hg up -q default
805 $ hg branch closewithamend
805 $ hg branch closewithamend
806 marked working directory as branch closewithamend
806 marked working directory as branch closewithamend
807 (branches are permanent and global, did you want a bookmark?)
807 (branches are permanent and global, did you want a bookmark?)
808 $ touch foo
808 $ touch foo
809 $ hg add foo
809 $ hg add foo
810 $ hg ci -m..
810 $ hg ci -m..
811 $ hg ci --amend --close-branch -m 'closing'
811 $ hg ci --amend --close-branch -m 'closing'
812 abort: can only close branch heads
812 abort: can only close branch heads
813 [255]
813 [255]
814
814
815 This silliness fails:
815 This silliness fails:
816
816
817 $ hg branch silliness
817 $ hg branch silliness
818 marked working directory as branch silliness
818 marked working directory as branch silliness
819 (branches are permanent and global, did you want a bookmark?)
819 (branches are permanent and global, did you want a bookmark?)
820 $ echo b >> b
820 $ echo b >> b
821 $ hg ci --close-branch -m'open and close'
821 $ hg ci --close-branch -m'open and close'
822 abort: can only close branch heads
822 abort: can only close branch heads
823 [255]
823 [255]
824
824
825 Test that amend with --secret creates new secret changeset forcibly
825 Test that amend with --secret creates new secret changeset forcibly
826 ---------------------------------------------------------------------
826 ---------------------------------------------------------------------
827
827
828 $ hg phase '.^::.'
828 $ hg phase '.^::.'
829 35: draft
829 35: draft
830 36: draft
830 36: draft
831 $ hg commit --amend --secret -m 'amend as secret' -q
831 $ hg commit --amend --secret -m 'amend as secret' -q
832 $ hg phase '.^::.'
832 $ hg phase '.^::.'
833 35: draft
833 35: draft
834 38: secret
834 38: secret
835
835
836 Test that amend with --edit invokes editor forcibly
836 Test that amend with --edit invokes editor forcibly
837 ---------------------------------------------------
837 ---------------------------------------------------
838
838
839 $ hg parents --template "{desc}\n"
839 $ hg parents --template "{desc}\n"
840 amend as secret
840 amend as secret
841 $ HGEDITOR=cat hg commit --amend -m "editor should be suppressed"
841 $ HGEDITOR=cat hg commit --amend -m "editor should be suppressed"
842 $ hg parents --template "{desc}\n"
842 $ hg parents --template "{desc}\n"
843 editor should be suppressed
843 editor should be suppressed
844
844
845 $ HGEDITOR=cat hg commit --amend -m "editor should be invoked" --edit
845 $ HGEDITOR=cat hg commit --amend -m "editor should be invoked" --edit
846 editor should be invoked
846 editor should be invoked
847
847
848
848
849 HG: Enter commit message. Lines beginning with 'HG:' are removed.
849 HG: Enter commit message. Lines beginning with 'HG:' are removed.
850 HG: Leave message empty to abort commit.
850 HG: Leave message empty to abort commit.
851 HG: --
851 HG: --
852 HG: user: test
852 HG: user: test
853 HG: branch 'silliness'
853 HG: branch 'silliness'
854 HG: changed foo
854 HG: changed foo
855 $ hg parents --template "{desc}\n"
855 $ hg parents --template "{desc}\n"
856 editor should be invoked
856 editor should be invoked
857
858 Check for issue4405
859 -------------------
860
861 Setup the repo with a file that gets moved in a second commit.
862 $ hg init repo
863 $ cd repo
864 $ touch a0
865 $ hg add a0
866 $ hg commit -m a0
867 $ hg mv a0 a1
868 $ hg commit -m a1
869 $ hg up -q 0
870 $ hg log -G --template '{rev} {desc}'
871 o 1 a1
872 |
873 @ 0 a0
874
875
876 Now we branch the repro, but re-use the file contents, so we have a divergence
877 in the file revlog topology and the changelog topology.
878 $ hg revert --rev 1 --all
879 removing a0
880 adding a1
881 $ hg ci -qm 'a1-amend'
882 $ hg log -G --template '{rev} {desc}'
883 @ 2 a1-amend
884 |
885 | o 1 a1
886 |/
887 o 0 a0
888
889
890 The way mercurial does amends is to create a temporary commit (rev 3) and then
891 fold the new and old commits together into another commit (rev 4). During this
892 process, findlimit is called to check how far back to look for the transitive
893 closure of file copy information, but due to the divergence of the filelog
894 and changelog graph topologies, before findlimit was fixed, it returned a rev
895 which was not far enough back in this case.
896 $ hg mv a1 a2
897 $ hg status --copies --rev 0
898 A a2
899 a0
900 R a0
901 $ hg ci --amend -q
902 $ hg log -G --template '{rev} {desc}'
903 @ 4 a1-amend
904 |
905 | o 1 a1
906 |/
907 o 0 a0
908
909
910 Before the fix, the copy information was lost.
911 $ hg status --copies --rev 0
912 A a2
913 a0
914 R a0
General Comments 0
You need to be logged in to leave comments. Login now