##// END OF EJS Templates
extdiff: use archiver to take snapshots of committed revisions...
Matt Harbison -
r25812:68822b7c default
parent child Browse files
Show More
@@ -1,344 +1,346 b''
1 1 # extdiff.py - external diff program support for mercurial
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 '''command to allow external programs to compare revisions
9 9
10 10 The extdiff Mercurial extension allows you to use external programs
11 11 to compare revisions, or revision with working directory. The external
12 12 diff programs are called with a configurable set of options and two
13 13 non-option arguments: paths to directories containing snapshots of
14 14 files to compare.
15 15
16 16 The extdiff extension also allows you to configure new diff commands, so
17 17 you do not need to type :hg:`extdiff -p kdiff3` always. ::
18 18
19 19 [extdiff]
20 20 # add new command that runs GNU diff(1) in 'context diff' mode
21 21 cdiff = gdiff -Nprc5
22 22 ## or the old way:
23 23 #cmd.cdiff = gdiff
24 24 #opts.cdiff = -Nprc5
25 25
26 26 # add new command called meld, runs meld (no need to name twice). If
27 27 # the meld executable is not available, the meld tool in [merge-tools]
28 28 # will be used, if available
29 29 meld =
30 30
31 31 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
32 32 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
33 33 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
34 34 # your .vimrc
35 35 vimdiff = gvim -f "+next" \\
36 36 "+execute 'DirDiff' fnameescape(argv(0)) fnameescape(argv(1))"
37 37
38 38 Tool arguments can include variables that are expanded at runtime::
39 39
40 40 $parent1, $plabel1 - filename, descriptive label of first parent
41 41 $child, $clabel - filename, descriptive label of child revision
42 42 $parent2, $plabel2 - filename, descriptive label of second parent
43 43 $root - repository root
44 44 $parent is an alias for $parent1.
45 45
46 46 The extdiff extension will look in your [diff-tools] and [merge-tools]
47 47 sections for diff tool arguments, when none are specified in [extdiff].
48 48
49 49 ::
50 50
51 51 [extdiff]
52 52 kdiff3 =
53 53
54 54 [diff-tools]
55 55 kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
56 56
57 57 You can use -I/-X and list of file or directory names like normal
58 58 :hg:`diff` command. The extdiff extension makes snapshots of only
59 59 needed files, so running the external diff program will actually be
60 60 pretty fast (at least faster than having to compare the entire tree).
61 61 '''
62 62
63 63 from mercurial.i18n import _
64 64 from mercurial.node import short, nullid
65 65 from mercurial import cmdutil, scmutil, util, commands, encoding, filemerge
66 from mercurial import archival
66 67 import os, shlex, shutil, tempfile, re
67 68
68 69 cmdtable = {}
69 70 command = cmdutil.command(cmdtable)
70 71 # Note for extension authors: ONLY specify testedwith = 'internal' for
71 72 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
72 73 # be specifying the version(s) of Mercurial they are tested with, or
73 74 # leave the attribute unspecified.
74 75 testedwith = 'internal'
75 76
76 77 def snapshot(ui, repo, files, node, tmproot):
77 78 '''snapshot files as of some revision
78 79 if not using snapshot, -I/-X does not work and recursive diff
79 80 in tools like kdiff3 and meld displays too many files.'''
80 81 dirname = os.path.basename(repo.root)
81 82 if dirname == "":
82 83 dirname = "root"
83 84 if node is not None:
84 85 dirname = '%s.%s' % (dirname, short(node))
85 86 base = os.path.join(tmproot, dirname)
86 87 os.mkdir(base)
88 fns_and_mtime = []
89
87 90 if node is not None:
88 91 ui.note(_('making snapshot of %d files from rev %s\n') %
89 92 (len(files), short(node)))
90 93 else:
91 94 ui.note(_('making snapshot of %d files from working directory\n') %
92 95 (len(files)))
93 wopener = scmutil.opener(base)
94 fns_and_mtime = []
96
97 if files:
98 repo.ui.setconfig("ui", "archivemeta", False)
99
100 archival.archive(repo, base, node, 'files',
101 matchfn=scmutil.matchfiles(repo, files))
102
95 103 ctx = repo[node]
96 104 for fn in sorted(files):
97 105 wfn = util.pconvert(fn)
98 106 if wfn not in ctx:
99 107 # File doesn't exist; could be a bogus modify
100 108 continue
101 109 ui.note(' %s\n' % wfn)
110
111 if node is None:
102 112 dest = os.path.join(base, wfn)
103 fctx = ctx[wfn]
104 data = repo.wwritedata(wfn, fctx.data())
105 if 'l' in fctx.flags():
106 wopener.symlink(data, wfn)
107 else:
108 wopener.write(wfn, data)
109 if 'x' in fctx.flags():
110 util.setflags(dest, False, True)
111 if node is None:
113
112 114 fns_and_mtime.append((dest, repo.wjoin(fn),
113 115 os.lstat(dest).st_mtime))
114 116 return dirname, fns_and_mtime
115 117
116 118 def dodiff(ui, repo, cmdline, pats, opts):
117 119 '''Do the actual diff:
118 120
119 121 - copy to a temp structure if diffing 2 internal revisions
120 122 - copy to a temp structure if diffing working revision with
121 123 another one and more than 1 file is changed
122 124 - just invoke the diff for a single file in the working dir
123 125 '''
124 126
125 127 revs = opts.get('rev')
126 128 change = opts.get('change')
127 129 do3way = '$parent2' in cmdline
128 130
129 131 if revs and change:
130 132 msg = _('cannot specify --rev and --change at the same time')
131 133 raise util.Abort(msg)
132 134 elif change:
133 135 node2 = scmutil.revsingle(repo, change, None).node()
134 136 node1a, node1b = repo.changelog.parents(node2)
135 137 else:
136 138 node1a, node2 = scmutil.revpair(repo, revs)
137 139 if not revs:
138 140 node1b = repo.dirstate.p2()
139 141 else:
140 142 node1b = nullid
141 143
142 144 # Disable 3-way merge if there is only one parent
143 145 if do3way:
144 146 if node1b == nullid:
145 147 do3way = False
146 148
147 149 matcher = scmutil.match(repo[node2], pats, opts)
148 150 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher)[:3])
149 151 if do3way:
150 152 mod_b, add_b, rem_b = map(set, repo.status(node1b, node2, matcher)[:3])
151 153 else:
152 154 mod_b, add_b, rem_b = set(), set(), set()
153 155 modadd = mod_a | add_a | mod_b | add_b
154 156 common = modadd | rem_a | rem_b
155 157 if not common:
156 158 return 0
157 159
158 160 tmproot = tempfile.mkdtemp(prefix='extdiff.')
159 161 try:
160 162 # Always make a copy of node1a (and node1b, if applicable)
161 163 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
162 164 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot)[0]
163 165 rev1a = '@%d' % repo[node1a].rev()
164 166 if do3way:
165 167 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
166 168 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot)[0]
167 169 rev1b = '@%d' % repo[node1b].rev()
168 170 else:
169 171 dir1b = None
170 172 rev1b = ''
171 173
172 174 fns_and_mtime = []
173 175
174 176 # If node2 in not the wc or there is >1 change, copy it
175 177 dir2root = ''
176 178 rev2 = ''
177 179 if node2:
178 180 dir2 = snapshot(ui, repo, modadd, node2, tmproot)[0]
179 181 rev2 = '@%d' % repo[node2].rev()
180 182 elif len(common) > 1:
181 183 #we only actually need to get the files to copy back to
182 184 #the working dir in this case (because the other cases
183 185 #are: diffing 2 revisions or single file -- in which case
184 186 #the file is already directly passed to the diff tool).
185 187 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot)
186 188 else:
187 189 # This lets the diff tool open the changed file directly
188 190 dir2 = ''
189 191 dir2root = repo.root
190 192
191 193 label1a = rev1a
192 194 label1b = rev1b
193 195 label2 = rev2
194 196
195 197 # If only one change, diff the files instead of the directories
196 198 # Handle bogus modifies correctly by checking if the files exist
197 199 if len(common) == 1:
198 200 common_file = util.localpath(common.pop())
199 201 dir1a = os.path.join(tmproot, dir1a, common_file)
200 202 label1a = common_file + rev1a
201 203 if not os.path.isfile(dir1a):
202 204 dir1a = os.devnull
203 205 if do3way:
204 206 dir1b = os.path.join(tmproot, dir1b, common_file)
205 207 label1b = common_file + rev1b
206 208 if not os.path.isfile(dir1b):
207 209 dir1b = os.devnull
208 210 dir2 = os.path.join(dir2root, dir2, common_file)
209 211 label2 = common_file + rev2
210 212
211 213 # Function to quote file/dir names in the argument string.
212 214 # When not operating in 3-way mode, an empty string is
213 215 # returned for parent2
214 216 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b,
215 217 'plabel1': label1a, 'plabel2': label1b,
216 218 'clabel': label2, 'child': dir2,
217 219 'root': repo.root}
218 220 def quote(match):
219 221 pre = match.group(2)
220 222 key = match.group(3)
221 223 if not do3way and key == 'parent2':
222 224 return pre
223 225 return pre + util.shellquote(replace[key])
224 226
225 227 # Match parent2 first, so 'parent1?' will match both parent1 and parent
226 228 regex = (r'''(['"]?)([^\s'"$]*)'''
227 229 r'\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1')
228 230 if not do3way and not re.search(regex, cmdline):
229 231 cmdline += ' $parent1 $child'
230 232 cmdline = re.sub(regex, quote, cmdline)
231 233
232 234 ui.debug('running %r in %s\n' % (cmdline, tmproot))
233 235 ui.system(cmdline, cwd=tmproot)
234 236
235 237 for copy_fn, working_fn, mtime in fns_and_mtime:
236 238 if os.lstat(copy_fn).st_mtime != mtime:
237 239 ui.debug('file changed while diffing. '
238 240 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
239 241 util.copyfile(copy_fn, working_fn)
240 242
241 243 return 1
242 244 finally:
243 245 ui.note(_('cleaning up temp directory\n'))
244 246 shutil.rmtree(tmproot)
245 247
246 248 @command('extdiff',
247 249 [('p', 'program', '',
248 250 _('comparison program to run'), _('CMD')),
249 251 ('o', 'option', [],
250 252 _('pass option to comparison program'), _('OPT')),
251 253 ('r', 'rev', [], _('revision'), _('REV')),
252 254 ('c', 'change', '', _('change made by revision'), _('REV')),
253 255 ] + commands.walkopts,
254 256 _('hg extdiff [OPT]... [FILE]...'),
255 257 inferrepo=True)
256 258 def extdiff(ui, repo, *pats, **opts):
257 259 '''use external program to diff repository (or selected files)
258 260
259 261 Show differences between revisions for the specified files, using
260 262 an external program. The default program used is diff, with
261 263 default options "-Npru".
262 264
263 265 To select a different program, use the -p/--program option. The
264 266 program will be passed the names of two directories to compare. To
265 267 pass additional options to the program, use -o/--option. These
266 268 will be passed before the names of the directories to compare.
267 269
268 270 When two revision arguments are given, then changes are shown
269 271 between those revisions. If only one revision is specified then
270 272 that revision is compared to the working directory, and, when no
271 273 revisions are specified, the working directory files are compared
272 274 to its parent.'''
273 275 program = opts.get('program')
274 276 option = opts.get('option')
275 277 if not program:
276 278 program = 'diff'
277 279 option = option or ['-Npru']
278 280 cmdline = ' '.join(map(util.shellquote, [program] + option))
279 281 return dodiff(ui, repo, cmdline, pats, opts)
280 282
281 283 def uisetup(ui):
282 284 for cmd, path in ui.configitems('extdiff'):
283 285 path = util.expandpath(path)
284 286 if cmd.startswith('cmd.'):
285 287 cmd = cmd[4:]
286 288 if not path:
287 289 path = util.findexe(cmd)
288 290 if path is None:
289 291 path = filemerge.findexternaltool(ui, cmd) or cmd
290 292 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
291 293 cmdline = util.shellquote(path)
292 294 if diffopts:
293 295 cmdline += ' ' + diffopts
294 296 elif cmd.startswith('opts.'):
295 297 continue
296 298 else:
297 299 if path:
298 300 # case "cmd = path opts"
299 301 cmdline = path
300 302 diffopts = len(shlex.split(cmdline)) > 1
301 303 else:
302 304 # case "cmd ="
303 305 path = util.findexe(cmd)
304 306 if path is None:
305 307 path = filemerge.findexternaltool(ui, cmd) or cmd
306 308 cmdline = util.shellquote(path)
307 309 diffopts = False
308 310 # look for diff arguments in [diff-tools] then [merge-tools]
309 311 if not diffopts:
310 312 args = ui.config('diff-tools', cmd+'.diffargs') or \
311 313 ui.config('merge-tools', cmd+'.diffargs')
312 314 if args:
313 315 cmdline += ' ' + args
314 316 def save(cmdline):
315 317 '''use closure to save diff command to use'''
316 318 def mydiff(ui, repo, *pats, **opts):
317 319 options = ' '.join(map(util.shellquote, opts['option']))
318 320 if options:
319 321 options = ' ' + options
320 322 return dodiff(ui, repo, cmdline + options, pats, opts)
321 323 doc = _('''\
322 324 use %(path)s to diff repository (or selected files)
323 325
324 326 Show differences between revisions for the specified files, using
325 327 the %(path)s program.
326 328
327 329 When two revision arguments are given, then changes are shown
328 330 between those revisions. If only one revision is specified then
329 331 that revision is compared to the working directory, and, when no
330 332 revisions are specified, the working directory files are compared
331 333 to its parent.\
332 334 ''') % {'path': util.uirepr(path)}
333 335
334 336 # We must translate the docstring right away since it is
335 337 # used as a format string. The string will unfortunately
336 338 # be translated again in commands.helpcmd and this will
337 339 # fail when the docstring contains non-ASCII characters.
338 340 # Decoding the string to a Unicode string here (using the
339 341 # right encoding) prevents that.
340 342 mydiff.__doc__ = doc.decode(encoding.encoding)
341 343 return mydiff
342 344 cmdtable[cmd] = (save(cmdline),
343 345 cmdtable['extdiff'][1][1:],
344 346 _('hg %s [OPTION]... [FILE]...') % cmd)
@@ -1,694 +1,708 b''
1 1 This file focuses mainly on updating largefiles in the working
2 2 directory (and ".hg/largefiles/dirstate")
3 3
4 4 $ cat >> $HGRCPATH <<EOF
5 5 > [ui]
6 6 > merge = internal:fail
7 7 > [extensions]
8 8 > largefiles =
9 9 > EOF
10 10
11 11 $ hg init repo
12 12 $ cd repo
13 13
14 14 $ echo large1 > large1
15 15 $ echo large2 > large2
16 16 $ hg add --large large1 large2
17 17 $ echo normal1 > normal1
18 18 $ hg add normal1
19 19 $ hg commit -m '#0'
20 20 $ echo 'large1 in #1' > large1
21 21 $ echo 'normal1 in #1' > normal1
22 22 $ hg commit -m '#1'
23 $ hg extdiff -r '.^' --config extensions.extdiff=
24 diff -Npru repo.0d9d9b8dc9a3/.hglf/large1 repo/.hglf/large1
25 --- repo.0d9d9b8dc9a3/.hglf/large1 * (glob)
26 +++ repo/.hglf/large1 * (glob)
27 @@ -1 +1 @@
28 -4669e532d5b2c093a78eca010077e708a071bb64
29 +58e24f733a964da346e2407a2bee99d9001184f5
30 diff -Npru repo.0d9d9b8dc9a3/normal1 repo/normal1
31 --- repo.0d9d9b8dc9a3/normal1 * (glob)
32 +++ repo/normal1 * (glob)
33 @@ -1 +1 @@
34 -normal1
35 +normal1 in #1
36 [1]
23 37 $ hg update -q -C 0
24 38 $ echo 'large2 in #2' > large2
25 39 $ hg commit -m '#2'
26 40 created new head
27 41
28 42 Test that update also updates the lfdirstate of 'unsure' largefiles after
29 43 hashing them:
30 44
31 45 The previous operations will usually have left us with largefiles with a mtime
32 46 within the same second as the dirstate was written.
33 47 The lfdirstate entries will thus have been written with an invalidated/unset
34 48 mtime to make sure further changes within the same second is detected.
35 49 We will however occasionally be "lucky" and get a tick between writing
36 50 largefiles and writing dirstate so we get valid lfdirstate timestamps. The
37 51 following verification is thus disabled but can be verified manually.
38 52
39 53 #if false
40 54 $ hg debugdirstate --large --nodate
41 55 n 644 7 unset large1
42 56 n 644 13 unset large2
43 57 #endif
44 58
45 59 Wait to make sure we get a tick so the mtime of the largefiles become valid.
46 60
47 61 $ sleep 1
48 62
49 63 A linear merge will update standins before performing the actual merge. It will
50 64 do a lfdirstate status walk and find 'unset'/'unsure' files, hash them, and
51 65 update the corresponding standins.
52 66 Verify that it actually marks the clean files as clean in lfdirstate so
53 67 we don't have to hash them again next time we update.
54 68
55 69 $ hg up
56 70 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 71 $ hg debugdirstate --large --nodate
58 72 n 644 7 set large1
59 73 n 644 13 set large2
60 74
61 75 Test that lfdirstate keeps track of last modification of largefiles and
62 76 prevents unnecessary hashing of content - also after linear/noop update
63 77
64 78 $ sleep 1
65 79 $ hg st
66 80 $ hg debugdirstate --large --nodate
67 81 n 644 7 set large1
68 82 n 644 13 set large2
69 83 $ hg up
70 84 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 85 $ hg debugdirstate --large --nodate
72 86 n 644 7 set large1
73 87 n 644 13 set large2
74 88
75 89 Test that "hg merge" updates largefiles from "other" correctly
76 90
77 91 (getting largefiles from "other" normally)
78 92
79 93 $ hg status -A large1
80 94 C large1
81 95 $ cat large1
82 96 large1
83 97 $ cat .hglf/large1
84 98 4669e532d5b2c093a78eca010077e708a071bb64
85 99 $ hg merge --config debug.dirstate.delaywrite=2
86 100 getting changed largefiles
87 101 1 largefiles updated, 0 removed
88 102 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 103 (branch merge, don't forget to commit)
90 104 $ hg status -A large1
91 105 M large1
92 106 $ cat large1
93 107 large1 in #1
94 108 $ cat .hglf/large1
95 109 58e24f733a964da346e2407a2bee99d9001184f5
96 110 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
97 111 -4669e532d5b2c093a78eca010077e708a071bb64
98 112 +58e24f733a964da346e2407a2bee99d9001184f5
99 113
100 114 (getting largefiles from "other" via conflict prompt)
101 115
102 116 $ hg update -q -C 2
103 117 $ echo 'large1 in #3' > large1
104 118 $ echo 'normal1 in #3' > normal1
105 119 $ hg commit -m '#3'
106 120 $ cat .hglf/large1
107 121 e5bb990443d6a92aaf7223813720f7566c9dd05b
108 122 $ hg merge --config debug.dirstate.delaywrite=2 --config ui.interactive=True <<EOF
109 123 > o
110 124 > EOF
111 125 largefile large1 has a merge conflict
112 126 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
113 127 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
114 128 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
115 129 merging normal1
116 130 warning: conflicts during merge.
117 131 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
118 132 getting changed largefiles
119 133 1 largefiles updated, 0 removed
120 134 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
121 135 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
122 136 [1]
123 137 $ hg status -A large1
124 138 M large1
125 139 $ cat large1
126 140 large1 in #1
127 141 $ cat .hglf/large1
128 142 58e24f733a964da346e2407a2bee99d9001184f5
129 143
130 144 Test that "hg revert -r REV" updates largefiles from "REV" correctly
131 145
132 146 $ hg update -q -C 3
133 147 $ hg status -A large1
134 148 C large1
135 149 $ cat large1
136 150 large1 in #3
137 151 $ cat .hglf/large1
138 152 e5bb990443d6a92aaf7223813720f7566c9dd05b
139 153 $ hg diff -c 1 --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
140 154 -4669e532d5b2c093a78eca010077e708a071bb64
141 155 +58e24f733a964da346e2407a2bee99d9001184f5
142 156 $ hg revert --no-backup -r 1 --config debug.dirstate.delaywrite=2 large1
143 157 $ hg status -A large1
144 158 M large1
145 159 $ cat large1
146 160 large1 in #1
147 161 $ cat .hglf/large1
148 162 58e24f733a964da346e2407a2bee99d9001184f5
149 163
150 164 Test that "hg rollback" restores status of largefiles correctly
151 165
152 166 $ hg update -C -q
153 167 $ hg remove large1
154 168 $ test -f .hglf/large1
155 169 [1]
156 170 $ hg forget large2
157 171 $ test -f .hglf/large2
158 172 [1]
159 173 $ echo largeX > largeX
160 174 $ hg add --large largeX
161 175 $ cat .hglf/largeX
162 176
163 177 $ hg commit -m 'will be rollback-ed soon'
164 178 $ echo largeY > largeY
165 179 $ hg add --large largeY
166 180 #if windows
167 181 $ hg status -A large1
168 182 large1: * (glob)
169 183 #else
170 184 $ hg status -A large1
171 185 large1: No such file or directory
172 186 #endif
173 187 $ hg status -A large2
174 188 ? large2
175 189 $ hg status -A largeX
176 190 C largeX
177 191 $ hg status -A largeY
178 192 A largeY
179 193 $ hg rollback
180 194 repository tip rolled back to revision 3 (undo commit)
181 195 working directory now based on revision 3
182 196 $ hg status -A large1
183 197 R large1
184 198 $ test -f .hglf/large1
185 199 [1]
186 200 $ hg status -A large2
187 201 R large2
188 202 $ test -f .hglf/large2
189 203 [1]
190 204 $ hg status -A largeX
191 205 A largeX
192 206 $ cat .hglf/largeX
193 207
194 208 $ hg status -A largeY
195 209 ? largeY
196 210 $ test -f .hglf/largeY
197 211 [1]
198 212
199 213 Test that "hg rollback" restores standins correctly
200 214
201 215 $ hg commit -m 'will be rollback-ed soon'
202 216 $ hg update -q -C 2
203 217 $ cat large1
204 218 large1
205 219 $ cat .hglf/large1
206 220 4669e532d5b2c093a78eca010077e708a071bb64
207 221 $ cat large2
208 222 large2 in #2
209 223 $ cat .hglf/large2
210 224 3cfce6277e7668985707b6887ce56f9f62f6ccd9
211 225
212 226 $ hg rollback -q -f
213 227 $ cat large1
214 228 large1
215 229 $ cat .hglf/large1
216 230 4669e532d5b2c093a78eca010077e708a071bb64
217 231 $ cat large2
218 232 large2 in #2
219 233 $ cat .hglf/large2
220 234 3cfce6277e7668985707b6887ce56f9f62f6ccd9
221 235
222 236 (rollback the parent of the working directory, when the parent of it
223 237 is not branch-tip)
224 238
225 239 $ hg update -q -C 1
226 240 $ cat .hglf/large1
227 241 58e24f733a964da346e2407a2bee99d9001184f5
228 242 $ cat .hglf/large2
229 243 1deebade43c8c498a3c8daddac0244dc55d1331d
230 244
231 245 $ echo normalX > normalX
232 246 $ hg add normalX
233 247 $ hg commit -m 'will be rollback-ed soon'
234 248 $ hg rollback -q
235 249
236 250 $ cat .hglf/large1
237 251 58e24f733a964da346e2407a2bee99d9001184f5
238 252 $ cat .hglf/large2
239 253 1deebade43c8c498a3c8daddac0244dc55d1331d
240 254
241 255 Test that "hg status" shows status of largefiles correctly just after
242 256 automated commit like rebase/transplant
243 257
244 258 $ cat >> .hg/hgrc <<EOF
245 259 > [extensions]
246 260 > rebase =
247 261 > strip =
248 262 > transplant =
249 263 > EOF
250 264 $ hg update -q -C 1
251 265 $ hg remove large1
252 266 $ echo largeX > largeX
253 267 $ hg add --large largeX
254 268 $ hg commit -m '#4'
255 269
256 270 $ hg rebase -s 1 -d 2 --keep
257 271 rebasing 1:72518492caa6 "#1"
258 272 rebasing 4:07d6153b5c04 "#4" (tip)
259 273 #if windows
260 274 $ hg status -A large1
261 275 large1: * (glob)
262 276 #else
263 277 $ hg status -A large1
264 278 large1: No such file or directory
265 279 #endif
266 280 $ hg status -A largeX
267 281 C largeX
268 282 $ hg strip -q 5
269 283
270 284 $ hg update -q -C 2
271 285 $ hg transplant -q 1 4
272 286 #if windows
273 287 $ hg status -A large1
274 288 large1: * (glob)
275 289 #else
276 290 $ hg status -A large1
277 291 large1: No such file or directory
278 292 #endif
279 293 $ hg status -A largeX
280 294 C largeX
281 295 $ hg strip -q 5
282 296
283 297 $ hg update -q -C 2
284 298 $ hg transplant -q --merge 1 --merge 4
285 299 #if windows
286 300 $ hg status -A large1
287 301 large1: * (glob)
288 302 #else
289 303 $ hg status -A large1
290 304 large1: No such file or directory
291 305 #endif
292 306 $ hg status -A largeX
293 307 C largeX
294 308 $ hg strip -q 5
295 309
296 310 Test that linear merge can detect modification (and conflict) correctly
297 311
298 312 (linear merge without conflict)
299 313
300 314 $ echo 'large2 for linear merge (no conflict)' > large2
301 315 $ hg update 3 --config debug.dirstate.delaywrite=2
302 316 getting changed largefiles
303 317 1 largefiles updated, 0 removed
304 318 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
305 319 $ hg status -A large2
306 320 M large2
307 321 $ cat large2
308 322 large2 for linear merge (no conflict)
309 323 $ cat .hglf/large2
310 324 9c4bf8f1b33536d6e5f89447e10620cfe52ea710
311 325
312 326 (linear merge with conflict, choosing "other")
313 327
314 328 $ hg update -q -C 2
315 329 $ echo 'large1 for linear merge (conflict)' > large1
316 330 $ hg update 3 --config ui.interactive=True <<EOF
317 331 > o
318 332 > EOF
319 333 largefile large1 has a merge conflict
320 334 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
321 335 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
322 336 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? o
323 337 getting changed largefiles
324 338 1 largefiles updated, 0 removed
325 339 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
326 340 $ hg status -A large1
327 341 C large1
328 342 $ cat large1
329 343 large1 in #3
330 344 $ cat .hglf/large1
331 345 e5bb990443d6a92aaf7223813720f7566c9dd05b
332 346
333 347 (linear merge with conflict, choosing "local")
334 348
335 349 $ hg update -q -C 2
336 350 $ echo 'large1 for linear merge (conflict)' > large1
337 351 $ hg update 3 --config debug.dirstate.delaywrite=2
338 352 largefile large1 has a merge conflict
339 353 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
340 354 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
341 355 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
342 356 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
343 357 $ hg status -A large1
344 358 M large1
345 359 $ cat large1
346 360 large1 for linear merge (conflict)
347 361 $ cat .hglf/large1
348 362 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
349 363
350 364 Test a linear merge to a revision containing same-name normal file
351 365
352 366 $ hg update -q -C 3
353 367 $ hg remove large2
354 368 $ echo 'large2 as normal file' > large2
355 369 $ hg add large2
356 370 $ echo 'large3 as normal file' > large3
357 371 $ hg add large3
358 372 $ hg commit -m '#5'
359 373 $ hg manifest
360 374 .hglf/large1
361 375 large2
362 376 large3
363 377 normal1
364 378
365 379 (modified largefile is already switched to normal)
366 380
367 381 $ hg update -q -C 2
368 382 $ echo 'modified large2 for linear merge' > large2
369 383 $ hg update -q 5
370 384 remote turned local largefile large2 into a normal file
371 385 keep (l)argefile or use (n)ormal file? l
372 386 $ hg debugdirstate --nodates | grep large2
373 387 a 0 -1 unset .hglf/large2
374 388 r 0 0 set large2
375 389 $ hg status -A large2
376 390 A large2
377 391 $ cat large2
378 392 modified large2 for linear merge
379 393
380 394 (added largefile is already committed as normal)
381 395
382 396 $ hg update -q -C 2
383 397 $ echo 'large3 as large file for linear merge' > large3
384 398 $ hg add --large large3
385 399 $ hg update -q 5
386 400 remote turned local largefile large3 into a normal file
387 401 keep (l)argefile or use (n)ormal file? l
388 402 $ hg debugdirstate --nodates | grep large3
389 403 a 0 -1 unset .hglf/large3
390 404 r 0 0 set large3
391 405 $ hg status -A large3
392 406 A large3
393 407 $ cat large3
394 408 large3 as large file for linear merge
395 409 $ rm -f large3 .hglf/large3
396 410
397 411 Test that the internal linear merging works correctly
398 412 (both heads are stripped to keep pairing of revision number and commit log)
399 413
400 414 $ hg update -q -C 2
401 415 $ hg strip 3 4
402 416 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9530e27857f7-2e7b195d-backup.hg (glob)
403 417 $ mv .hg/strip-backup/9530e27857f7-2e7b195d-backup.hg $TESTTMP
404 418
405 419 (internal linear merging at "hg pull --update")
406 420
407 421 $ echo 'large1 for linear merge (conflict)' > large1
408 422 $ echo 'large2 for linear merge (conflict with normal file)' > large2
409 423 $ hg pull --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-2e7b195d-backup.hg
410 424 pulling from $TESTTMP/9530e27857f7-2e7b195d-backup.hg (glob)
411 425 searching for changes
412 426 adding changesets
413 427 adding manifests
414 428 adding file changes
415 429 added 3 changesets with 5 changes to 5 files
416 430 remote turned local largefile large2 into a normal file
417 431 keep (l)argefile or use (n)ormal file? l
418 432 largefile large1 has a merge conflict
419 433 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
420 434 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
421 435 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
422 436 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
423 437
424 438 $ hg status -A large1
425 439 M large1
426 440 $ cat large1
427 441 large1 for linear merge (conflict)
428 442 $ cat .hglf/large1
429 443 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
430 444 $ hg status -A large2
431 445 A large2
432 446 $ cat large2
433 447 large2 for linear merge (conflict with normal file)
434 448 $ cat .hglf/large2
435 449 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
436 450
437 451 (internal linear merging at "hg unbundle --update")
438 452
439 453 $ hg update -q -C 2
440 454 $ hg rollback -q
441 455
442 456 $ echo 'large1 for linear merge (conflict)' > large1
443 457 $ echo 'large2 for linear merge (conflict with normal file)' > large2
444 458 $ hg unbundle --update --config debug.dirstate.delaywrite=2 $TESTTMP/9530e27857f7-2e7b195d-backup.hg
445 459 adding changesets
446 460 adding manifests
447 461 adding file changes
448 462 added 3 changesets with 5 changes to 5 files
449 463 remote turned local largefile large2 into a normal file
450 464 keep (l)argefile or use (n)ormal file? l
451 465 largefile large1 has a merge conflict
452 466 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
453 467 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
454 468 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
455 469 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
456 470
457 471 $ hg status -A large1
458 472 M large1
459 473 $ cat large1
460 474 large1 for linear merge (conflict)
461 475 $ cat .hglf/large1
462 476 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
463 477 $ hg status -A large2
464 478 A large2
465 479 $ cat large2
466 480 large2 for linear merge (conflict with normal file)
467 481 $ cat .hglf/large2
468 482 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
469 483
470 484 (internal linear merging in subrepo at "hg update")
471 485
472 486 $ cd ..
473 487 $ hg init subparent
474 488 $ cd subparent
475 489
476 490 $ hg clone -q -u 2 ../repo sub
477 491 $ cat > .hgsub <<EOF
478 492 > sub = sub
479 493 > EOF
480 494 $ hg add .hgsub
481 495 $ hg commit -m '#0@parent'
482 496 $ cat .hgsubstate
483 497 f74e50bd9e5594b7cf1e6c5cbab86ddd25f3ca2f sub
484 498 $ hg -R sub update -q
485 499 $ hg commit -m '#1@parent'
486 500 $ cat .hgsubstate
487 501 d65e59e952a9638e2ce863b41a420ca723dd3e8d sub
488 502 $ hg update -q 0
489 503
490 504 $ echo 'large1 for linear merge (conflict)' > sub/large1
491 505 $ echo 'large2 for linear merge (conflict with normal file)' > sub/large2
492 506 $ hg update --config ui.interactive=True --config debug.dirstate.delaywrite=2 <<EOF
493 507 > m
494 508 > r
495 509 > l
496 510 > l
497 511 > EOF
498 512 subrepository sub diverged (local revision: f74e50bd9e55, remote revision: d65e59e952a9)
499 513 (M)erge, keep (l)ocal or keep (r)emote? m
500 514 subrepository sources for sub differ (in checked out version)
501 515 use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9)? r
502 516 remote turned local largefile large2 into a normal file
503 517 keep (l)argefile or use (n)ormal file? l
504 518 largefile large1 has a merge conflict
505 519 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
506 520 keep (l)ocal ba94c2efe5b7c5e0af8d189295ce00553b0612b7 or
507 521 take (o)ther e5bb990443d6a92aaf7223813720f7566c9dd05b? l
508 522 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
509 523 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
510 524
511 525 $ hg -R sub status -A sub/large1
512 526 M sub/large1
513 527 $ cat sub/large1
514 528 large1 for linear merge (conflict)
515 529 $ cat sub/.hglf/large1
516 530 ba94c2efe5b7c5e0af8d189295ce00553b0612b7
517 531 $ hg -R sub status -A sub/large2
518 532 A sub/large2
519 533 $ cat sub/large2
520 534 large2 for linear merge (conflict with normal file)
521 535 $ cat sub/.hglf/large2
522 536 d7591fe9be0f6227d90bddf3e4f52ff41fc1f544
523 537
524 538 $ cd ..
525 539 $ cd repo
526 540
527 541 Test that rebase updates largefiles in the working directory even if
528 542 it is aborted by conflict.
529 543
530 544 $ hg update -q -C 3
531 545 $ cat .hglf/large1
532 546 e5bb990443d6a92aaf7223813720f7566c9dd05b
533 547 $ cat large1
534 548 large1 in #3
535 549 $ hg rebase -s 1 -d 3 --keep --config ui.interactive=True <<EOF
536 550 > o
537 551 > EOF
538 552 rebasing 1:72518492caa6 "#1"
539 553 largefile large1 has a merge conflict
540 554 ancestor was 4669e532d5b2c093a78eca010077e708a071bb64
541 555 keep (l)ocal e5bb990443d6a92aaf7223813720f7566c9dd05b or
542 556 take (o)ther 58e24f733a964da346e2407a2bee99d9001184f5? o
543 557 merging normal1
544 558 warning: conflicts during merge.
545 559 merging normal1 incomplete! (edit conflicts, then use 'hg resolve --mark')
546 560 unresolved conflicts (see hg resolve, then hg rebase --continue)
547 561 [1]
548 562 $ cat .hglf/large1
549 563 58e24f733a964da346e2407a2bee99d9001184f5
550 564 $ cat large1
551 565 large1 in #1
552 566
553 567 Test that rebase updates standins for manually modified largefiles at
554 568 the 1st commit of resuming.
555 569
556 570 $ echo "manually modified before 'hg rebase --continue'" > large1
557 571 $ hg resolve -m normal1
558 572 (no more unresolved files)
559 573 $ hg rebase --continue --config ui.interactive=True <<EOF
560 574 > c
561 575 > EOF
562 576 rebasing 1:72518492caa6 "#1"
563 577 rebasing 4:07d6153b5c04 "#4"
564 578 local changed .hglf/large1 which remote deleted
565 579 use (c)hanged version or (d)elete? c
566 580
567 581 $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
568 582 -e5bb990443d6a92aaf7223813720f7566c9dd05b
569 583 +8a4f783556e7dea21139ca0466eafce954c75c13
570 584 $ rm -f large1
571 585 $ hg update -q -C tip
572 586 $ cat large1
573 587 manually modified before 'hg rebase --continue'
574 588
575 589 Test that transplant updates largefiles, of which standins are safely
576 590 changed, even if it is aborted by conflict of other.
577 591
578 592 $ hg update -q -C 5
579 593 $ cat .hglf/large1
580 594 e5bb990443d6a92aaf7223813720f7566c9dd05b
581 595 $ cat large1
582 596 large1 in #3
583 597 $ hg diff -c 4 .hglf/largeX | grep '^[+-][0-9a-z]'
584 598 +fa44618ea25181aff4f48b70428294790cec9f61
585 599 $ hg transplant 4
586 600 applying 07d6153b5c04
587 601 patching file .hglf/large1
588 602 Hunk #1 FAILED at 0
589 603 1 out of 1 hunks FAILED -- saving rejects to file .hglf/large1.rej
590 604 patch failed to apply
591 605 abort: fix up the merge and run hg transplant --continue
592 606 [255]
593 607 $ hg status -A large1
594 608 C large1
595 609 $ cat .hglf/large1
596 610 e5bb990443d6a92aaf7223813720f7566c9dd05b
597 611 $ cat large1
598 612 large1 in #3
599 613 $ hg status -A largeX
600 614 A largeX
601 615 $ cat .hglf/largeX
602 616 fa44618ea25181aff4f48b70428294790cec9f61
603 617 $ cat largeX
604 618 largeX
605 619
606 620 Test that transplant updates standins for manually modified largefiles
607 621 at the 1st commit of resuming.
608 622
609 623 $ echo "manually modified before 'hg transplant --continue'" > large1
610 624 $ hg transplant --continue
611 625 07d6153b5c04 transplanted as f1bf30eb88cc
612 626 $ hg diff -c tip .hglf/large1 | grep '^[+-][0-9a-z]'
613 627 -e5bb990443d6a92aaf7223813720f7566c9dd05b
614 628 +6a4f36d4075fbe0f30ec1d26ca44e63c05903671
615 629 $ rm -f large1
616 630 $ hg update -q -C tip
617 631 $ cat large1
618 632 manually modified before 'hg transplant --continue'
619 633
620 634 Test that "hg status" doesn't show removal of largefiles not managed
621 635 in the target context.
622 636
623 637 $ hg update -q -C 4
624 638 $ hg remove largeX
625 639 $ hg status -A largeX
626 640 R largeX
627 641 $ hg status -A --rev '.^1' largeX
628 642
629 643 #if execbit
630 644
631 645 Test that "hg status" against revisions other than parent notices exec
632 646 bit changes of largefiles.
633 647
634 648 $ hg update -q -C 4
635 649
636 650 (the case that large2 doesn't have exec bit in the target context but
637 651 in the working context)
638 652
639 653 $ chmod +x large2
640 654 $ hg status -A --rev 0 large2
641 655 M large2
642 656 $ hg commit -m 'chmod +x large2'
643 657
644 658 (the case that large2 has exec bit in the target context but not in
645 659 the working context)
646 660
647 661 $ echo dummy > dummy
648 662 $ hg add dummy
649 663 $ hg commit -m 'revision for separation'
650 664 $ chmod -x large2
651 665 $ hg status -A --rev '.^1' large2
652 666 M large2
653 667
654 668 #else
655 669
656 670 Test that "hg status" against revisions other than parent ignores exec
657 671 bit correctly on the platform being unaware of it.
658 672
659 673 $ hg update -q -C 4
660 674
661 675 $ cat > exec-bit.patch <<EOF
662 676 > # HG changeset patch
663 677 > # User test
664 678 > # Date 0 0
665 679 > # Thu Jan 01 00:00:00 1970 +0000
666 680 > # Node ID be1b433a65b12b27b5519d92213e14f7e1769b90
667 681 > # Parent 07d6153b5c04313efb75deec9ba577de7faeb727
668 682 > chmod +x large2
669 683 >
670 684 > diff --git a/.hglf/large2 b/.hglf/large2
671 685 > old mode 100644
672 686 > new mode 100755
673 687 > EOF
674 688 $ hg import --exact --bypass exec-bit.patch
675 689 applying exec-bit.patch
676 690 $ hg status -A --rev tip large2
677 691 C large2
678 692
679 693 #endif
680 694
681 695 $ cd ..
682 696
683 697 Test that "hg convert" avoids copying largefiles from the working
684 698 directory into store, because "hg convert" doesn't update largefiles
685 699 in the working directory (removing files under ".cache/largefiles"
686 700 forces "hg convert" to copy corresponding largefiles)
687 701
688 702 $ cat >> $HGRCPATH <<EOF
689 703 > [extensions]
690 704 > convert =
691 705 > EOF
692 706
693 707 $ rm $TESTTMP/.cache/largefiles/6a4f36d4075fbe0f30ec1d26ca44e63c05903671
694 708 $ hg convert -q repo repo.converted
General Comments 0
You need to be logged in to leave comments. Login now