Mercurial > hg
comparison hgext/extdiff.py @ 32217:affd753ddaf1
extdiff: copy back files to the working directory if the size changed
In theory, it should be enough to pay attention only to the modification time
when detecting if a snapshotted working directory file changed. In practice,
BeyondCompare preserves all file attributes when syncing files at the directory
level. (If you open the file and sync individual hunks, then mtime does change,
and everything was being copied back as desired.) I'm not sure how many other
synchronization tools would trigger this issue, but it's annoyingly inconsistent
(if a single file is diffed, it isn't snapshotted, so the same BeyondCompare
file sync operation _is_ visible, because wdir() is updated in place.
I filed a bug with them, and they stated it is on their wish list, but won't be
fixed in the near term. This isn't a complete fix (there is still the case of
the size not changing), but this seems like a trivial enough change to fix most
of the problem. I suppose we could fool around with making files in the other
snapshot readonly, and copy back if we see the readonly bit copied. That seems
pretty hacky though, and only works if the external tool copies all attributes.
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Sat, 06 May 2017 23:00:57 -0400 |
parents | 53865692a354 |
children | 8a1ff5ed620e |
comparison
equal
deleted
inserted
replaced
32216:98bb992bef19 | 32217:affd753ddaf1 |
---|---|
99 dirname = "root" | 99 dirname = "root" |
100 if node is not None: | 100 if node is not None: |
101 dirname = '%s.%s' % (dirname, short(node)) | 101 dirname = '%s.%s' % (dirname, short(node)) |
102 base = os.path.join(tmproot, dirname) | 102 base = os.path.join(tmproot, dirname) |
103 os.mkdir(base) | 103 os.mkdir(base) |
104 fns_and_mtime = [] | 104 fnsandstat = [] |
105 | 105 |
106 if node is not None: | 106 if node is not None: |
107 ui.note(_('making snapshot of %d files from rev %s\n') % | 107 ui.note(_('making snapshot of %d files from rev %s\n') % |
108 (len(files), short(node))) | 108 (len(files), short(node))) |
109 else: | 109 else: |
122 ui.note(' %s\n' % wfn) | 122 ui.note(' %s\n' % wfn) |
123 | 123 |
124 if node is None: | 124 if node is None: |
125 dest = os.path.join(base, wfn) | 125 dest = os.path.join(base, wfn) |
126 | 126 |
127 fns_and_mtime.append((dest, repo.wjoin(fn), | 127 fnsandstat.append((dest, repo.wjoin(fn), os.lstat(dest))) |
128 os.lstat(dest).st_mtime)) | 128 return dirname, fnsandstat |
129 return dirname, fns_and_mtime | |
130 | 129 |
131 def dodiff(ui, repo, cmdline, pats, opts): | 130 def dodiff(ui, repo, cmdline, pats, opts): |
132 '''Do the actual diff: | 131 '''Do the actual diff: |
133 | 132 |
134 - copy to a temp structure if diffing 2 internal revisions | 133 - copy to a temp structure if diffing 2 internal revisions |
197 rev1b = '@%d' % repo[node1b].rev() | 196 rev1b = '@%d' % repo[node1b].rev() |
198 else: | 197 else: |
199 dir1b = None | 198 dir1b = None |
200 rev1b = '' | 199 rev1b = '' |
201 | 200 |
202 fns_and_mtime = [] | 201 fnsandstat = [] |
203 | 202 |
204 # If node2 in not the wc or there is >1 change, copy it | 203 # If node2 in not the wc or there is >1 change, copy it |
205 dir2root = '' | 204 dir2root = '' |
206 rev2 = '' | 205 rev2 = '' |
207 if node2: | 206 if node2: |
210 elif len(common) > 1: | 209 elif len(common) > 1: |
211 #we only actually need to get the files to copy back to | 210 #we only actually need to get the files to copy back to |
212 #the working dir in this case (because the other cases | 211 #the working dir in this case (because the other cases |
213 #are: diffing 2 revisions or single file -- in which case | 212 #are: diffing 2 revisions or single file -- in which case |
214 #the file is already directly passed to the diff tool). | 213 #the file is already directly passed to the diff tool). |
215 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot, | 214 dir2, fnsandstat = snapshot(ui, repo, modadd, None, tmproot, |
216 subrepos) | 215 subrepos) |
217 else: | 216 else: |
218 # This lets the diff tool open the changed file directly | 217 # This lets the diff tool open the changed file directly |
219 dir2 = '' | 218 dir2 = '' |
220 dir2root = repo.root | 219 dir2root = repo.root |
221 | 220 |
247 label2 = cmdutil.makefilename(repo, template, node2) | 246 label2 = cmdutil.makefilename(repo, template, node2) |
248 dir1a = repo.vfs.reljoin(tmproot, label1a) | 247 dir1a = repo.vfs.reljoin(tmproot, label1a) |
249 dir2 = repo.vfs.reljoin(tmproot, label2) | 248 dir2 = repo.vfs.reljoin(tmproot, label2) |
250 dir1b = None | 249 dir1b = None |
251 label1b = None | 250 label1b = None |
252 fns_and_mtime = [] | 251 fnsandstat = [] |
253 | 252 |
254 # Function to quote file/dir names in the argument string. | 253 # Function to quote file/dir names in the argument string. |
255 # When not operating in 3-way mode, an empty string is | 254 # When not operating in 3-way mode, an empty string is |
256 # returned for parent2 | 255 # returned for parent2 |
257 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b, | 256 replace = {'parent': dir1a, 'parent1': dir1a, 'parent2': dir1b, |
273 cmdline = re.sub(regex, quote, cmdline) | 272 cmdline = re.sub(regex, quote, cmdline) |
274 | 273 |
275 ui.debug('running %r in %s\n' % (cmdline, tmproot)) | 274 ui.debug('running %r in %s\n' % (cmdline, tmproot)) |
276 ui.system(cmdline, cwd=tmproot, blockedtag='extdiff') | 275 ui.system(cmdline, cwd=tmproot, blockedtag='extdiff') |
277 | 276 |
278 for copy_fn, working_fn, mtime in fns_and_mtime: | 277 for copy_fn, working_fn, st in fnsandstat: |
279 if os.lstat(copy_fn).st_mtime != mtime: | 278 cpstat = os.lstat(copy_fn) |
279 # Some tools copy the file and attributes, so mtime may not detect | |
280 # all changes. A size check will detect more cases, but not all. | |
281 # The only certain way to detect every case is to diff all files, | |
282 # which could be expensive. | |
283 if cpstat.st_mtime != st.st_mtime or cpstat.st_size != st.st_size: | |
280 ui.debug('file changed while diffing. ' | 284 ui.debug('file changed while diffing. ' |
281 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn)) | 285 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn)) |
282 util.copyfile(copy_fn, working_fn) | 286 util.copyfile(copy_fn, working_fn) |
283 | 287 |
284 return 1 | 288 return 1 |