def debuglfsdownload(ui, repo, *pats, **opts): """calculate the LFS download size when updating between REV1 and REV2 If --no-sparse is provided, this operation would ignore any sparse profile that might be present and report data for the full checkout. With -v also prints which files are to be downloaded and the size of each file.""" revs = opts.get("rev") node1, node2 = scmutil.revpair(repo, revs) match = lambda s: True if not opts.get("sparse"): ui.debug("will ignore sparse profile in this repo\n") else: if not util.safehasattr(repo, "sparsematch"): raise error.Abort( _("--ignore-sparse makes no sense in a non-sparse" " repository") ) match = repo.sparsematch(node2) with ui.configoverride({("remotefilelog", "dolfsprefetch"): False}): ctx1, ctx2 = repo[node1], repo[node2] mfdiff = ctx2.manifest().diff(ctx1.manifest()) lfsflogs = util.sortdict() # LFS filelogs for fname in mfdiff: if not match(fname): continue flog = repo.file(fname) try: node = ctx2.filenode(fname) except error.ManifestLookupError: continue if wrapper._islfs(flog, node=node): lfsflogs[fname] = flog totalsize = 0 presentsize = 0 for fname, flog in lfsflogs.items(): rawtext = flog.revision(ctx2.filenode(fname), raw=True) p = pointer.deserialize(rawtext) present = repo.svfs.lfslocalblobstore.has(p.oid()) lfssize = int(p["size"]) ui.note(_("%s: %i (present=%r)\n") % (fname, lfssize, present)) totalsize += lfssize presentsize += lfssize if present else 0 ui.status( _("Total size: %i, to download: %i, already exists: %r\n") % (totalsize, totalsize - presentsize, presentsize) )
def autodiff(ui, repo, *pats, **opts): diffopts = patch.difffeatureopts(ui, opts) git = opts.get("git", "no") brokenfiles = set() losedatafn = None if git in ("yes", "no"): diffopts.git = git == "yes" diffopts.upgrade = False elif git == "auto": diffopts.git = False diffopts.upgrade = True elif git == "warn": diffopts.git = False diffopts.upgrade = True def losedatafn(fn=None, **kwargs): brokenfiles.add(fn) return True elif git == "abort": diffopts.git = False diffopts.upgrade = True def losedatafn(fn=None, **kwargs): raise error.Abort("losing data for %s" % fn) else: raise error.Abort("--git must be yes, no or auto") node1, node2 = scmutil.revpair(repo, []) m = scmutil.match(repo[node2], pats, opts) it = patch.diff(repo, repo[node1], repo[node2], match=m, opts=diffopts, losedatafn=losedatafn) for chunk in it: ui.writebytes(chunk) for fn in sorted(brokenfiles): ui.write(("data lost for: %s\n" % fn))
def dodiff(ui, repo, cmdline, pats, opts): """Do the actual diff: - copy to a temp structure if diffing 2 internal revisions - copy to a temp structure if diffing working revision with another one and more than 1 file is changed - just invoke the diff for a single file in the working dir """ revs = opts.get("rev") change = opts.get("change") do3way = "$parent2" in cmdline if revs and change: msg = _("cannot specify --rev and --change at the same time") raise error.Abort(msg) elif change: node2 = scmutil.revsingle(repo, change, None).node() node1a, node1b = repo.changelog.parents(node2) else: node1a, node2 = scmutil.revpair(repo, revs) if not revs: node1b = repo.dirstate.p2() else: node1b = nullid # Disable 3-way merge if there is only one parent if do3way: if node1b == nullid: do3way = False matcher = scmutil.match(repo[node2], pats, opts) if opts.get("patch"): if node2 is None: raise error.Abort(_("--patch requires two revisions")) else: mod_a, add_a, rem_a = list(map(set, repo.status(node1a, node2, matcher)[:3])) if do3way: mod_b, add_b, rem_b = list( map(set, repo.status(node1b, node2, matcher)[:3]) ) else: mod_b, add_b, rem_b = set(), set(), set() modadd = mod_a | add_a | mod_b | add_b common = modadd | rem_a | rem_b if not common: return 0 tmproot = tempfile.mkdtemp(prefix="extdiff.") try: if not opts.get("patch"): # Always make a copy of node1a (and node1b, if applicable) dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a) dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot)[0] rev1a = "@%d" % repo[node1a].rev() if do3way: dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b) dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot)[0] rev1b = "@%d" % repo[node1b].rev() else: dir1b = None rev1b = "" fnsandstat = [] # If node2 in not the wc or there is >1 change, copy it dir2root = "" rev2 = "" if node2: dir2 = snapshot(ui, repo, modadd, node2, tmproot)[0] rev2 = "@%d" % repo[node2].rev() elif len(common) > 1: # we only actually need to get the files to copy back to # the working dir in this case (because the other cases # are: diffing 2 revisions or single file -- in which case # the file is already directly passed to the diff tool). dir2, fnsandstat = snapshot(ui, repo, modadd, None, tmproot) else: # This lets the diff tool open the changed file directly dir2 = "" dir2root = repo.root label1a = rev1a label1b = rev1b label2 = rev2 # If only one change, diff the files instead of the directories # Handle bogus modifies correctly by checking if the files exist if len(common) == 1: common_file = util.localpath(common.pop()) dir1a = os.path.join(tmproot, dir1a, common_file) label1a = common_file + rev1a if not os.path.isfile(dir1a): dir1a = os.devnull if do3way: dir1b = os.path.join(tmproot, dir1b, common_file) label1b = common_file + rev1b if not os.path.isfile(dir1b): dir1b = os.devnull dir2 = os.path.join(dir2root, dir2, common_file) label2 = common_file + rev2 else: template = "hg-%h.patch" cmdutil.export( repo, [repo[node1a].rev(), repo[node2].rev()], fntemplate=repo.localvfs.reljoin(tmproot, template), match=matcher, ) label1a = cmdutil.makefilename(repo, template, node1a) label2 = cmdutil.makefilename(repo, template, node2) dir1a = repo.localvfs.reljoin(tmproot, label1a) dir2 = repo.localvfs.reljoin(tmproot, label2) dir1b = None label1b = None fnsandstat = [] # Function to quote file/dir names in the argument string. # When not operating in 3-way mode, an empty string is # returned for parent2 replace = { "parent": dir1a, "parent1": dir1a, "parent2": dir1b, "plabel1": label1a, "plabel2": label1b, "clabel": label2, "child": dir2, "root": repo.root, } def quote(match): pre = match.group(2) key = match.group(3) if not do3way and key == "parent2": return pre return pre + util.shellquote(replace[key]) # Match parent2 first, so 'parent1?' will match both parent1 and parent regex = ( r"""(['"]?)([^\s'"$]*)""" r"\$(parent2|parent1?|child|plabel1|plabel2|clabel|root)\1" ) if not do3way and not re.search(regex, cmdline): cmdline += " $parent1 $child" cmdline = re.sub(regex, quote, cmdline) ui.debug("running %r in %s\n" % (cmdline, tmproot)) ui.system(cmdline, cwd=tmproot, blockedtag="extdiff") for copy_fn, working_fn, st in fnsandstat: cpstat = util.lstat(copy_fn) # Some tools copy the file and attributes, so mtime may not detect # all changes. A size check will detect more cases, but not all. # The only certain way to detect every case is to diff all files, # which could be expensive. # copyfile() carries over the permission, so the mode check could # be in an 'elif' branch, but for the case where the file has # changed without affecting mtime or size. if ( cpstat.st_mtime != st.st_mtime or cpstat.st_size != st.st_size or (cpstat.st_mode & 0o100) != (st.st_mode & 0o100) ): ui.debug( "file changed while diffing. " "Overwriting: %s (src: %s)\n" % (working_fn, copy_fn) ) util.copyfile(copy_fn, working_fn) return 1 finally: ui.note(_("cleaning up temp directory\n")) shutil.rmtree(tmproot)