##// END OF EJS Templates
simplemerge: use contexts to read file data from, if passed...
Phil Cohen -
r33827:8b91a4ff default
parent child Browse files
Show More
@@ -1,473 +1,491 b''
1 # Copyright (C) 2004, 2005 Canonical Ltd
1 # Copyright (C) 2004, 2005 Canonical Ltd
2 #
2 #
3 # This program is free software; you can redistribute it and/or modify
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
6 # (at your option) any later version.
7 #
7 #
8 # This program is distributed in the hope that it will be useful,
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
11 # GNU General Public License for more details.
12 #
12 #
13 # You should have received a copy of the GNU General Public License
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 # along with this program; if not, see <http://www.gnu.org/licenses/>.
15
15
16 # mbp: "you know that thing where cvs gives you conflict markers?"
16 # mbp: "you know that thing where cvs gives you conflict markers?"
17 # s: "i hate that."
17 # s: "i hate that."
18
18
19 from __future__ import absolute_import
19 from __future__ import absolute_import
20
20
21 import os
21 import os
22
22
23 from .i18n import _
23 from .i18n import _
24 from . import (
24 from . import (
25 error,
25 error,
26 mdiff,
26 mdiff,
27 pycompat,
27 pycompat,
28 util,
28 util,
29 vfs as vfsmod,
29 vfs as vfsmod,
30 )
30 )
31
31
32 class CantReprocessAndShowBase(Exception):
32 class CantReprocessAndShowBase(Exception):
33 pass
33 pass
34
34
35 def intersect(ra, rb):
35 def intersect(ra, rb):
36 """Given two ranges return the range where they intersect or None.
36 """Given two ranges return the range where they intersect or None.
37
37
38 >>> intersect((0, 10), (0, 6))
38 >>> intersect((0, 10), (0, 6))
39 (0, 6)
39 (0, 6)
40 >>> intersect((0, 10), (5, 15))
40 >>> intersect((0, 10), (5, 15))
41 (5, 10)
41 (5, 10)
42 >>> intersect((0, 10), (10, 15))
42 >>> intersect((0, 10), (10, 15))
43 >>> intersect((0, 9), (10, 15))
43 >>> intersect((0, 9), (10, 15))
44 >>> intersect((0, 9), (7, 15))
44 >>> intersect((0, 9), (7, 15))
45 (7, 9)
45 (7, 9)
46 """
46 """
47 assert ra[0] <= ra[1]
47 assert ra[0] <= ra[1]
48 assert rb[0] <= rb[1]
48 assert rb[0] <= rb[1]
49
49
50 sa = max(ra[0], rb[0])
50 sa = max(ra[0], rb[0])
51 sb = min(ra[1], rb[1])
51 sb = min(ra[1], rb[1])
52 if sa < sb:
52 if sa < sb:
53 return sa, sb
53 return sa, sb
54 else:
54 else:
55 return None
55 return None
56
56
57 def compare_range(a, astart, aend, b, bstart, bend):
57 def compare_range(a, astart, aend, b, bstart, bend):
58 """Compare a[astart:aend] == b[bstart:bend], without slicing.
58 """Compare a[astart:aend] == b[bstart:bend], without slicing.
59 """
59 """
60 if (aend - astart) != (bend - bstart):
60 if (aend - astart) != (bend - bstart):
61 return False
61 return False
62 for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
62 for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
63 if a[ia] != b[ib]:
63 if a[ia] != b[ib]:
64 return False
64 return False
65 else:
65 else:
66 return True
66 return True
67
67
68 class Merge3Text(object):
68 class Merge3Text(object):
69 """3-way merge of texts.
69 """3-way merge of texts.
70
70
71 Given strings BASE, OTHER, THIS, tries to produce a combined text
71 Given strings BASE, OTHER, THIS, tries to produce a combined text
72 incorporating the changes from both BASE->OTHER and BASE->THIS."""
72 incorporating the changes from both BASE->OTHER and BASE->THIS."""
73 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
73 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
74 self.basetext = basetext
74 self.basetext = basetext
75 self.atext = atext
75 self.atext = atext
76 self.btext = btext
76 self.btext = btext
77 if base is None:
77 if base is None:
78 base = mdiff.splitnewlines(basetext)
78 base = mdiff.splitnewlines(basetext)
79 if a is None:
79 if a is None:
80 a = mdiff.splitnewlines(atext)
80 a = mdiff.splitnewlines(atext)
81 if b is None:
81 if b is None:
82 b = mdiff.splitnewlines(btext)
82 b = mdiff.splitnewlines(btext)
83 self.base = base
83 self.base = base
84 self.a = a
84 self.a = a
85 self.b = b
85 self.b = b
86
86
87 def merge_lines(self,
87 def merge_lines(self,
88 name_a=None,
88 name_a=None,
89 name_b=None,
89 name_b=None,
90 name_base=None,
90 name_base=None,
91 start_marker='<<<<<<<',
91 start_marker='<<<<<<<',
92 mid_marker='=======',
92 mid_marker='=======',
93 end_marker='>>>>>>>',
93 end_marker='>>>>>>>',
94 base_marker=None,
94 base_marker=None,
95 localorother=None,
95 localorother=None,
96 minimize=False):
96 minimize=False):
97 """Return merge in cvs-like form.
97 """Return merge in cvs-like form.
98 """
98 """
99 self.conflicts = False
99 self.conflicts = False
100 newline = '\n'
100 newline = '\n'
101 if len(self.a) > 0:
101 if len(self.a) > 0:
102 if self.a[0].endswith('\r\n'):
102 if self.a[0].endswith('\r\n'):
103 newline = '\r\n'
103 newline = '\r\n'
104 elif self.a[0].endswith('\r'):
104 elif self.a[0].endswith('\r'):
105 newline = '\r'
105 newline = '\r'
106 if name_a and start_marker:
106 if name_a and start_marker:
107 start_marker = start_marker + ' ' + name_a
107 start_marker = start_marker + ' ' + name_a
108 if name_b and end_marker:
108 if name_b and end_marker:
109 end_marker = end_marker + ' ' + name_b
109 end_marker = end_marker + ' ' + name_b
110 if name_base and base_marker:
110 if name_base and base_marker:
111 base_marker = base_marker + ' ' + name_base
111 base_marker = base_marker + ' ' + name_base
112 merge_regions = self.merge_regions()
112 merge_regions = self.merge_regions()
113 if minimize:
113 if minimize:
114 merge_regions = self.minimize(merge_regions)
114 merge_regions = self.minimize(merge_regions)
115 for t in merge_regions:
115 for t in merge_regions:
116 what = t[0]
116 what = t[0]
117 if what == 'unchanged':
117 if what == 'unchanged':
118 for i in range(t[1], t[2]):
118 for i in range(t[1], t[2]):
119 yield self.base[i]
119 yield self.base[i]
120 elif what == 'a' or what == 'same':
120 elif what == 'a' or what == 'same':
121 for i in range(t[1], t[2]):
121 for i in range(t[1], t[2]):
122 yield self.a[i]
122 yield self.a[i]
123 elif what == 'b':
123 elif what == 'b':
124 for i in range(t[1], t[2]):
124 for i in range(t[1], t[2]):
125 yield self.b[i]
125 yield self.b[i]
126 elif what == 'conflict':
126 elif what == 'conflict':
127 if localorother == 'local':
127 if localorother == 'local':
128 for i in range(t[3], t[4]):
128 for i in range(t[3], t[4]):
129 yield self.a[i]
129 yield self.a[i]
130 elif localorother == 'other':
130 elif localorother == 'other':
131 for i in range(t[5], t[6]):
131 for i in range(t[5], t[6]):
132 yield self.b[i]
132 yield self.b[i]
133 else:
133 else:
134 self.conflicts = True
134 self.conflicts = True
135 if start_marker is not None:
135 if start_marker is not None:
136 yield start_marker + newline
136 yield start_marker + newline
137 for i in range(t[3], t[4]):
137 for i in range(t[3], t[4]):
138 yield self.a[i]
138 yield self.a[i]
139 if base_marker is not None:
139 if base_marker is not None:
140 yield base_marker + newline
140 yield base_marker + newline
141 for i in range(t[1], t[2]):
141 for i in range(t[1], t[2]):
142 yield self.base[i]
142 yield self.base[i]
143 if mid_marker is not None:
143 if mid_marker is not None:
144 yield mid_marker + newline
144 yield mid_marker + newline
145 for i in range(t[5], t[6]):
145 for i in range(t[5], t[6]):
146 yield self.b[i]
146 yield self.b[i]
147 if end_marker is not None:
147 if end_marker is not None:
148 yield end_marker + newline
148 yield end_marker + newline
149 else:
149 else:
150 raise ValueError(what)
150 raise ValueError(what)
151
151
152 def merge_groups(self):
152 def merge_groups(self):
153 """Yield sequence of line groups. Each one is a tuple:
153 """Yield sequence of line groups. Each one is a tuple:
154
154
155 'unchanged', lines
155 'unchanged', lines
156 Lines unchanged from base
156 Lines unchanged from base
157
157
158 'a', lines
158 'a', lines
159 Lines taken from a
159 Lines taken from a
160
160
161 'same', lines
161 'same', lines
162 Lines taken from a (and equal to b)
162 Lines taken from a (and equal to b)
163
163
164 'b', lines
164 'b', lines
165 Lines taken from b
165 Lines taken from b
166
166
167 'conflict', base_lines, a_lines, b_lines
167 'conflict', base_lines, a_lines, b_lines
168 Lines from base were changed to either a or b and conflict.
168 Lines from base were changed to either a or b and conflict.
169 """
169 """
170 for t in self.merge_regions():
170 for t in self.merge_regions():
171 what = t[0]
171 what = t[0]
172 if what == 'unchanged':
172 if what == 'unchanged':
173 yield what, self.base[t[1]:t[2]]
173 yield what, self.base[t[1]:t[2]]
174 elif what == 'a' or what == 'same':
174 elif what == 'a' or what == 'same':
175 yield what, self.a[t[1]:t[2]]
175 yield what, self.a[t[1]:t[2]]
176 elif what == 'b':
176 elif what == 'b':
177 yield what, self.b[t[1]:t[2]]
177 yield what, self.b[t[1]:t[2]]
178 elif what == 'conflict':
178 elif what == 'conflict':
179 yield (what,
179 yield (what,
180 self.base[t[1]:t[2]],
180 self.base[t[1]:t[2]],
181 self.a[t[3]:t[4]],
181 self.a[t[3]:t[4]],
182 self.b[t[5]:t[6]])
182 self.b[t[5]:t[6]])
183 else:
183 else:
184 raise ValueError(what)
184 raise ValueError(what)
185
185
186 def merge_regions(self):
186 def merge_regions(self):
187 """Return sequences of matching and conflicting regions.
187 """Return sequences of matching and conflicting regions.
188
188
189 This returns tuples, where the first value says what kind we
189 This returns tuples, where the first value says what kind we
190 have:
190 have:
191
191
192 'unchanged', start, end
192 'unchanged', start, end
193 Take a region of base[start:end]
193 Take a region of base[start:end]
194
194
195 'same', astart, aend
195 'same', astart, aend
196 b and a are different from base but give the same result
196 b and a are different from base but give the same result
197
197
198 'a', start, end
198 'a', start, end
199 Non-clashing insertion from a[start:end]
199 Non-clashing insertion from a[start:end]
200
200
201 'conflict', zstart, zend, astart, aend, bstart, bend
201 'conflict', zstart, zend, astart, aend, bstart, bend
202 Conflict between a and b, with z as common ancestor
202 Conflict between a and b, with z as common ancestor
203
203
204 Method is as follows:
204 Method is as follows:
205
205
206 The two sequences align only on regions which match the base
206 The two sequences align only on regions which match the base
207 and both descendants. These are found by doing a two-way diff
207 and both descendants. These are found by doing a two-way diff
208 of each one against the base, and then finding the
208 of each one against the base, and then finding the
209 intersections between those regions. These "sync regions"
209 intersections between those regions. These "sync regions"
210 are by definition unchanged in both and easily dealt with.
210 are by definition unchanged in both and easily dealt with.
211
211
212 The regions in between can be in any of three cases:
212 The regions in between can be in any of three cases:
213 conflicted, or changed on only one side.
213 conflicted, or changed on only one side.
214 """
214 """
215
215
216 # section a[0:ia] has been disposed of, etc
216 # section a[0:ia] has been disposed of, etc
217 iz = ia = ib = 0
217 iz = ia = ib = 0
218
218
219 for region in self.find_sync_regions():
219 for region in self.find_sync_regions():
220 zmatch, zend, amatch, aend, bmatch, bend = region
220 zmatch, zend, amatch, aend, bmatch, bend = region
221 #print 'match base [%d:%d]' % (zmatch, zend)
221 #print 'match base [%d:%d]' % (zmatch, zend)
222
222
223 matchlen = zend - zmatch
223 matchlen = zend - zmatch
224 assert matchlen >= 0
224 assert matchlen >= 0
225 assert matchlen == (aend - amatch)
225 assert matchlen == (aend - amatch)
226 assert matchlen == (bend - bmatch)
226 assert matchlen == (bend - bmatch)
227
227
228 len_a = amatch - ia
228 len_a = amatch - ia
229 len_b = bmatch - ib
229 len_b = bmatch - ib
230 len_base = zmatch - iz
230 len_base = zmatch - iz
231 assert len_a >= 0
231 assert len_a >= 0
232 assert len_b >= 0
232 assert len_b >= 0
233 assert len_base >= 0
233 assert len_base >= 0
234
234
235 #print 'unmatched a=%d, b=%d' % (len_a, len_b)
235 #print 'unmatched a=%d, b=%d' % (len_a, len_b)
236
236
237 if len_a or len_b:
237 if len_a or len_b:
238 # try to avoid actually slicing the lists
238 # try to avoid actually slicing the lists
239 equal_a = compare_range(self.a, ia, amatch,
239 equal_a = compare_range(self.a, ia, amatch,
240 self.base, iz, zmatch)
240 self.base, iz, zmatch)
241 equal_b = compare_range(self.b, ib, bmatch,
241 equal_b = compare_range(self.b, ib, bmatch,
242 self.base, iz, zmatch)
242 self.base, iz, zmatch)
243 same = compare_range(self.a, ia, amatch,
243 same = compare_range(self.a, ia, amatch,
244 self.b, ib, bmatch)
244 self.b, ib, bmatch)
245
245
246 if same:
246 if same:
247 yield 'same', ia, amatch
247 yield 'same', ia, amatch
248 elif equal_a and not equal_b:
248 elif equal_a and not equal_b:
249 yield 'b', ib, bmatch
249 yield 'b', ib, bmatch
250 elif equal_b and not equal_a:
250 elif equal_b and not equal_a:
251 yield 'a', ia, amatch
251 yield 'a', ia, amatch
252 elif not equal_a and not equal_b:
252 elif not equal_a and not equal_b:
253 yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
253 yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
254 else:
254 else:
255 raise AssertionError("can't handle a=b=base but unmatched")
255 raise AssertionError("can't handle a=b=base but unmatched")
256
256
257 ia = amatch
257 ia = amatch
258 ib = bmatch
258 ib = bmatch
259 iz = zmatch
259 iz = zmatch
260
260
261 # if the same part of the base was deleted on both sides
261 # if the same part of the base was deleted on both sides
262 # that's OK, we can just skip it.
262 # that's OK, we can just skip it.
263
263
264
264
265 if matchlen > 0:
265 if matchlen > 0:
266 assert ia == amatch
266 assert ia == amatch
267 assert ib == bmatch
267 assert ib == bmatch
268 assert iz == zmatch
268 assert iz == zmatch
269
269
270 yield 'unchanged', zmatch, zend
270 yield 'unchanged', zmatch, zend
271 iz = zend
271 iz = zend
272 ia = aend
272 ia = aend
273 ib = bend
273 ib = bend
274
274
275 def minimize(self, merge_regions):
275 def minimize(self, merge_regions):
276 """Trim conflict regions of lines where A and B sides match.
276 """Trim conflict regions of lines where A and B sides match.
277
277
278 Lines where both A and B have made the same changes at the beginning
278 Lines where both A and B have made the same changes at the beginning
279 or the end of each merge region are eliminated from the conflict
279 or the end of each merge region are eliminated from the conflict
280 region and are instead considered the same.
280 region and are instead considered the same.
281 """
281 """
282 for region in merge_regions:
282 for region in merge_regions:
283 if region[0] != "conflict":
283 if region[0] != "conflict":
284 yield region
284 yield region
285 continue
285 continue
286 issue, z1, z2, a1, a2, b1, b2 = region
286 issue, z1, z2, a1, a2, b1, b2 = region
287 alen = a2 - a1
287 alen = a2 - a1
288 blen = b2 - b1
288 blen = b2 - b1
289
289
290 # find matches at the front
290 # find matches at the front
291 ii = 0
291 ii = 0
292 while ii < alen and ii < blen and \
292 while ii < alen and ii < blen and \
293 self.a[a1 + ii] == self.b[b1 + ii]:
293 self.a[a1 + ii] == self.b[b1 + ii]:
294 ii += 1
294 ii += 1
295 startmatches = ii
295 startmatches = ii
296
296
297 # find matches at the end
297 # find matches at the end
298 ii = 0
298 ii = 0
299 while ii < alen and ii < blen and \
299 while ii < alen and ii < blen and \
300 self.a[a2 - ii - 1] == self.b[b2 - ii - 1]:
300 self.a[a2 - ii - 1] == self.b[b2 - ii - 1]:
301 ii += 1
301 ii += 1
302 endmatches = ii
302 endmatches = ii
303
303
304 if startmatches > 0:
304 if startmatches > 0:
305 yield 'same', a1, a1 + startmatches
305 yield 'same', a1, a1 + startmatches
306
306
307 yield ('conflict', z1, z2,
307 yield ('conflict', z1, z2,
308 a1 + startmatches, a2 - endmatches,
308 a1 + startmatches, a2 - endmatches,
309 b1 + startmatches, b2 - endmatches)
309 b1 + startmatches, b2 - endmatches)
310
310
311 if endmatches > 0:
311 if endmatches > 0:
312 yield 'same', a2 - endmatches, a2
312 yield 'same', a2 - endmatches, a2
313
313
314 def find_sync_regions(self):
314 def find_sync_regions(self):
315 """Return a list of sync regions, where both descendants match the base.
315 """Return a list of sync regions, where both descendants match the base.
316
316
317 Generates a list of (base1, base2, a1, a2, b1, b2). There is
317 Generates a list of (base1, base2, a1, a2, b1, b2). There is
318 always a zero-length sync region at the end of all the files.
318 always a zero-length sync region at the end of all the files.
319 """
319 """
320
320
321 ia = ib = 0
321 ia = ib = 0
322 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
322 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
323 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
323 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
324 len_a = len(amatches)
324 len_a = len(amatches)
325 len_b = len(bmatches)
325 len_b = len(bmatches)
326
326
327 sl = []
327 sl = []
328
328
329 while ia < len_a and ib < len_b:
329 while ia < len_a and ib < len_b:
330 abase, amatch, alen = amatches[ia]
330 abase, amatch, alen = amatches[ia]
331 bbase, bmatch, blen = bmatches[ib]
331 bbase, bmatch, blen = bmatches[ib]
332
332
333 # there is an unconflicted block at i; how long does it
333 # there is an unconflicted block at i; how long does it
334 # extend? until whichever one ends earlier.
334 # extend? until whichever one ends earlier.
335 i = intersect((abase, abase + alen), (bbase, bbase + blen))
335 i = intersect((abase, abase + alen), (bbase, bbase + blen))
336 if i:
336 if i:
337 intbase = i[0]
337 intbase = i[0]
338 intend = i[1]
338 intend = i[1]
339 intlen = intend - intbase
339 intlen = intend - intbase
340
340
341 # found a match of base[i[0], i[1]]; this may be less than
341 # found a match of base[i[0], i[1]]; this may be less than
342 # the region that matches in either one
342 # the region that matches in either one
343 assert intlen <= alen
343 assert intlen <= alen
344 assert intlen <= blen
344 assert intlen <= blen
345 assert abase <= intbase
345 assert abase <= intbase
346 assert bbase <= intbase
346 assert bbase <= intbase
347
347
348 asub = amatch + (intbase - abase)
348 asub = amatch + (intbase - abase)
349 bsub = bmatch + (intbase - bbase)
349 bsub = bmatch + (intbase - bbase)
350 aend = asub + intlen
350 aend = asub + intlen
351 bend = bsub + intlen
351 bend = bsub + intlen
352
352
353 assert self.base[intbase:intend] == self.a[asub:aend], \
353 assert self.base[intbase:intend] == self.a[asub:aend], \
354 (self.base[intbase:intend], self.a[asub:aend])
354 (self.base[intbase:intend], self.a[asub:aend])
355
355
356 assert self.base[intbase:intend] == self.b[bsub:bend]
356 assert self.base[intbase:intend] == self.b[bsub:bend]
357
357
358 sl.append((intbase, intend,
358 sl.append((intbase, intend,
359 asub, aend,
359 asub, aend,
360 bsub, bend))
360 bsub, bend))
361
361
362 # advance whichever one ends first in the base text
362 # advance whichever one ends first in the base text
363 if (abase + alen) < (bbase + blen):
363 if (abase + alen) < (bbase + blen):
364 ia += 1
364 ia += 1
365 else:
365 else:
366 ib += 1
366 ib += 1
367
367
368 intbase = len(self.base)
368 intbase = len(self.base)
369 abase = len(self.a)
369 abase = len(self.a)
370 bbase = len(self.b)
370 bbase = len(self.b)
371 sl.append((intbase, intbase, abase, abase, bbase, bbase))
371 sl.append((intbase, intbase, abase, abase, bbase, bbase))
372
372
373 return sl
373 return sl
374
374
375 def find_unconflicted(self):
375 def find_unconflicted(self):
376 """Return a list of ranges in base that are not conflicted."""
376 """Return a list of ranges in base that are not conflicted."""
377 am = mdiff.get_matching_blocks(self.basetext, self.atext)
377 am = mdiff.get_matching_blocks(self.basetext, self.atext)
378 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
378 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
379
379
380 unc = []
380 unc = []
381
381
382 while am and bm:
382 while am and bm:
383 # there is an unconflicted block at i; how long does it
383 # there is an unconflicted block at i; how long does it
384 # extend? until whichever one ends earlier.
384 # extend? until whichever one ends earlier.
385 a1 = am[0][0]
385 a1 = am[0][0]
386 a2 = a1 + am[0][2]
386 a2 = a1 + am[0][2]
387 b1 = bm[0][0]
387 b1 = bm[0][0]
388 b2 = b1 + bm[0][2]
388 b2 = b1 + bm[0][2]
389 i = intersect((a1, a2), (b1, b2))
389 i = intersect((a1, a2), (b1, b2))
390 if i:
390 if i:
391 unc.append(i)
391 unc.append(i)
392
392
393 if a2 < b2:
393 if a2 < b2:
394 del am[0]
394 del am[0]
395 else:
395 else:
396 del bm[0]
396 del bm[0]
397
397
398 return unc
398 return unc
399
399
400 def _verifytext(text, path, ui, opts):
400 def _verifytext(text, path, ui, opts):
401 """verifies that text is non-binary (unless opts[text] is passed,
401 """verifies that text is non-binary (unless opts[text] is passed,
402 then we just warn)"""
402 then we just warn)"""
403 if util.binary(text):
403 if util.binary(text):
404 msg = _("%s looks like a binary file.") % path
404 msg = _("%s looks like a binary file.") % path
405 if not opts.get('quiet'):
405 if not opts.get('quiet'):
406 ui.warn(_('warning: %s\n') % msg)
406 ui.warn(_('warning: %s\n') % msg)
407 if not opts.get('text'):
407 if not opts.get('text'):
408 raise error.Abort(msg)
408 raise error.Abort(msg)
409 return text
409 return text
410
410
411 def simplemerge(ui, localfile, basefile, otherfile,
411 def simplemerge(ui, localfile, basefile, otherfile,
412 localctx=None, basectx=None, otherctx=None, repo=None, **opts):
412 localctx=None, basectx=None, otherctx=None, repo=None, **opts):
413 """Performs the simplemerge algorithm.
414
415 {local|base|other}ctx are optional. If passed, they (local/base/other) will
416 be read from. You should pass explicit labels in this mode since the default
417 is to use the file paths."""
413 def readfile(filename):
418 def readfile(filename):
414 f = open(filename, "rb")
419 f = open(filename, "rb")
415 text = f.read()
420 text = f.read()
416 f.close()
421 f.close()
417 return _verifytext(text, filename, ui, opts)
422 return _verifytext(text, filename, ui, opts)
418
423
424 def readctx(ctx):
425 if not ctx:
426 return None
427 if not repo:
428 raise error.ProgrammingError('simplemerge: repo must be passed if '
429 'using contexts')
430 # `wwritedata` is used to get the post-filter data from `ctx` (i.e.,
431 # what would have been in the working copy). Since merges were run in
432 # the working copy, and thus used post-filter data, we do the same to
433 # maintain behavior.
434 return repo.wwritedata(ctx.path(),
435 _verifytext(ctx.data(), ctx.path(), ui, opts))
436
419 mode = opts.get('mode','merge')
437 mode = opts.get('mode','merge')
420 if mode == 'union':
438 if mode == 'union':
421 name_a = None
439 name_a = None
422 name_b = None
440 name_b = None
423 name_base = None
441 name_base = None
424 else:
442 else:
425 name_a = localfile
443 name_a = localfile
426 name_b = otherfile
444 name_b = otherfile
427 name_base = None
445 name_base = None
428 labels = opts.get('label', [])
446 labels = opts.get('label', [])
429 if len(labels) > 0:
447 if len(labels) > 0:
430 name_a = labels[0]
448 name_a = labels[0]
431 if len(labels) > 1:
449 if len(labels) > 1:
432 name_b = labels[1]
450 name_b = labels[1]
433 if len(labels) > 2:
451 if len(labels) > 2:
434 name_base = labels[2]
452 name_base = labels[2]
435 if len(labels) > 3:
453 if len(labels) > 3:
436 raise error.Abort(_("can only specify three labels."))
454 raise error.Abort(_("can only specify three labels."))
437
455
438 try:
456 try:
439 localtext = readfile(localfile)
457 localtext = readctx(localctx) if localctx else readfile(localfile)
440 basetext = readfile(basefile)
458 basetext = readctx(basectx) if basectx else readfile(basefile)
441 othertext = readfile(otherfile)
459 othertext = readctx(otherctx) if otherctx else readfile(otherfile)
442 except error.Abort:
460 except error.Abort:
443 return 1
461 return 1
444
462
445 localfile = os.path.realpath(localfile)
463 localfile = os.path.realpath(localfile)
446 if not opts.get('print'):
464 if not opts.get('print'):
447 opener = vfsmod.vfs(os.path.dirname(localfile))
465 opener = vfsmod.vfs(os.path.dirname(localfile))
448 out = opener(os.path.basename(localfile), "w", atomictemp=True)
466 out = opener(os.path.basename(localfile), "w", atomictemp=True)
449 else:
467 else:
450 out = ui.fout
468 out = ui.fout
451
469
452 m3 = Merge3Text(basetext, localtext, othertext)
470 m3 = Merge3Text(basetext, localtext, othertext)
453 extrakwargs = {
471 extrakwargs = {
454 "localorother": opts.get("localorother", None),
472 "localorother": opts.get("localorother", None),
455 'minimize': True,
473 'minimize': True,
456 }
474 }
457 if mode == 'union':
475 if mode == 'union':
458 extrakwargs['start_marker'] = None
476 extrakwargs['start_marker'] = None
459 extrakwargs['mid_marker'] = None
477 extrakwargs['mid_marker'] = None
460 extrakwargs['end_marker'] = None
478 extrakwargs['end_marker'] = None
461 elif name_base is not None:
479 elif name_base is not None:
462 extrakwargs['base_marker'] = '|||||||'
480 extrakwargs['base_marker'] = '|||||||'
463 extrakwargs['name_base'] = name_base
481 extrakwargs['name_base'] = name_base
464 extrakwargs['minimize'] = False
482 extrakwargs['minimize'] = False
465 for line in m3.merge_lines(name_a=name_a, name_b=name_b,
483 for line in m3.merge_lines(name_a=name_a, name_b=name_b,
466 **pycompat.strkwargs(extrakwargs)):
484 **pycompat.strkwargs(extrakwargs)):
467 out.write(line)
485 out.write(line)
468
486
469 if not opts.get('print'):
487 if not opts.get('print'):
470 out.close()
488 out.close()
471
489
472 if m3.conflicts and not mode == 'union':
490 if m3.conflicts and not mode == 'union':
473 return 1
491 return 1
@@ -1,397 +1,397 b''
1 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
1 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
2 $ mkdir "${USERCACHE}"
2 $ mkdir "${USERCACHE}"
3 $ cat >> $HGRCPATH <<EOF
3 $ cat >> $HGRCPATH <<EOF
4 > [format]
4 > [format]
5 > usegeneraldelta=yes
5 > usegeneraldelta=yes
6 > [extensions]
6 > [extensions]
7 > largefiles =
7 > largefiles =
8 > share =
8 > share =
9 > strip =
9 > strip =
10 > convert =
10 > convert =
11 > [largefiles]
11 > [largefiles]
12 > minsize = 0.5
12 > minsize = 0.5
13 > patterns = **.other
13 > patterns = **.other
14 > **.dat
14 > **.dat
15 > usercache=${USERCACHE}
15 > usercache=${USERCACHE}
16 > EOF
16 > EOF
17
17
18 "lfconvert" works
18 "lfconvert" works
19 $ hg init bigfile-repo
19 $ hg init bigfile-repo
20 $ cd bigfile-repo
20 $ cd bigfile-repo
21 $ cat >> .hg/hgrc <<EOF
21 $ cat >> .hg/hgrc <<EOF
22 > [extensions]
22 > [extensions]
23 > largefiles = !
23 > largefiles = !
24 > EOF
24 > EOF
25 $ mkdir sub
25 $ mkdir sub
26 $ dd if=/dev/zero bs=1k count=256 > large 2> /dev/null
26 $ dd if=/dev/zero bs=1k count=256 > large 2> /dev/null
27 $ dd if=/dev/zero bs=1k count=256 > large2 2> /dev/null
27 $ dd if=/dev/zero bs=1k count=256 > large2 2> /dev/null
28 $ echo normal > normal1
28 $ echo normal > normal1
29 $ echo alsonormal > sub/normal2
29 $ echo alsonormal > sub/normal2
30 $ dd if=/dev/zero bs=1k count=10 > sub/maybelarge.dat 2> /dev/null
30 $ dd if=/dev/zero bs=1k count=10 > sub/maybelarge.dat 2> /dev/null
31 $ hg addremove
31 $ hg addremove
32 adding large
32 adding large
33 adding large2
33 adding large2
34 adding normal1
34 adding normal1
35 adding sub/maybelarge.dat
35 adding sub/maybelarge.dat
36 adding sub/normal2
36 adding sub/normal2
37 $ hg commit -m"add large, normal1" large normal1
37 $ hg commit -m"add large, normal1" large normal1
38 $ hg commit -m"add sub/*" sub
38 $ hg commit -m"add sub/*" sub
39
39
40 Test tag parsing
40 Test tag parsing
41 $ cat >> .hgtags <<EOF
41 $ cat >> .hgtags <<EOF
42 > IncorrectlyFormattedTag!
42 > IncorrectlyFormattedTag!
43 > invalidhash sometag
43 > invalidhash sometag
44 > 0123456789abcdef anothertag
44 > 0123456789abcdef anothertag
45 > EOF
45 > EOF
46 $ hg add .hgtags
46 $ hg add .hgtags
47 $ hg commit -m"add large2" large2 .hgtags
47 $ hg commit -m"add large2" large2 .hgtags
48
48
49 Test link+rename largefile codepath
49 Test link+rename largefile codepath
50 $ [ -d .hg/largefiles ] && echo fail || echo pass
50 $ [ -d .hg/largefiles ] && echo fail || echo pass
51 pass
51 pass
52 $ cd ..
52 $ cd ..
53 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
53 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
54 initializing destination largefiles-repo
54 initializing destination largefiles-repo
55 skipping incorrectly formatted tag IncorrectlyFormattedTag!
55 skipping incorrectly formatted tag IncorrectlyFormattedTag!
56 skipping incorrectly formatted id invalidhash
56 skipping incorrectly formatted id invalidhash
57 no mapping for id 0123456789abcdef
57 no mapping for id 0123456789abcdef
58 #if symlink
58 #if symlink
59 $ hg --cwd bigfile-repo rename large2 large3
59 $ hg --cwd bigfile-repo rename large2 large3
60 $ ln -sf large bigfile-repo/large3
60 $ ln -sf large bigfile-repo/large3
61 $ hg --cwd bigfile-repo commit -m"make large2 a symlink" large2 large3
61 $ hg --cwd bigfile-repo commit -m"make large2 a symlink" large2 large3
62 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo-symlink
62 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo-symlink
63 initializing destination largefiles-repo-symlink
63 initializing destination largefiles-repo-symlink
64 skipping incorrectly formatted tag IncorrectlyFormattedTag!
64 skipping incorrectly formatted tag IncorrectlyFormattedTag!
65 skipping incorrectly formatted id invalidhash
65 skipping incorrectly formatted id invalidhash
66 no mapping for id 0123456789abcdef
66 no mapping for id 0123456789abcdef
67 abort: renamed/copied largefile large3 becomes symlink
67 abort: renamed/copied largefile large3 becomes symlink
68 [255]
68 [255]
69 #endif
69 #endif
70 $ cd bigfile-repo
70 $ cd bigfile-repo
71 $ hg strip --no-backup 2
71 $ hg strip --no-backup 2
72 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
72 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
73 $ cd ..
73 $ cd ..
74 $ rm -rf largefiles-repo largefiles-repo-symlink
74 $ rm -rf largefiles-repo largefiles-repo-symlink
75
75
76 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
76 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
77 initializing destination largefiles-repo
77 initializing destination largefiles-repo
78
78
79 "lfconvert" converts content correctly
79 "lfconvert" converts content correctly
80 $ cd largefiles-repo
80 $ cd largefiles-repo
81 $ hg up
81 $ hg up
82 getting changed largefiles
82 getting changed largefiles
83 2 largefiles updated, 0 removed
83 2 largefiles updated, 0 removed
84 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
85 $ hg locate
85 $ hg locate
86 .hglf/large
86 .hglf/large
87 .hglf/sub/maybelarge.dat
87 .hglf/sub/maybelarge.dat
88 normal1
88 normal1
89 sub/normal2
89 sub/normal2
90 $ cat normal1
90 $ cat normal1
91 normal
91 normal
92 $ cat sub/normal2
92 $ cat sub/normal2
93 alsonormal
93 alsonormal
94 $ md5sum.py large sub/maybelarge.dat
94 $ md5sum.py large sub/maybelarge.dat
95 ec87a838931d4d5d2e94a04644788a55 large
95 ec87a838931d4d5d2e94a04644788a55 large
96 1276481102f218c981e0324180bafd9f sub/maybelarge.dat
96 1276481102f218c981e0324180bafd9f sub/maybelarge.dat
97
97
98 "lfconvert" adds 'largefiles' to .hg/requires.
98 "lfconvert" adds 'largefiles' to .hg/requires.
99 $ cat .hg/requires
99 $ cat .hg/requires
100 dotencode
100 dotencode
101 fncache
101 fncache
102 generaldelta
102 generaldelta
103 largefiles
103 largefiles
104 revlogv1
104 revlogv1
105 store
105 store
106
106
107 "lfconvert" includes a newline at the end of the standin files.
107 "lfconvert" includes a newline at the end of the standin files.
108 $ cat .hglf/large .hglf/sub/maybelarge.dat
108 $ cat .hglf/large .hglf/sub/maybelarge.dat
109 2e000fa7e85759c7f4c254d4d9c33ef481e459a7
109 2e000fa7e85759c7f4c254d4d9c33ef481e459a7
110 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c
110 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c
111 $ cd ..
111 $ cd ..
112
112
113 add some changesets to rename/remove/merge
113 add some changesets to rename/remove/merge
114 $ cd bigfile-repo
114 $ cd bigfile-repo
115 $ hg mv -q sub stuff
115 $ hg mv -q sub stuff
116 $ hg commit -m"rename sub/ to stuff/"
116 $ hg commit -m"rename sub/ to stuff/"
117 $ hg update -q 1
117 $ hg update -q 1
118 $ echo blah >> normal3
118 $ echo blah >> normal3
119 $ echo blah >> sub/normal2
119 $ echo blah >> sub/normal2
120 $ echo blah >> sub/maybelarge.dat
120 $ echo blah >> sub/maybelarge.dat
121 $ md5sum.py sub/maybelarge.dat
121 $ md5sum.py sub/maybelarge.dat
122 1dd0b99ff80e19cff409702a1d3f5e15 sub/maybelarge.dat
122 1dd0b99ff80e19cff409702a1d3f5e15 sub/maybelarge.dat
123 $ hg commit -A -m"add normal3, modify sub/*"
123 $ hg commit -A -m"add normal3, modify sub/*"
124 adding normal3
124 adding normal3
125 created new head
125 created new head
126 $ hg rm large normal3
126 $ hg rm large normal3
127 $ hg commit -q -m"remove large, normal3"
127 $ hg commit -q -m"remove large, normal3"
128 $ hg merge
128 $ hg merge
129 merging sub/maybelarge.dat and stuff/maybelarge.dat to stuff/maybelarge.dat
129 merging sub/maybelarge.dat and stuff/maybelarge.dat to stuff/maybelarge.dat
130 merging sub/normal2 and stuff/normal2 to stuff/normal2
130 merging sub/normal2 and stuff/normal2 to stuff/normal2
131 warning: $TESTTMP/bigfile-repo/stuff/maybelarge.dat looks like a binary file. (glob)
131 warning: stuff/maybelarge.dat looks like a binary file. (glob)
132 warning: conflicts while merging stuff/maybelarge.dat! (edit, then use 'hg resolve --mark')
132 warning: conflicts while merging stuff/maybelarge.dat! (edit, then use 'hg resolve --mark')
133 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
133 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
134 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
134 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
135 [1]
135 [1]
136 $ hg cat -r . sub/maybelarge.dat > stuff/maybelarge.dat
136 $ hg cat -r . sub/maybelarge.dat > stuff/maybelarge.dat
137 $ hg resolve -m stuff/maybelarge.dat
137 $ hg resolve -m stuff/maybelarge.dat
138 (no more unresolved files)
138 (no more unresolved files)
139 $ hg commit -m"merge"
139 $ hg commit -m"merge"
140 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
140 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
141 @ 5:4884f215abda merge
141 @ 5:4884f215abda merge
142 |\
142 |\
143 | o 4:7285f817b77e remove large, normal3
143 | o 4:7285f817b77e remove large, normal3
144 | |
144 | |
145 | o 3:67e3892e3534 add normal3, modify sub/*
145 | o 3:67e3892e3534 add normal3, modify sub/*
146 | |
146 | |
147 o | 2:c96c8beb5d56 rename sub/ to stuff/
147 o | 2:c96c8beb5d56 rename sub/ to stuff/
148 |/
148 |/
149 o 1:020c65d24e11 add sub/*
149 o 1:020c65d24e11 add sub/*
150 |
150 |
151 o 0:117b8328f97a add large, normal1
151 o 0:117b8328f97a add large, normal1
152
152
153 $ cd ..
153 $ cd ..
154
154
155 lfconvert with rename, merge, and remove
155 lfconvert with rename, merge, and remove
156 $ rm -rf largefiles-repo
156 $ rm -rf largefiles-repo
157 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
157 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
158 initializing destination largefiles-repo
158 initializing destination largefiles-repo
159 $ cd largefiles-repo
159 $ cd largefiles-repo
160 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
160 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
161 o 5:8e05f5f2b77e merge
161 o 5:8e05f5f2b77e merge
162 |\
162 |\
163 | o 4:a5a02de7a8e4 remove large, normal3
163 | o 4:a5a02de7a8e4 remove large, normal3
164 | |
164 | |
165 | o 3:55759520c76f add normal3, modify sub/*
165 | o 3:55759520c76f add normal3, modify sub/*
166 | |
166 | |
167 o | 2:261ad3f3f037 rename sub/ to stuff/
167 o | 2:261ad3f3f037 rename sub/ to stuff/
168 |/
168 |/
169 o 1:334e5237836d add sub/*
169 o 1:334e5237836d add sub/*
170 |
170 |
171 o 0:d4892ec57ce2 add large, normal1
171 o 0:d4892ec57ce2 add large, normal1
172
172
173 $ hg locate -r 2
173 $ hg locate -r 2
174 .hglf/large
174 .hglf/large
175 .hglf/stuff/maybelarge.dat
175 .hglf/stuff/maybelarge.dat
176 normal1
176 normal1
177 stuff/normal2
177 stuff/normal2
178 $ hg locate -r 3
178 $ hg locate -r 3
179 .hglf/large
179 .hglf/large
180 .hglf/sub/maybelarge.dat
180 .hglf/sub/maybelarge.dat
181 normal1
181 normal1
182 normal3
182 normal3
183 sub/normal2
183 sub/normal2
184 $ hg locate -r 4
184 $ hg locate -r 4
185 .hglf/sub/maybelarge.dat
185 .hglf/sub/maybelarge.dat
186 normal1
186 normal1
187 sub/normal2
187 sub/normal2
188 $ hg locate -r 5
188 $ hg locate -r 5
189 .hglf/stuff/maybelarge.dat
189 .hglf/stuff/maybelarge.dat
190 normal1
190 normal1
191 stuff/normal2
191 stuff/normal2
192 $ hg update
192 $ hg update
193 getting changed largefiles
193 getting changed largefiles
194 1 largefiles updated, 0 removed
194 1 largefiles updated, 0 removed
195 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
195 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
196 $ cat stuff/normal2
196 $ cat stuff/normal2
197 alsonormal
197 alsonormal
198 blah
198 blah
199 $ md5sum.py stuff/maybelarge.dat
199 $ md5sum.py stuff/maybelarge.dat
200 1dd0b99ff80e19cff409702a1d3f5e15 stuff/maybelarge.dat
200 1dd0b99ff80e19cff409702a1d3f5e15 stuff/maybelarge.dat
201 $ cat .hglf/stuff/maybelarge.dat
201 $ cat .hglf/stuff/maybelarge.dat
202 76236b6a2c6102826c61af4297dd738fb3b1de38
202 76236b6a2c6102826c61af4297dd738fb3b1de38
203 $ cd ..
203 $ cd ..
204
204
205 "lfconvert" error cases
205 "lfconvert" error cases
206 $ hg lfconvert http://localhost/foo foo
206 $ hg lfconvert http://localhost/foo foo
207 abort: http://localhost/foo is not a local Mercurial repo
207 abort: http://localhost/foo is not a local Mercurial repo
208 [255]
208 [255]
209 $ hg lfconvert foo ssh://localhost/foo
209 $ hg lfconvert foo ssh://localhost/foo
210 abort: ssh://localhost/foo is not a local Mercurial repo
210 abort: ssh://localhost/foo is not a local Mercurial repo
211 [255]
211 [255]
212 $ hg lfconvert nosuchrepo foo
212 $ hg lfconvert nosuchrepo foo
213 abort: repository nosuchrepo not found!
213 abort: repository nosuchrepo not found!
214 [255]
214 [255]
215 $ hg share -q -U bigfile-repo shared
215 $ hg share -q -U bigfile-repo shared
216 $ printf 'bogus' > shared/.hg/sharedpath
216 $ printf 'bogus' > shared/.hg/sharedpath
217 $ hg lfconvert shared foo
217 $ hg lfconvert shared foo
218 abort: .hg/sharedpath points to nonexistent directory $TESTTMP/bogus! (glob)
218 abort: .hg/sharedpath points to nonexistent directory $TESTTMP/bogus! (glob)
219 [255]
219 [255]
220 $ hg lfconvert bigfile-repo largefiles-repo
220 $ hg lfconvert bigfile-repo largefiles-repo
221 initializing destination largefiles-repo
221 initializing destination largefiles-repo
222 abort: repository largefiles-repo already exists!
222 abort: repository largefiles-repo already exists!
223 [255]
223 [255]
224
224
225 add another largefile to the new largefiles repo
225 add another largefile to the new largefiles repo
226 $ cd largefiles-repo
226 $ cd largefiles-repo
227 $ dd if=/dev/zero bs=1k count=1k > anotherlarge 2> /dev/null
227 $ dd if=/dev/zero bs=1k count=1k > anotherlarge 2> /dev/null
228 $ hg add --lfsize=1 anotherlarge
228 $ hg add --lfsize=1 anotherlarge
229 $ hg commit -m "add anotherlarge (should be a largefile)"
229 $ hg commit -m "add anotherlarge (should be a largefile)"
230 $ cat .hglf/anotherlarge
230 $ cat .hglf/anotherlarge
231 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
231 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
232 $ hg tag mytag
232 $ hg tag mytag
233 $ cd ..
233 $ cd ..
234
234
235 round-trip: converting back to a normal (non-largefiles) repo with
235 round-trip: converting back to a normal (non-largefiles) repo with
236 "lfconvert --to-normal" should give the same as ../bigfile-repo
236 "lfconvert --to-normal" should give the same as ../bigfile-repo
237 $ cd largefiles-repo
237 $ cd largefiles-repo
238 $ hg lfconvert --to-normal . ../normal-repo
238 $ hg lfconvert --to-normal . ../normal-repo
239 initializing destination ../normal-repo
239 initializing destination ../normal-repo
240 0 additional largefiles cached
240 0 additional largefiles cached
241 scanning source...
241 scanning source...
242 sorting...
242 sorting...
243 converting...
243 converting...
244 7 add large, normal1
244 7 add large, normal1
245 6 add sub/*
245 6 add sub/*
246 5 rename sub/ to stuff/
246 5 rename sub/ to stuff/
247 4 add normal3, modify sub/*
247 4 add normal3, modify sub/*
248 3 remove large, normal3
248 3 remove large, normal3
249 2 merge
249 2 merge
250 1 add anotherlarge (should be a largefile)
250 1 add anotherlarge (should be a largefile)
251 0 Added tag mytag for changeset abacddda7028
251 0 Added tag mytag for changeset abacddda7028
252 $ cd ../normal-repo
252 $ cd ../normal-repo
253 $ cat >> .hg/hgrc <<EOF
253 $ cat >> .hg/hgrc <<EOF
254 > [extensions]
254 > [extensions]
255 > largefiles = !
255 > largefiles = !
256 > EOF
256 > EOF
257
257
258 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
258 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
259 o 7:b5fedc110b9d Added tag mytag for changeset 867ab992ecf4
259 o 7:b5fedc110b9d Added tag mytag for changeset 867ab992ecf4
260 |
260 |
261 o 6:867ab992ecf4 add anotherlarge (should be a largefile)
261 o 6:867ab992ecf4 add anotherlarge (should be a largefile)
262 |
262 |
263 o 5:4884f215abda merge
263 o 5:4884f215abda merge
264 |\
264 |\
265 | o 4:7285f817b77e remove large, normal3
265 | o 4:7285f817b77e remove large, normal3
266 | |
266 | |
267 | o 3:67e3892e3534 add normal3, modify sub/*
267 | o 3:67e3892e3534 add normal3, modify sub/*
268 | |
268 | |
269 o | 2:c96c8beb5d56 rename sub/ to stuff/
269 o | 2:c96c8beb5d56 rename sub/ to stuff/
270 |/
270 |/
271 o 1:020c65d24e11 add sub/*
271 o 1:020c65d24e11 add sub/*
272 |
272 |
273 o 0:117b8328f97a add large, normal1
273 o 0:117b8328f97a add large, normal1
274
274
275 $ hg update
275 $ hg update
276 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
277 $ hg locate
277 $ hg locate
278 .hgtags
278 .hgtags
279 anotherlarge
279 anotherlarge
280 normal1
280 normal1
281 stuff/maybelarge.dat
281 stuff/maybelarge.dat
282 stuff/normal2
282 stuff/normal2
283 $ [ -d .hg/largefiles ] && echo fail || echo pass
283 $ [ -d .hg/largefiles ] && echo fail || echo pass
284 pass
284 pass
285
285
286 $ cd ..
286 $ cd ..
287
287
288 Clearing the usercache ensures that commitctx doesn't try to cache largefiles
288 Clearing the usercache ensures that commitctx doesn't try to cache largefiles
289 from the working dir on a convert.
289 from the working dir on a convert.
290 $ rm "${USERCACHE}"/*
290 $ rm "${USERCACHE}"/*
291 $ hg convert largefiles-repo
291 $ hg convert largefiles-repo
292 assuming destination largefiles-repo-hg
292 assuming destination largefiles-repo-hg
293 initializing destination largefiles-repo-hg repository
293 initializing destination largefiles-repo-hg repository
294 scanning source...
294 scanning source...
295 sorting...
295 sorting...
296 converting...
296 converting...
297 7 add large, normal1
297 7 add large, normal1
298 6 add sub/*
298 6 add sub/*
299 5 rename sub/ to stuff/
299 5 rename sub/ to stuff/
300 4 add normal3, modify sub/*
300 4 add normal3, modify sub/*
301 3 remove large, normal3
301 3 remove large, normal3
302 2 merge
302 2 merge
303 1 add anotherlarge (should be a largefile)
303 1 add anotherlarge (should be a largefile)
304 0 Added tag mytag for changeset abacddda7028
304 0 Added tag mytag for changeset abacddda7028
305
305
306 $ hg -R largefiles-repo-hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
306 $ hg -R largefiles-repo-hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
307 o 7:2f08f66459b7 Added tag mytag for changeset 17126745edfd
307 o 7:2f08f66459b7 Added tag mytag for changeset 17126745edfd
308 |
308 |
309 o 6:17126745edfd add anotherlarge (should be a largefile)
309 o 6:17126745edfd add anotherlarge (should be a largefile)
310 |
310 |
311 o 5:9cc5aa7204f0 merge
311 o 5:9cc5aa7204f0 merge
312 |\
312 |\
313 | o 4:a5a02de7a8e4 remove large, normal3
313 | o 4:a5a02de7a8e4 remove large, normal3
314 | |
314 | |
315 | o 3:55759520c76f add normal3, modify sub/*
315 | o 3:55759520c76f add normal3, modify sub/*
316 | |
316 | |
317 o | 2:261ad3f3f037 rename sub/ to stuff/
317 o | 2:261ad3f3f037 rename sub/ to stuff/
318 |/
318 |/
319 o 1:334e5237836d add sub/*
319 o 1:334e5237836d add sub/*
320 |
320 |
321 o 0:d4892ec57ce2 add large, normal1
321 o 0:d4892ec57ce2 add large, normal1
322
322
323 Verify will fail (for now) if the usercache is purged before converting, since
323 Verify will fail (for now) if the usercache is purged before converting, since
324 largefiles are not cached in the converted repo's local store by the conversion
324 largefiles are not cached in the converted repo's local store by the conversion
325 process.
325 process.
326 $ cd largefiles-repo-hg
326 $ cd largefiles-repo-hg
327 $ cat >> .hg/hgrc <<EOF
327 $ cat >> .hg/hgrc <<EOF
328 > [experimental]
328 > [experimental]
329 > stabilization=createmarkers
329 > stabilization=createmarkers
330 > EOF
330 > EOF
331 $ hg debugobsolete `hg log -r tip -T "{node}"`
331 $ hg debugobsolete `hg log -r tip -T "{node}"`
332 obsoleted 1 changesets
332 obsoleted 1 changesets
333 $ cd ..
333 $ cd ..
334
334
335 $ hg -R largefiles-repo-hg verify --large --lfa
335 $ hg -R largefiles-repo-hg verify --large --lfa
336 checking changesets
336 checking changesets
337 checking manifests
337 checking manifests
338 crosschecking files in changesets and manifests
338 crosschecking files in changesets and manifests
339 checking files
339 checking files
340 9 files, 8 changesets, 13 total revisions
340 9 files, 8 changesets, 13 total revisions
341 searching 7 changesets for largefiles
341 searching 7 changesets for largefiles
342 changeset 0:d4892ec57ce2: large references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/2e000fa7e85759c7f4c254d4d9c33ef481e459a7 (glob)
342 changeset 0:d4892ec57ce2: large references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/2e000fa7e85759c7f4c254d4d9c33ef481e459a7 (glob)
343 changeset 1:334e5237836d: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob)
343 changeset 1:334e5237836d: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob)
344 changeset 2:261ad3f3f037: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob)
344 changeset 2:261ad3f3f037: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c (glob)
345 changeset 3:55759520c76f: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38 (glob)
345 changeset 3:55759520c76f: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38 (glob)
346 changeset 5:9cc5aa7204f0: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38 (glob)
346 changeset 5:9cc5aa7204f0: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38 (glob)
347 changeset 6:17126745edfd: anotherlarge references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 (glob)
347 changeset 6:17126745edfd: anotherlarge references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 (glob)
348 verified existence of 6 revisions of 4 largefiles
348 verified existence of 6 revisions of 4 largefiles
349 [1]
349 [1]
350 $ hg -R largefiles-repo-hg showconfig paths
350 $ hg -R largefiles-repo-hg showconfig paths
351 [1]
351 [1]
352
352
353
353
354 Avoid a traceback if a largefile isn't available (issue3519)
354 Avoid a traceback if a largefile isn't available (issue3519)
355
355
356 Ensure the largefile can be cached in the source if necessary
356 Ensure the largefile can be cached in the source if necessary
357 $ hg clone -U largefiles-repo issue3519
357 $ hg clone -U largefiles-repo issue3519
358 $ rm -f "${USERCACHE}"/*
358 $ rm -f "${USERCACHE}"/*
359 $ hg lfconvert --to-normal issue3519 normalized3519
359 $ hg lfconvert --to-normal issue3519 normalized3519
360 initializing destination normalized3519
360 initializing destination normalized3519
361 4 additional largefiles cached
361 4 additional largefiles cached
362 scanning source...
362 scanning source...
363 sorting...
363 sorting...
364 converting...
364 converting...
365 7 add large, normal1
365 7 add large, normal1
366 6 add sub/*
366 6 add sub/*
367 5 rename sub/ to stuff/
367 5 rename sub/ to stuff/
368 4 add normal3, modify sub/*
368 4 add normal3, modify sub/*
369 3 remove large, normal3
369 3 remove large, normal3
370 2 merge
370 2 merge
371 1 add anotherlarge (should be a largefile)
371 1 add anotherlarge (should be a largefile)
372 0 Added tag mytag for changeset abacddda7028
372 0 Added tag mytag for changeset abacddda7028
373
373
374 Ensure the abort message is useful if a largefile is entirely unavailable
374 Ensure the abort message is useful if a largefile is entirely unavailable
375 $ rm -rf normalized3519
375 $ rm -rf normalized3519
376 $ rm "${USERCACHE}"/*
376 $ rm "${USERCACHE}"/*
377 $ rm issue3519/.hg/largefiles/*
377 $ rm issue3519/.hg/largefiles/*
378 $ rm largefiles-repo/.hg/largefiles/*
378 $ rm largefiles-repo/.hg/largefiles/*
379 $ hg lfconvert --to-normal issue3519 normalized3519
379 $ hg lfconvert --to-normal issue3519 normalized3519
380 initializing destination normalized3519
380 initializing destination normalized3519
381 anotherlarge: largefile 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 not available from file:/*/$TESTTMP/largefiles-repo (glob)
381 anotherlarge: largefile 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 not available from file:/*/$TESTTMP/largefiles-repo (glob)
382 stuff/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
382 stuff/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
383 stuff/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
383 stuff/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
384 sub/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
384 sub/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
385 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
385 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
386 sub/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
386 sub/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
387 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
387 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
388 stuff/maybelarge.dat: largefile 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c not available from file:/*/$TESTTMP/largefiles-repo (glob)
388 stuff/maybelarge.dat: largefile 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c not available from file:/*/$TESTTMP/largefiles-repo (glob)
389 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
389 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
390 sub/maybelarge.dat: largefile 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c not available from file:/*/$TESTTMP/largefiles-repo (glob)
390 sub/maybelarge.dat: largefile 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c not available from file:/*/$TESTTMP/largefiles-repo (glob)
391 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
391 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
392 0 additional largefiles cached
392 0 additional largefiles cached
393 11 largefiles failed to download
393 11 largefiles failed to download
394 abort: all largefiles must be present locally
394 abort: all largefiles must be present locally
395 [255]
395 [255]
396
396
397
397
General Comments 0
You need to be logged in to leave comments. Login now