def applydiff(name): files = {} patch.patch(self.join(patchfile), self.ui, strip=1, files=files) files2 = {} for k in files.keys(): files2[k.strip('\r')]=files[k] patch.updatedir(self.ui, repo, files2, similarity=sim/100.)
def _applypatch(self, repo, patchfile, sim, force=False, **opts): """applies a patch the old fashioned way.""" def epwrapper(orig, *epargs, **epopts): if opts['reverse']: epargs[1].append('-R') return orig(*epargs, **epopts) def adwrapper(orig, *adargs, **adopts): if opts['reverse']: adopts['reverse'] = True return orig(*adargs, **adopts) epo = extensions.wrapfunction(patch, 'externalpatch', epwrapper) ado = extensions.wrapfunction(patch, 'applydiff', adwrapper) files, success = {}, True try: try: fuzz = patch.patch(self.join(patchfile), self.ui, strip = 1, cwd = repo.root, files = files) patch.updatedir(self.ui, repo, files, similarity = sim/100.) except Exception, inst: self.ui.note(str(inst) + '\n') if not self.ui.verbose: self.ui.warn('patch failed, unable to continue (try -v)\n') success = False finally: patch.externalpatch = epo patch.applydiff = ado return success
def unstash(ui, repo, task): '''Unstashes a working copy. Returns True if a stash was found and applied, False if no stash exists.''' if not hasstash(repo, task): return False ui.write('unstashing task: %s\n' % task) ui.debug('unstashing %s from stash file: %s\n' % (task, stashfiles(repo, task)[0])) patchfile = repo.join(stashfiles(repo, task)[0]) dirstatefile = repo.join(stashfiles(repo, task)[1]) files = {} if os.path.exists(patchfile): try: fuzz = patch.internalpatch(patchfile, ui, strip=1, cwd=repo.root, files=files) except Exception, inst: ui.note(str(inst) + '\n') ui.warn('patch failed, unable to continue\n') ui.warn('see %s for stash patch\n' % patchfile) return False if files: patch.updatedir(ui, repo, files)
def unstash(ui, repo, task): '''Unstashes a working copy. Returns True if a stash was found and applied, False if no stash exists.''' if not hasstash(repo, task): return False ui.write('unstashing task: %s\n' % task) ui.debug('unstashing %s from stash file: %s\n' % (task, stashfiles(repo, task)[0])) patchfile = repo.join(stashfiles(repo, task)[0]) dirstatefile = repo.join(stashfiles(repo, task)[1]) files = {} if os.path.exists(patchfile): try: fuzz = patch.internalpatch(patchfile, ui, strip = 1, cwd = repo.root, files = files) except Exception, inst: ui.note(str(inst) + '\n') ui.warn('patch failed, unable to continue\n') ui.warn('see %s for stash patch\n' % patchfile) return False if files: patch.updatedir(ui, repo, files)
def updatedir(*args, **kwargs): # updatedir moved from patch to cmdutil in hg 1.7 if hasattr(patch, 'updatedir'): patch.updatedir(*args, **kwargs) elif hasattr(cmdutil, 'updatedir'): cmdutil.updatedir(*args, **kwargs) else: # hg 1.9+ moved updatedir, and more importantly it's not needed with the way patch is called now.. scmutil.updatedir(*args, **kwargs)
def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges): parent = ctx.parents()[0].node() hg.update(repo, parent) fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') fp = os.fdopen(fd, 'w') diffopts = patch.diffopts(ui, opts) diffopts.git = True gen = patch.diff(repo, parent, newnode, opts=diffopts) for chunk in gen: fp.write(chunk) fp.close() files = {} try: patch.patch(patchfile, ui, cwd=repo.root, files=files, eolmode=None) finally: files = patch.updatedir(ui, repo, files) os.unlink(patchfile) newmessage = '\n***\n'.join( [ctx.description(), ] + [repo[r].description() for r in internalchanges] + [oldctx.description(), ]) newmessage = ui.edit(newmessage, ui.username()) n = repo.commit(text=newmessage, user=ui.username(), date=max(ctx.date(), oldctx.date()), extra=oldctx.extra()) return repo[n], [n, ], [oldctx.node(), ctx.node() ], [newnode, ] # xxx
def pick(ui, repo, ctx, ha, opts): oldctx = repo[ha] if oldctx.parents()[0] == ctx: ui.debug('node %s unchanged\n' % ha) return oldctx, [], [], [] hg.update(repo, ctx.node()) fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') fp = os.fdopen(fd, 'w') diffopts = patch.diffopts(ui, opts) diffopts.git = True gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts) for chunk in gen: fp.write(chunk) fp.close() try: files = {} try: patch.patch(patchfile, ui, cwd=repo.root, files=files, eolmode=None) if not files: ui.warn(_('%s: empty changeset') % node.hex(ha)) return ctx, [], [], [] finally: files = patch.updatedir(ui, repo, files) os.unlink(patchfile) except Exception, inst: raise util.Abort(_('Fix up the change and run ' 'hg histedit --continue'))
def applyone(self, repo, node, cl, patchfile, merge=False, log=False, filter=None): '''apply the patch in patchfile to the repository as a transplant''' (manifest, user, (time, timezone), files, message) = cl[:5] date = "%d %d" % (time, timezone) extra = {'transplant_source': node} if filter: (user, date, message) = self.filter(filter, cl, patchfile) if log: # we don't translate messages inserted into commits message += '\n(transplanted from %s)' % revlog.hex(node) self.ui.status(_('applying %s\n') % revlog.short(node)) self.ui.note('%s %s\n%s\n' % (user, date, message)) if not patchfile and not merge: raise util.Abort(_('can only omit patchfile if merging')) if patchfile: try: files = {} try: patch.patch(patchfile, self.ui, cwd=repo.root, files=files, eolmode=None) if not files: self.ui.warn( _('%s: empty changeset') % revlog.hex(node)) return None finally: files = patch.updatedir(self.ui, repo, files) except Exception, inst: if filter: os.unlink(patchfile) seriespath = os.path.join(self.path, 'series') if os.path.exists(seriespath): os.unlink(seriespath) p1 = repo.dirstate.parents()[0] p2 = node self.log(user, date, message, p1, p2, merge=merge) self.ui.write(str(inst) + '\n') raise util.Abort( _('Fix up the merge and run ' 'hg transplant --continue'))
def applyone(self, repo, node, cl, patchfile, merge=False, log=False, filter=None): '''apply the patch in patchfile to the repository as a transplant''' (manifest, user, (time, timezone), files, message) = cl[:5] date = "%d %d" % (time, timezone) extra = {'transplant_source': node} if filter: (user, date, message) = self.filter(filter, cl, patchfile) if log: # we don't translate messages inserted into commits message += '\n(transplanted from %s)' % revlog.hex(node) self.ui.status(_('applying %s\n') % revlog.short(node)) self.ui.note('%s %s\n%s\n' % (user, date, message)) if not patchfile and not merge: raise util.Abort(_('can only omit patchfile if merging')) if patchfile: try: files = {} try: patch.patch(patchfile, self.ui, cwd=repo.root, files=files, eolmode=None) if not files: self.ui.warn(_('%s: empty changeset') % revlog.hex(node)) return None finally: files = patch.updatedir(self.ui, repo, files) except Exception, inst: if filter: os.unlink(patchfile) seriespath = os.path.join(self.path, 'series') if os.path.exists(seriespath): os.unlink(seriespath) p1 = repo.dirstate.parents()[0] p2 = node self.log(user, date, message, p1, p2, merge=merge) self.ui.write(str(inst) + '\n') raise util.Abort(_('Fix up the merge and run ' 'hg transplant --continue'))
def edit(ui, repo, ctx, ha, opts): oldctx = repo[ha] hg.update(repo, ctx.node()) fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-') fp = os.fdopen(fd, 'w') diffopts = patch.diffopts(ui, opts) diffopts.git = True gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts) for chunk in gen: fp.write(chunk) fp.close() try: files = {} try: patch.patch(patchfile, ui, cwd=repo.root, files=files, eolmode=None) finally: files = patch.updatedir(ui, repo, files) os.unlink(patchfile) except Exception, inst: pass
def dorecord(ui, repo, commitfunc, *pats, **opts): if not ui.interactive(): raise util.Abort(_('running non-interactively, use commit instead')) def recordfunc(ui, repo, message, match, opts): """This is generic record driver. Its job is to interactively filter local changes, and accordingly prepare working dir into a state, where the job can be delegated to non-interactive commit command such as 'commit' or 'qrefresh'. After the actual job is done by non-interactive command, working dir state is restored to original. In the end we'll record intresting changes, and everything else will be left in place, so the user can continue his work. """ changes = repo.status(match=match)[:3] diffopts = mdiff.diffopts(git=True, nodates=True) chunks = patch.diff(repo, changes=changes, opts=diffopts) fp = cStringIO.StringIO() fp.write(''.join(chunks)) fp.seek(0) # 1. filter patch, so we have intending-to apply subset of it chunks = filterpatch(ui, parsepatch(fp)) del fp contenders = set() for h in chunks: try: contenders.update(set(h.files())) except AttributeError: pass changed = changes[0] + changes[1] + changes[2] newfiles = [f for f in changed if f in contenders] if not newfiles: ui.status(_('no changes to record\n')) return 0 modified = set(changes[0]) # 2. backup changed files, so we can restore them in the end backups = {} backupdir = repo.join('record-backups') try: os.mkdir(backupdir) except OSError, err: if err.errno != errno.EEXIST: raise try: # backup continues for f in newfiles: if f not in modified: continue fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_') + '.', dir=backupdir) os.close(fd) ui.debug('backup %r as %r\n' % (f, tmpname)) util.copyfile(repo.wjoin(f), tmpname) backups[f] = tmpname fp = cStringIO.StringIO() for c in chunks: if c.filename() in backups: c.write(fp) dopatch = fp.tell() fp.seek(0) # 3a. apply filtered patch to clean repo (clean) if backups: hg.revert(repo, repo.dirstate.parents()[0], backups.has_key) # 3b. (apply) if dopatch: try: ui.debug('applying patch\n') ui.debug(fp.getvalue()) pfiles = {} patch.internalpatch(fp, ui, 1, repo.root, files=pfiles, eolmode=None) patch.updatedir(ui, repo, pfiles) except patch.PatchError, err: s = str(err) if s: raise util.Abort(s) else: raise util.Abort(_('patch failed to apply')) del fp # 4. We prepared working directory according to filtered patch. # Now is the time to delegate the job to commit/qrefresh or the like! # it is important to first chdir to repo root -- we'll call a # highlevel command with list of pathnames relative to repo root cwd = os.getcwd() os.chdir(repo.root) try: commitfunc(ui, repo, *newfiles, **opts) finally: os.chdir(cwd) return 0
def updatedir(*args, **kwargs): # updatedir moved from patch to cmdutil in hg 1.7 if hasattr(patch, 'updatedir'): patch.updatedir(*args, **kwargs) else: cmdutil.updatedir(*args, **kwargs)
def dorecord(ui, repo, commitfunc, *pats, **opts): if not ui.interactive(): raise util.Abort(_('running non-interactively, use commit instead')) def recordfunc(ui, repo, message, match, opts): """This is generic record driver. Its job is to interactively filter local changes, and accordingly prepare working dir into a state, where the job can be delegated to non-interactive commit command such as 'commit' or 'qrefresh'. After the actual job is done by non-interactive command, working dir state is restored to original. In the end we'll record intresting changes, and everything else will be left in place, so the user can continue his work. """ changes = repo.status(match=match)[:3] diffopts = mdiff.diffopts(git=True, nodates=True) chunks = patch.diff(repo, changes=changes, opts=diffopts) fp = cStringIO.StringIO() fp.write(''.join(chunks)) fp.seek(0) # 1. filter patch, so we have intending-to apply subset of it chunks = filterpatch(ui, parsepatch(fp)) del fp contenders = set() for h in chunks: try: contenders.update(set(h.files())) except AttributeError: pass changed = changes[0] + changes[1] + changes[2] newfiles = [f for f in changed if f in contenders] if not newfiles: ui.status(_('no changes to record\n')) return 0 modified = set(changes[0]) # 2. backup changed files, so we can restore them in the end backups = {} backupdir = repo.join('record-backups') try: os.mkdir(backupdir) except OSError, err: if err.errno != errno.EEXIST: raise try: # backup continues for f in newfiles: if f not in modified: continue fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.', dir=backupdir) os.close(fd) ui.debug('backup %r as %r\n' % (f, tmpname)) util.copyfile(repo.wjoin(f), tmpname) backups[f] = tmpname fp = cStringIO.StringIO() for c in chunks: if c.filename() in backups: c.write(fp) dopatch = fp.tell() fp.seek(0) # 3a. apply filtered patch to clean repo (clean) if backups: hg.revert(repo, repo.dirstate.parents()[0], backups.has_key) # 3b. (apply) if dopatch: try: ui.debug('applying patch\n') ui.debug(fp.getvalue()) pfiles = {} patch.internalpatch(fp, ui, 1, repo.root, files=pfiles, eolmode=None) patch.updatedir(ui, repo, pfiles) except patch.PatchError, err: s = str(err) if s: raise util.Abort(s) else: raise util.Abort(_('patch failed to apply')) del fp # 4. We prepared working directory according to filtered patch. # Now is the time to delegate the job to commit/qrefresh or the like! # it is important to first chdir to repo root -- we'll call a # highlevel command with list of pathnames relative to repo root cwd = os.getcwd() os.chdir(repo.root) try: commitfunc(ui, repo, *newfiles, **opts) finally: os.chdir(cwd) return 0
def dorecord(ui, repo, commitfunc, *pats, **opts): try: if not ui.interactive(): raise util.Abort(_('running non-interactively, use commit instead')) except TypeError: # backwards compatibility with hg 1.1 if not ui.interactive: raise util.Abort(_('running non-interactively, use commit instead')) def recordfunc(ui, repo, message, match, opts): """This is generic record driver. Its job is to interactively filter local changes, and accordingly prepare working dir into a state, where the job can be delegated to non-interactive commit command such as 'commit' or 'qrefresh'. After the actual job is done by non-interactive command, working dir state is restored to original. In the end we'll record interesting changes, and everything else will be left in place, so the user can continue his work. """ merge = len(repo[None].parents()) > 1 if merge: raise util.Abort(_('cannot partially commit a merge ' '(use hg commit instead)')) # status gives back # modified, added, removed, deleted, unknown, ignored, clean # we take only the first 3 of these changes = repo.status(match=match)[:3] modified, added, removed = changes diffopts = opts.copy() diffopts['nodates'] = True diffopts['git'] = True diffopts = patch.diffopts(ui, opts=diffopts) chunks = patch.diff(repo, changes=changes, opts=diffopts) fp = cStringIO.StringIO() fp.write(''.join(chunks)) fp.seek(0) # 1. filter patch, so we have intending-to apply subset of it chunks = crpatch.filterpatch(opts, crpatch.parsepatch(changes, fp), chunk_selector.chunkselector, ui) del fp contenders = set() for h in chunks: try: contenders.update(set(h.files())) except AttributeError: pass changed = changes[0] + changes[1] + changes[2] newfiles = [f for f in changed if f in contenders] if not newfiles: ui.status(_('no changes to record\n')) return 0 # 2. backup changed files, so we can restore them in the end backups = {} newly_added_backups = {} backupdir = repo.join('record-backups') try: os.mkdir(backupdir) except OSError, err: if err.errno != errno.EEXIST: raise try: # backup continues for f in newfiles: if f not in (modified + added): continue fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.', dir=backupdir) os.close(fd) ui.debug('backup %r as %r\n' % (f, tmpname)) util.copyfile(repo.wjoin(f), tmpname) if f in modified: backups[f] = tmpname elif f in added: newly_added_backups[f] = tmpname fp = cStringIO.StringIO() all_backups = {} all_backups.update(backups) all_backups.update(newly_added_backups) for c in chunks: if c.filename() in all_backups: c.write(fp) dopatch = fp.tell() fp.seek(0) # 2.5 optionally review / modify patch in text editor if opts['crecord_reviewpatch']: patchtext = fp.read() reviewedpatch = ui.edit(patchtext, "") fp.truncate(0) fp.write(reviewedpatch) fp.seek(0) # 3a. apply filtered patch to clean repo (clean) if backups: hg.revert(repo, repo.dirstate.parents()[0], lambda key: key in backups) # remove newly added files from 'clean' repo (so patch can apply) for f in newly_added_backups: os.unlink(f) # 3b. (apply) if dopatch: try: ui.debug('applying patch\n') ui.debug(fp.getvalue()) if hasattr(patch, 'workingbackend'): # detect 1.9 patch.internalpatch(ui, repo, fp, strip=1, eolmode=None) else: pfiles = {} try: patch.internalpatch(ui, repo, fp, 1, eolmode=None) except (TypeError, AttributeError): # pre 17cea10c343e try: patch.internalpatch(ui, repo, fp, 1, repo.root, eolmode=None) except (TypeError, AttributeError): # pre 00a881581400 try: patch.internalpatch(fp, ui, 1, repo.root, files=pfiles, eolmode=None) except TypeError: # backwards compatible with hg 1.1 patch.internalpatch(fp, ui, 1, repo.root, files=pfiles) try: cmdutil.updatedir(ui, repo, pfiles) except AttributeError: try: patch.updatedir(ui, repo, pfiles) except AttributeError: # from 00a881581400 onwards pass except patch.PatchError, err: s = str(err) if s: raise util.Abort(s) else: raise util.Abort(_('patch failed to apply')) del fp # 4. We prepared working directory according to filtered patch. # Now is the time to delegate the job to commit/qrefresh or the like! # it is important to first chdir to repo root -- we'll call a # highlevel command with list of pathnames relative to repo root newfiles = [repo.wjoin(n) for n in newfiles] commitfunc(ui, repo, *newfiles, **opts) return 0
files = {} try: diff = self.bu.backupfile('diff') try: fuzz = patch.patch(diff, self.ws.ui, strip=1, cwd=self.ws.repo.root, files=files) if fuzz: raise util.Abort('working copy diff applied with fuzz') except Exception, e: raise util.Abort("couldn't apply working copy diff: %s\n" " %s" % (diff, e)) finally: if Version.at_least("1.7"): cmdutil.updatedir(self.ws.ui, self.ws.repo, files) else: patch.updatedir(self.ws.ui, self.ws.repo, files) if not self.bu.exists('renames'): return # # We need to re-apply name changes where the new name # (rename/copy destination) is an already versioned file, as # Hg would otherwise ignore them. # try: fp = self.bu.open('renames') for line in fp: source, dest = line.strip().split() self.ws.copy(source, dest) except EnvironmentError, e:
class CdmUncommittedBackup(object): '''Backup of uncommitted changes''' def __init__(self, backup, ws): self.ws = ws self.bu = backup def _clobbering_renames(self): '''Return a list of pairs of files representing renames/copies that clobber already versioned files. [(oldname newname)...]''' # # Note that this doesn't handle uncommitted merges # as CdmUncommittedBackup itself doesn't. # wctx = self.ws.repo.workingctx() parent = wctx.parents()[0] ret = [] for fname in wctx.added() + wctx.modified(): rn = wctx.filectx(fname).renamed() if rn and fname in parent: ret.append((rn[0], fname)) return ret def backup(self): '''Backup uncommitted changes''' if self.ws.merged(): raise util.Abort("Unable to backup an uncommitted merge.\n" "Please complete your merge and commit") dirstate = node.hex(self.ws.repo.changectx().node()) fp = None try: try: fp = open(self.bu.backupfile('dirstate'), 'w') fp.write(dirstate + '\n') except EnvironmentError, e: raise util.Abort("couldn't save working copy parent: %s" % e) finally: if fp and not fp.closed: fp.close() try: try: fp = open(self.bu.backupfile('renames'), 'w') for cons in self._clobbering_renames(): fp.write("%s %s\n" % cons) except EnvironmentError, e: raise util.Abort("couldn't save clobbering copies: %s" % e) finally: if fp and not fp.closed: fp.close() try: try: fp = open(self.bu.backupfile('diff'), 'w') patch.diff(self.ws.repo, fp=fp, opts=patch.diffopts(self.ws.ui, opts={'git': True})) except EnvironmentError, e: raise util.Abort("couldn't save working copy diff: %s" % e) finally: if fp and not fp.closed: fp.close() def _dirstate(self): '''Return the current working copy node''' fp = None try: try: fp = open(self.bu.backupfile('dirstate')) dirstate = fp.readline().strip() return dirstate except EnvironmentError, e: raise util.Abort("couldn't read saved parent: %s" % e) finally: if fp and not fp.closed: fp.close() def restore(self): '''Restore uncommitted changes''' diff = self.bu.backupfile('diff') dirstate = self._dirstate() try: self.ws.clean(rev=dirstate) except util.Abort, e: raise util.Abort("couldn't update to saved node: %s" % e) if not os.path.exists(diff): return # # There's a race here whereby if the patch (or part thereof) # is applied within the same second as the clean above (such # that mtime doesn't change) and if the size of that file # does not change, Hg may not see the change. # # We sleep a full second to avoid this, as sleeping merely # until the next second begins would require very close clock # synchronization on network filesystems. # time.sleep(1) files = {} try: try: fuzz = patch.patch(diff, self.ws.ui, strip=1, cwd=self.ws.repo.root, files=files) if fuzz: raise util.Abort('working copy diff applied with fuzz') except Exception, e: raise util.Abort("couldn't apply working copy diff: %s\n" " %s" % (diff, e)) finally: patch.updatedir(self.ws.ui, self.ws.repo, files) if not os.path.exists(self.bu.backupfile('renames')): return # # We need to re-apply name changes where the new name # (rename/copy destination) is an already versioned file, as # Hg would otherwise ignore them. # try: fp = open(self.bu.backupfile('renames')) for line in fp: source, dest = line.strip().split() self.ws.repo.copy(source, dest) except EnvironmentError, e: raise util.Abort('unable to open renames file: %s' % e)