def link(self, args): """ Actually deploy the configuration by linking the correct spot to the original file. @param args: The parsed arguments from the command-line. """ if args.dry: print(str(self.source) + " -> " + str(self.destination)) return if not self.src_exists: return if self.dst_exists_before: if args.replace: if self.dst_is_link_before: util.unlink(self.destination) elif self.dst_is_file_before: util.remove(self.destination) elif self.dst_is_dir_before: util.remove_dir(self.destination) else: #SUS should never get here. raise Exception("WTF is this shit") else: # File already exists and isn't going to be replaced. return else: #This is some weird nonsense conserning broken links if self.dst_is_link_before: util.unlink(self.destination) if args.copy: util.copy(self.source, self.destination) else: util.link(self.source, self.destination)
def testlock(self): """return id of locker if lock is valid, else None. If old-style lock, we cannot tell what machine locker is on. with new-style lock, if locker is on this machine, we can see if locker is alive. If locker is on this machine but not alive, we can safely break lock. The lock file is only deleted when None is returned. """ locker = util.readlock(self.f) try: host, pid = locker.split(":", 1) except ValueError: return locker if host != lock._host: return locker try: pid = int(pid) except ValueError: return locker if util.testpid(pid): return locker # if locker dead, break lock. must do this with another lock # held, or can race and break valid lock. try: l = lock(self.f + '.break', timeout=0) util.unlink(self.f) l.release() except error.LockError: return locker
def upgrade(self, version, force = False): if not mkdir(self.path): return version if self.updater == "bukkitdev": uver, urlh = self.__bukkitdev_info() elif self.updater == "bukkitdl": uver, urlh = self.__bukkitdl_info() elif self.updater == "github": uver, urlh = self.__github_info() elif self.updater == "jenkins": uver, urlh = self.__jenkins_info() else: log.error("%s: package upgrade failed: invalid updater `%s'", self.package, self.updater) return version if not urlh: log.error("%s: package upgrade failed", self.package) return version out = os.path.join(self.path, "%s.%s" % (self.package, self.type)) if uver and uver == version and not force: log.info("%s: package already up-to-date", self.package) return version if not self.dryrun and not download(urlh, out): return version log.info("%s: package upgraded: %s -> %s", self.package, version, uver) if self.dryrun or (self.type != "zip"): return uver if len(self.extract) < 1: return uver zf = ZipFile(out, "r") nl = zf.namelist() for path in self.extract: if not path.endswith("/") and path in nl: zf.extract(path, self.path) continue for zpath in nl: if zpath.endswith("/"): continue if not zpath.startswith(path): continue zf.extract(zpath, self.path) zf.close() unlink(out) return uver
def kill(server): log.info("%s: fake server stopping...", server) pidfile = FakeServer.pidfile(server) fp = util.fopen(pidfile, "r") if not fp: return False try: pid = int(fp.read()) except: pid = 0 fp.close() util.unlink(pidfile) if not pid: return False try: os.kill(pid, SIGINT) except Exception, msg: log.error("Failed to kill process (%d): %s", pid, msg) return False
def remove(self, list, unlink=False): if unlink: for f in list: try: util.unlink(self._repo.wjoin(f)) except OSError, inst: if inst.errno != errno.ENOENT: raise
def applyupdates(repo, action, wctx, mctx): "apply the merge action list to the working directory" updated, merged, removed, unresolved = 0, 0, 0, 0 ms = mergestate(repo) ms.reset(wctx.parents()[0].node()) moves = [] action.sort(actioncmp) # prescan for merges for a in action: f, m = a[:2] if m == 'm': # merge f2, fd, flags, move = a[2:] repo.ui.debug(_("preserving %s for resolve of %s\n") % (f, fd)) fcl = wctx[f] fco = mctx[f2] fca = fcl.ancestor(fco) or repo.filectx(f, fileid=nullrev) ms.add(fcl, fco, fca, fd, flags) if f != fd and move: moves.append(f) # remove renamed files after safely stored for f in moves: if util.lexists(repo.wjoin(f)): repo.ui.debug(_("removing %s\n") % f) os.unlink(repo.wjoin(f)) audit_path = util.path_auditor(repo.root) for a in action: f, m = a[:2] if f and f[0] == "/": continue if m == "r": # remove repo.ui.note(_("removing %s\n") % f) audit_path(f) try: util.unlink(repo.wjoin(f)) except OSError, inst: if inst.errno != errno.ENOENT: repo.ui.warn(_("update failed to remove %s: %s!\n") % (f, inst.strerror)) removed += 1 elif m == "m": # merge f2, fd, flags, move = a[2:] r = ms.resolve(fd, wctx, mctx) if r > 0: unresolved += 1 else: if r is None: updated += 1 else: merged += 1 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags) if f != fd and move and util.lexists(repo.wjoin(f)): repo.ui.debug(_("removing %s\n") % f) os.unlink(repo.wjoin(f))
def test_raise_exceptions(self): """Test that exceptions raised by a task are propagated""" m = util.Manager() m.add_slave(IMP.parallel.LocalSlave()) c = m.get_context() c.add_task(tasks.error_task) self.assertRaises(IMP.parallel.RemoteError, list, c.get_results_unordered()) util.unlink("slave0.output")
def test_pass_exceptions(self): """Test that exceptions can be passed to and from tasks""" m = util.Manager() m.add_slave(IMP.parallel.LocalSlave()) c = m.get_context() c.add_task(tasks.SimpleTask(IndexError("test"))) results = list(c.get_results_unordered()) self.assertIsInstance(results[0], IndexError) util.unlink("slave0.output")
def __call__(self, path, mode="r", text=False, atomictemp=False, notindexed=False): '''Open ``path`` file, which is relative to vfs root. Newly created directories are marked as "not to be indexed by the content indexing service", if ``notindexed`` is specified for "write" mode access. ''' if self._audit: r = util.checkosfilename(path) if r: raise util.Abort("%s: %r" % (r, path)) self.audit(path) f = self.join(path) if not text and "b" not in mode: mode += "b" # for that other OS nlink = -1 if mode not in ('r', 'rb'): dirname, basename = util.split(f) # If basename is empty, then the path is malformed because it points # to a directory. Let the posixfile() call below raise IOError. if basename: if atomictemp: util.ensuredirs(dirname, self.createmode, notindexed) return util.atomictempfile(f, mode, self.createmode) try: if 'w' in mode: util.unlink(f) nlink = 0 else: # nlinks() may behave differently for files on Windows # shares if the file is open. fd = util.posixfile(f) nlink = util.nlinks(f) if nlink < 1: nlink = 2 # force mktempcopy (issue1922) fd.close() except (OSError, IOError) as e: if e.errno != errno.ENOENT: raise nlink = 0 util.ensuredirs(dirname, self.createmode, notindexed) if nlink > 0: if self._trustnlink is None: self._trustnlink = nlink > 1 or util.checknlink(f) if nlink > 1 or not self._trustnlink: util.rename(util.mktempcopy(f), f) fp = util.posixfile(f, mode) if nlink == 0: self._fixfilemode(f) return fp
def test_startup(self): """Test context startup callable""" m = util.Manager(output='startup%d.out') m.add_slave(IMP.parallel.LocalSlave()) c = m.get_context(startup=tasks.SimpleTask(("foo", "bar"))) c.add_task(tasks.simple_func) c.add_task(tasks.simple_func) results = list(c.get_results_unordered()) self.assertEqual(results, [('foo', 'bar'), ('foo', 'bar')]) util.unlink("startup0.out")
def release(self): if self.held > 1: self.held -= 1 elif self.held == 1: self.held = 0 if self.releasefn: self.releasefn() try: util.unlink(self.f) except OSError: pass
def test_simple(self): """Test that slave tasks can start up and communicate""" m = util.Manager(output='simple%d.out') m.add_slave(IMP.parallel.LocalSlave()) c = m.get_context() for i in range(10): c.add_task(tasks.SimpleTask(i)) results = list(c.get_results_unordered()) results.sort() self.assertEqual(results, list(range(10))) util.unlink("simple0.out")
def close(self): '''commit the transaction''' self.count -= 1 if self.count != 0: return self.file.close() self.entries = [] if self.after: self.after() if os.path.isfile(self.journal): util.unlink(self.journal) self.journal = None
def test_startup_heartbeat(self): """Make sure that startup failures cause a timeout""" def empty_task(): pass m = util.Manager(python="/path/does/not/exist") m.heartbeat_timeout = 0.1 m.add_slave(IMP.parallel.LocalSlave()) c = m.get_context() c.add_task(empty_task) self.assertRaises(IMP.parallel.NetworkError, list, c.get_results_unordered()) util.unlink('slave0.output')
def release(self): """release the lock and execute callback function if any If the lock have been aquired multiple time, the actual release is delayed to the last relase call.""" if self.held > 1: self.held -= 1 elif self.held == 1: self.held = 0 if self.releasefn: self.releasefn() try: util.unlink(self.f) except OSError: pass for callback in self.postrelease: callback()
def applyupdates(repo, action, wctx, mctx): "apply the merge action list to the working directory" updated, merged, removed, unresolved = 0, 0, 0, 0 action.sort() # prescan for copy/renames for a in action: f, m = a[:2] if m == 'm': # merge f2, fd, flags, move = a[2:] if f != fd: repo.ui.debug(_("copying %s to %s\n") % (f, fd)) repo.wwrite(fd, repo.wread(f), flags) audit_path = util.path_auditor(repo.root) for a in action: f, m = a[:2] if f and f[0] == "/": continue if m == "r": # remove repo.ui.note(_("removing %s\n") % f) audit_path(f) try: util.unlink(repo.wjoin(f)) except OSError, inst: if inst.errno != errno.ENOENT: repo.ui.warn( _("update failed to remove %s: %s!\n") % (f, inst.strerror)) removed += 1 elif m == "m": # merge f2, fd, flags, move = a[2:] r = filemerge(repo, f, fd, f2, wctx, mctx) if r > 0: unresolved += 1 else: if r is None: updated += 1 else: merged += 1 util.set_exec(repo.wjoin(fd), "x" in flags) if f != fd and move and util.lexists(repo.wjoin(f)): repo.ui.debug(_("removing %s\n") % f) os.unlink(repo.wjoin(f))
def applyupdates(repo, action, wctx, mctx): "apply the merge action list to the working directory" updated, merged, removed, unresolved = 0, 0, 0, 0 action.sort() # prescan for copy/renames for a in action: f, m = a[:2] if m == 'm': # merge f2, fd, flags, move = a[2:] if f != fd: repo.ui.debug(_("copying %s to %s\n") % (f, fd)) repo.wwrite(fd, repo.wread(f), flags) audit_path = util.path_auditor(repo.root) for a in action: f, m = a[:2] if f and f[0] == "/": continue if m == "r": # remove repo.ui.note(_("removing %s\n") % f) audit_path(f) try: util.unlink(repo.wjoin(f)) except OSError, inst: if inst.errno != errno.ENOENT: repo.ui.warn(_("update failed to remove %s: %s!\n") % (f, inst.strerror)) removed += 1 elif m == "m": # merge f2, fd, flags, move = a[2:] r = filemerge.filemerge(repo, f, fd, f2, wctx, mctx) if r > 0: unresolved += 1 else: if r is None: updated += 1 else: merged += 1 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags) if f != fd and move and util.lexists(repo.wjoin(f)): repo.ui.debug(_("removing %s\n") % f) os.unlink(repo.wjoin(f))
def __call__(self, path, mode="r", text=False, atomictemp=False): if self._audit: r = util.checkosfilename(path) if r: raise util.Abort("%s: %r" % (r, path)) self.auditor(path) f = self.join(path) if not text and "b" not in mode: mode += "b" # for that other OS nlink = -1 dirname, basename = os.path.split(f) # If basename is empty, then the path is malformed because it points # to a directory. Let the posixfile() call below raise IOError. if basename and mode not in ('r', 'rb'): if atomictemp: if not os.path.isdir(dirname): util.makedirs(dirname, self.createmode) return util.atomictempfile(f, mode, self.createmode) try: if 'w' in mode: util.unlink(f) nlink = 0 else: # nlinks() may behave differently for files on Windows # shares if the file is open. fd = util.posixfile(f) nlink = util.nlinks(f) if nlink < 1: nlink = 2 # force mktempcopy (issue1922) fd.close() except (OSError, IOError), e: if e.errno != errno.ENOENT: raise nlink = 0 if not os.path.isdir(dirname): util.makedirs(dirname, self.createmode) if nlink > 0: if self._trustnlink is None: self._trustnlink = nlink > 1 or util.checknlink(f) if nlink > 1 or not self._trustnlink: util.rename(util.mktempcopy(f), f)
def __call__(self, path, mode="r", text=False, atomictemp=False): if self._audit: r = util.checkosfilename(path) if r: raise util.Abort("%s: %r" % (r, path)) self.auditor(path) f = os.path.join(self.base, path) if not text and "b" not in mode: mode += "b" # for that other OS nlink = -1 dirname, basename = os.path.split(f) # If basename is empty, then the path is malformed because it points # to a directory. Let the posixfile() call below raise IOError. if basename and mode not in ('r', 'rb'): if atomictemp: if not os.path.isdir(dirname): util.makedirs(dirname, self.createmode) return util.atomictempfile(f, mode, self.createmode) try: if 'w' in mode: util.unlink(f) nlink = 0 else: # nlinks() may behave differently for files on Windows # shares if the file is open. fd = util.posixfile(f) nlink = util.nlinks(f) if nlink < 1: nlink = 2 # force mktempcopy (issue1922) fd.close() except (OSError, IOError), e: if e.errno != errno.ENOENT: raise nlink = 0 if not os.path.isdir(dirname): util.makedirs(dirname, self.createmode) if nlink > 0: if self._trustnlink is None: self._trustnlink = nlink > 1 or util.checknlink(f) if nlink > 1 or not self._trustnlink: util.rename(util.mktempcopy(f), f)
def _playback(journal, report, opener, entries, unlink=True): for f, o, ignore in entries: if o or not unlink: try: fp = opener(f, 'a') fp.truncate(o) fp.close() except IOError: report(_("failed to truncate %s\n") % f) raise else: try: fp = opener(f) fn = fp.name fp.close() util.unlink(fn) except (IOError, OSError), inst: if inst.errno != errno.ENOENT: raise
def release(self): """release the lock and execute callback function if any If the lock has been acquired multiple times, the actual release is delayed to the last release call.""" if self.held > 1: self.held -= 1 elif self.held == 1: self.held = 0 if os.getpid() != self.pid: # we forked, and are not the parent return if self.releasefn: self.releasefn() try: util.unlink(self.f) except OSError: pass for callback in self.postrelease: callback()
def _abort(self): self.count = 0 self.usages = 0 self.file.close() try: if not self.entries: if self.journal: util.unlink(self.journal) return self.report(_("transaction abort!\n")) try: _playback(self.journal, self.report, self.opener, self.entries, False) self.report(_("rollback completed\n")) except: self.report(_("rollback failed - please run hg recover\n")) finally: self.journal = None
def test_floats(self): """Check that NaN and Inf floats are pickled correctly""" if sys.platform in ('irix6', 'osf1V5'): self.skipTest("Cannot reliably handle NaN and Inf on Irix or Alpha") m = util.Manager() m.add_slave(IMP.parallel.LocalSlave()) c = m.get_context() try: inf = float('inf') nan = float('nan') except ValueError: # Python 2.5 on Windows reports 'invalid literal', so use another # method to get inf and nan: inf = 1e300 * 1e300 nan = inf - inf for f in (inf, nan): c.add_task(tasks.SimpleTask(f)) results = list(c.get_results_unordered()) util.unlink("slave0.output")
def _abort(self): self.count = 0 self.usages = 0 self.file.close() try: if not self.entries: if self.journal: util.unlink(self.journal) return self.report(_("transaction abort!\n")) try: _playback(self.journal, self.report, self.opener, self.entries, False) self.report(_("rollback completed\n")) except Exception: self.report(_("rollback failed - please run hg recover\n")) finally: self.journal = None
def update(repo, node, branchmerge, force, partial, ancestor=None, mergeancestor=False, labels=None): """ Perform a merge between the working directory and the given node node = the node to update to, or None if unspecified branchmerge = whether to merge between branches force = whether to force branch merging or file overwriting partial = a function to filter file lists (dirstate not updated) mergeancestor = whether it is merging with an ancestor. If true, we should accept the incoming changes for any prompts that occur. If false, merging with an ancestor (fast-forward) is only allowed between different named branches. This flag is used by rebase extension as a temporary fix and should be avoided in general. The table below shows all the behaviors of the update command given the -c and -C or no options, whether the working directory is dirty, whether a revision is specified, and the relationship of the parent rev to the target rev (linear, on the same named branch, or on another named branch). This logic is tested by test-update-branches.t. -c -C dirty rev | linear same cross n n n n | ok (1) x n n n y | ok ok ok n n y n | merge (2) (2) n n y y | merge (3) (3) n y * * | --- discard --- y n y * | --- (4) --- y n n * | --- ok --- y y * * | --- (5) --- x = can't happen * = don't-care 1 = abort: not a linear update (merge or update --check to force update) 2 = abort: uncommitted changes (commit and merge, or update --clean to discard changes) 3 = abort: uncommitted changes (commit or update --clean to discard changes) 4 = abort: uncommitted changes (checked in commands.py) 5 = incompatible options (checked in commands.py) Return the same tuple as applyupdates(). """ onode = node wlock = repo.wlock() try: wc = repo[None] pl = wc.parents() p1 = pl[0] pas = [None] if ancestor: pas = [repo[ancestor]] if node is None: # Here is where we should consider bookmarks, divergent bookmarks, # foreground changesets (successors), and tip of current branch; # but currently we are only checking the branch tips. try: node = repo.branchtip(wc.branch()) except errormod.RepoLookupError: if wc.branch() == "default": # no default branch! node = repo.lookup("tip") # update to tip else: raise util.Abort(_("branch %s not found") % wc.branch()) if p1.obsolete() and not p1.children(): # allow updating to successors successors = obsolete.successorssets(repo, p1.node()) # behavior of certain cases is as follows, # # divergent changesets: update to highest rev, similar to what # is currently done when there are more than one head # (i.e. 'tip') # # replaced changesets: same as divergent except we know there # is no conflict # # pruned changeset: no update is done; though, we could # consider updating to the first non-obsolete parent, # similar to what is current done for 'hg prune' if successors: # flatten the list here handles both divergent (len > 1) # and the usual case (len = 1) successors = [n for sub in successors for n in sub] # get the max revision for the given successors set, # i.e. the 'tip' of a set node = repo.revs("max(%ln)", successors).first() pas = [p1] overwrite = force and not branchmerge p2 = repo[node] if pas[0] is None: if repo.ui.config("merge", "preferancestor", '*') == '*': cahs = repo.changelog.commonancestorsheads( p1.node(), p2.node()) pas = [repo[anc] for anc in (sorted(cahs) or [nullid])] else: pas = [p1.ancestor(p2, warn=branchmerge)] fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2) ### check phase if not overwrite and len(pl) > 1: raise util.Abort(_("outstanding uncommitted merge")) if branchmerge: if pas == [p2]: raise util.Abort( _("merging with a working directory ancestor" " has no effect")) elif pas == [p1]: if not mergeancestor and p1.branch() == p2.branch(): raise util.Abort(_("nothing to merge"), hint=_("use 'hg update' " "or check 'hg heads'")) if not force and (wc.files() or wc.deleted()): raise util.Abort(_("uncommitted changes"), hint=_("use 'hg status' to list changes")) for s in sorted(wc.substate): if wc.sub(s).dirty(): raise util.Abort( _("uncommitted changes in " "subrepository '%s'") % s) elif not overwrite: if p1 == p2: # no-op update # call the hooks and exit early repo.hook('preupdate', throw=True, parent1=xp2, parent2='') repo.hook('update', parent1=xp2, parent2='', error=0) return 0, 0, 0, 0 if pas not in ([p1], [p2]): # nonlinear dirty = wc.dirty(missing=True) if dirty or onode is None: # Branching is a bit strange to ensure we do the minimal # amount of call to obsolete.background. foreground = obsolete.foreground(repo, [p1.node()]) # note: the <node> variable contains a random identifier if repo[node].node() in foreground: pas = [p1] # allow updating to successors elif dirty: msg = _("uncommitted changes") if onode is None: hint = _("commit and merge, or update --clean to" " discard changes") else: hint = _("commit or update --clean to discard" " changes") raise util.Abort(msg, hint=hint) else: # node is none msg = _("not a linear update") hint = _("merge or update --check to force update") raise util.Abort(msg, hint=hint) else: # Allow jumping branches if clean and specific rev given pas = [p1] followcopies = False if overwrite: pas = [wc] elif pas == [p2]: # backwards pas = [wc.p1()] elif not branchmerge and not wc.dirty(missing=True): pass elif pas[0] and repo.ui.configbool("merge", "followcopies", True): followcopies = True ### calculate phase actions = calculateupdates(repo, wc, p2, pas, branchmerge, force, partial, mergeancestor, followcopies) ### apply phase if not branchmerge: # just jump to the new rev fp1, fp2, xp1, xp2 = fp2, nullid, xp2, '' if not partial: repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2) # note that we're in the middle of an update repo.vfs.write('updatestate', p2.hex()) stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels) if not partial: repo.dirstate.beginparentchange() repo.setparents(fp1, fp2) recordupdates(repo, actions, branchmerge) # update completed, clear state util.unlink(repo.join('updatestate')) if not branchmerge: repo.dirstate.setbranch(p2.branch()) repo.dirstate.endparentchange() finally: wlock.release() if not partial: repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3]) return stats
def filemerge(repo, mynode, orig, fcd, fco, fca, labels=None): """perform a 3-way merge in the working directory mynode = parent node before merge orig = original local filename before merge fco = other file context fca = ancestor file context fcd = local file context for current/destination file """ def temp(prefix, ctx): pre = "%s~%s." % (os.path.basename(ctx.path()), prefix) (fd, name) = tempfile.mkstemp(prefix=pre) data = repo.wwritedata(ctx.path(), ctx.data()) f = os.fdopen(fd, "wb") f.write(data) f.close() return name if not fco.cmp(fcd): # files identical? return None ui = repo.ui fd = fcd.path() binary = fcd.isbinary() or fco.isbinary() or fca.isbinary() symlink = 'l' in fcd.flags() + fco.flags() tool, toolpath = _picktool(repo, ui, fd, binary, symlink) ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" % (tool, fd, binary, symlink)) if tool in internals: func = internals[tool] trymerge = func.trymerge onfailure = func.onfailure else: func = _xmerge trymerge = True onfailure = _("merging %s failed!\n") toolconf = tool, toolpath, binary, symlink if not trymerge: return func(repo, mynode, orig, fcd, fco, fca, toolconf) a = repo.wjoin(fd) b = temp("base", fca) c = temp("other", fco) back = a + ".orig" util.copyfile(a, back) if orig != fco.path(): ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd)) else: ui.status(_("merging %s\n") % fd) ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca)) markerstyle = ui.config('ui', 'mergemarkers', 'basic') if not labels: labels = _defaultconflictlabels if markerstyle != 'basic': labels = _formatlabels(repo, fcd, fco, fca, labels) needcheck, r = func(repo, mynode, orig, fcd, fco, fca, toolconf, (a, b, c, back), labels=labels) if not needcheck: if r: if onfailure: ui.warn(onfailure % fd) else: util.unlink(back) util.unlink(b) util.unlink(c) return r if not r and (_toolbool(ui, tool, "checkconflicts") or 'conflicts' in _toollist(ui, tool, "check")): if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(), re.MULTILINE): r = 1 checked = False if 'prompt' in _toollist(ui, tool, "check"): checked = True if ui.promptchoice( _("was merge of '%s' successful (yn)?" "$$ &Yes $$ &No") % fd, 1): r = 1 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or 'changed' in _toollist(ui, tool, "check")): if filecmp.cmp(a, back): if ui.promptchoice( _(" output file %s appears unchanged\n" "was merge successful (yn)?" "$$ &Yes $$ &No") % fd, 1): r = 1 if _toolbool(ui, tool, "fixeol"): _matcheol(a, back) if r: if onfailure: ui.warn(onfailure % fd) else: util.unlink(back) util.unlink(b) util.unlink(c) return r
def unlink(self, path=None): return util.unlink(self.join(path))
def applyupdates(repo, action, wctx, mctx): "apply the merge action list to the working directory" updated, merged, removed, unresolved = 0, 0, 0, 0 ms = mergestate(repo) ms.reset(wctx.parents()[0].node()) moves = [] action.sort(key=actionkey) substate = wctx.substate # prime # prescan for merges for a in action: f, m = a[:2] if m == 'm': # merge f2, fd, flags, move = a[2:] if f == '.hgsubstate': # merged internally continue repo.ui.debug(_("preserving %s for resolve of %s\n") % (f, fd)) fcl = wctx[f] fco = mctx[f2] fca = fcl.ancestor(fco) or repo.filectx(f, fileid=nullrev) ms.add(fcl, fco, fca, fd, flags) if f != fd and move: moves.append(f) # remove renamed files after safely stored for f in moves: if util.lexists(repo.wjoin(f)): repo.ui.debug(_("removing %s\n") % f) os.unlink(repo.wjoin(f)) audit_path = util.path_auditor(repo.root) for a in action: f, m = a[:2] if f and f[0] == "/": continue if m == "r": # remove repo.ui.note(_("removing %s\n") % f) audit_path(f) if f == '.hgsubstate': # subrepo states need updating subrepo.submerge(repo, wctx, mctx, wctx) try: util.unlink(repo.wjoin(f)) except OSError, inst: if inst.errno != errno.ENOENT: repo.ui.warn( _("update failed to remove %s: %s!\n") % (f, inst.strerror)) removed += 1 elif m == "m": # merge if f == '.hgsubstate': # subrepo states need updating subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx)) continue f2, fd, flags, move = a[2:] r = ms.resolve(fd, wctx, mctx) if r is not None and r > 0: unresolved += 1 else: if r is None: updated += 1 else: merged += 1 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags) if f != fd and move and util.lexists(repo.wjoin(f)): repo.ui.debug(_("removing %s\n") % f) os.unlink(repo.wjoin(f))
os.unlink(repo.wjoin(f)) elif m == "g": # get flags = a[2] repo.ui.note(_("getting %s\n") % f) t = mctx.filectx(f).data() repo.wwrite(f, t, flags) updated += 1 if f == '.hgsubstate': # subrepo states need updating subrepo.submerge(repo, wctx, mctx, wctx) elif m == "d": # directory rename f2, fd, flags = a[2:] if f: repo.ui.note(_("moving %s to %s\n") % (f, fd)) t = wctx.filectx(f).data() repo.wwrite(fd, t, flags) util.unlink(repo.wjoin(f)) if f2: repo.ui.note(_("getting %s to %s\n") % (f2, fd)) t = mctx.filectx(f2).data() repo.wwrite(fd, t, flags) updated += 1 elif m == "dr": # divergent renames fl = a[2] repo.ui.warn( _("warning: detected divergent renames of %s to:\n") % f) for nf in fl: repo.ui.warn(" %s\n" % nf) elif m == "e": # exec flags = a[2] util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags)
def filemerge(repo, mynode, orig, fcd, fco, fca, labels=None): """perform a 3-way merge in the working directory mynode = parent node before merge orig = original local filename before merge fco = other file context fca = ancestor file context fcd = local file context for current/destination file """ def temp(prefix, ctx): pre = "%s~%s." % (os.path.basename(ctx.path()), prefix) (fd, name) = tempfile.mkstemp(prefix=pre) data = repo.wwritedata(ctx.path(), ctx.data()) f = os.fdopen(fd, "wb") f.write(data) f.close() return name if not fco.cmp(fcd): # files identical? return None ui = repo.ui fd = fcd.path() binary = fcd.isbinary() or fco.isbinary() or fca.isbinary() symlink = 'l' in fcd.flags() + fco.flags() tool, toolpath = _picktool(repo, ui, fd, binary, symlink) ui.debug("picked tool '%s' for %s (binary %s symlink %s)\n" % (tool, fd, binary, symlink)) if tool in internals: func = internals[tool] trymerge = func.trymerge onfailure = func.onfailure else: func = _xmerge trymerge = True onfailure = _("merging %s failed!\n") toolconf = tool, toolpath, binary, symlink if not trymerge: return func(repo, mynode, orig, fcd, fco, fca, toolconf) a = repo.wjoin(fd) b = temp("base", fca) c = temp("other", fco) back = a + ".orig" util.copyfile(a, back) if orig != fco.path(): ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd)) else: ui.status(_("merging %s\n") % fd) ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca)) markerstyle = ui.config('ui', 'mergemarkers', 'basic') if not labels: labels = _defaultconflictlabels if markerstyle != 'basic': labels = _formatlabels(repo, fcd, fco, fca, labels) needcheck, r = func(repo, mynode, orig, fcd, fco, fca, toolconf, (a, b, c, back), labels=labels) if not needcheck: if r: if onfailure: ui.warn(onfailure % fd) else: util.unlink(back) util.unlink(b) util.unlink(c) return r if not r and (_toolbool(ui, tool, "checkconflicts") or 'conflicts' in _toollist(ui, tool, "check")): if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(), re.MULTILINE): r = 1 checked = False if 'prompt' in _toollist(ui, tool, "check"): checked = True if ui.promptchoice(_("was merge of '%s' successful (yn)?" "$$ &Yes $$ &No") % fd, 1): r = 1 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or 'changed' in _toollist(ui, tool, "check")): if filecmp.cmp(a, back): if ui.promptchoice(_(" output file %s appears unchanged\n" "was merge successful (yn)?" "$$ &Yes $$ &No") % fd, 1): r = 1 if _toolbool(ui, tool, "fixeol"): _matcheol(a, back) if r: if onfailure: ui.warn(onfailure % fd) else: util.unlink(back) util.unlink(b) util.unlink(c) return r
def update(repo, node, branchmerge, force, partial, ancestor=None, mergeancestor=False, labels=None): """ Perform a merge between the working directory and the given node node = the node to update to, or None if unspecified branchmerge = whether to merge between branches force = whether to force branch merging or file overwriting partial = a function to filter file lists (dirstate not updated) mergeancestor = whether it is merging with an ancestor. If true, we should accept the incoming changes for any prompts that occur. If false, merging with an ancestor (fast-forward) is only allowed between different named branches. This flag is used by rebase extension as a temporary fix and should be avoided in general. The table below shows all the behaviors of the update command given the -c and -C or no options, whether the working directory is dirty, whether a revision is specified, and the relationship of the parent rev to the target rev (linear, on the same named branch, or on another named branch). This logic is tested by test-update-branches.t. -c -C dirty rev | linear same cross n n n n | ok (1) x n n n y | ok ok ok n n y n | merge (2) (2) n n y y | merge (3) (3) n y * * | --- discard --- y n y * | --- (4) --- y n n * | --- ok --- y y * * | --- (5) --- x = can't happen * = don't-care 1 = abort: not a linear update (merge or update --check to force update) 2 = abort: uncommitted changes (commit and merge, or update --clean to discard changes) 3 = abort: uncommitted changes (commit or update --clean to discard changes) 4 = abort: uncommitted changes (checked in commands.py) 5 = incompatible options (checked in commands.py) Return the same tuple as applyupdates(). """ onode = node wlock = repo.wlock() try: wc = repo[None] pl = wc.parents() p1 = pl[0] pas = [None] if ancestor is not None: pas = [repo[ancestor]] if node is None: # Here is where we should consider bookmarks, divergent bookmarks, # foreground changesets (successors), and tip of current branch; # but currently we are only checking the branch tips. try: node = repo.branchtip(wc.branch()) except errormod.RepoLookupError: if wc.branch() == 'default': # no default branch! node = repo.lookup('tip') # update to tip else: raise util.Abort(_("branch %s not found") % wc.branch()) if p1.obsolete() and not p1.children(): # allow updating to successors successors = obsolete.successorssets(repo, p1.node()) # behavior of certain cases is as follows, # # divergent changesets: update to highest rev, similar to what # is currently done when there are more than one head # (i.e. 'tip') # # replaced changesets: same as divergent except we know there # is no conflict # # pruned changeset: no update is done; though, we could # consider updating to the first non-obsolete parent, # similar to what is current done for 'hg prune' if successors: # flatten the list here handles both divergent (len > 1) # and the usual case (len = 1) successors = [n for sub in successors for n in sub] # get the max revision for the given successors set, # i.e. the 'tip' of a set node = repo.revs('max(%ln)', successors).first() pas = [p1] overwrite = force and not branchmerge p2 = repo[node] if pas[0] is None: if repo.ui.config('merge', 'preferancestor', '*') == '*': cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node()) pas = [repo[anc] for anc in (sorted(cahs) or [nullid])] else: pas = [p1.ancestor(p2, warn=branchmerge)] fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2) ### check phase if not overwrite and len(pl) > 1: raise util.Abort(_("outstanding uncommitted merge")) if branchmerge: if pas == [p2]: raise util.Abort(_("merging with a working directory ancestor" " has no effect")) elif pas == [p1]: if not mergeancestor and p1.branch() == p2.branch(): raise util.Abort(_("nothing to merge"), hint=_("use 'hg update' " "or check 'hg heads'")) if not force and (wc.files() or wc.deleted()): raise util.Abort(_("uncommitted changes"), hint=_("use 'hg status' to list changes")) for s in sorted(wc.substate): wc.sub(s).bailifchanged() elif not overwrite: if p1 == p2: # no-op update # call the hooks and exit early repo.hook('preupdate', throw=True, parent1=xp2, parent2='') repo.hook('update', parent1=xp2, parent2='', error=0) return 0, 0, 0, 0 if pas not in ([p1], [p2]): # nonlinear dirty = wc.dirty(missing=True) if dirty or onode is None: # Branching is a bit strange to ensure we do the minimal # amount of call to obsolete.background. foreground = obsolete.foreground(repo, [p1.node()]) # note: the <node> variable contains a random identifier if repo[node].node() in foreground: pas = [p1] # allow updating to successors elif dirty: msg = _("uncommitted changes") if onode is None: hint = _("commit and merge, or update --clean to" " discard changes") else: hint = _("commit or update --clean to discard" " changes") raise util.Abort(msg, hint=hint) else: # node is none msg = _("not a linear update") hint = _("merge or update --check to force update") raise util.Abort(msg, hint=hint) else: # Allow jumping branches if clean and specific rev given pas = [p1] followcopies = False if overwrite: pas = [wc] elif pas == [p2]: # backwards pas = [wc.p1()] elif not branchmerge and not wc.dirty(missing=True): pass elif pas[0] and repo.ui.configbool('merge', 'followcopies', True): followcopies = True ### calculate phase actionbyfile, diverge, renamedelete = calculateupdates( repo, wc, p2, pas, branchmerge, force, partial, mergeancestor, followcopies) # Convert to dictionary-of-lists format actions = dict((m, []) for m in 'a f g cd dc r dm dg m e k'.split()) for f, (m, args, msg) in actionbyfile.iteritems(): if m not in actions: actions[m] = [] actions[m].append((f, args, msg)) if not util.checkcase(repo.path): # check collision between files only in p2 for clean update if (not branchmerge and (force or not wc.dirty(missing=True, branch=False))): _checkcollision(repo, p2.manifest(), None) else: _checkcollision(repo, wc.manifest(), actions) # Prompt and create actions. TODO: Move this towards resolve phase. for f, args, msg in sorted(actions['cd']): if repo.ui.promptchoice( _("local changed %s which remote deleted\n" "use (c)hanged version or (d)elete?" "$$ &Changed $$ &Delete") % f, 0): actions['r'].append((f, None, "prompt delete")) else: actions['a'].append((f, None, "prompt keep")) del actions['cd'][:] for f, args, msg in sorted(actions['dc']): flags, = args if repo.ui.promptchoice( _("remote changed %s which local deleted\n" "use (c)hanged version or leave (d)eleted?" "$$ &Changed $$ &Deleted") % f, 0) == 0: actions['g'].append((f, (flags,), "prompt recreating")) del actions['dc'][:] ### apply phase if not branchmerge: # just jump to the new rev fp1, fp2, xp1, xp2 = fp2, nullid, xp2, '' if not partial: repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2) # note that we're in the middle of an update repo.vfs.write('updatestate', p2.hex()) stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels) # divergent renames for f, fl in sorted(diverge.iteritems()): repo.ui.warn(_("note: possible conflict - %s was renamed " "multiple times to:\n") % f) for nf in fl: repo.ui.warn(" %s\n" % nf) # rename and delete for f, fl in sorted(renamedelete.iteritems()): repo.ui.warn(_("note: possible conflict - %s was deleted " "and renamed to:\n") % f) for nf in fl: repo.ui.warn(" %s\n" % nf) if not partial: repo.dirstate.beginparentchange() repo.setparents(fp1, fp2) recordupdates(repo, actions, branchmerge) # update completed, clear state util.unlink(repo.join('updatestate')) if not branchmerge: repo.dirstate.setbranch(p2.branch()) repo.dirstate.endparentchange() finally: wlock.release() if not partial: def updatehook(parent1=xp1, parent2=xp2, error=stats[3]): repo.hook('update', parent1=parent1, parent2=parent2, error=error) repo._afterlock(updatehook) return stats
def update(repo, node, branchmerge, force, partial, ancestor=None, mergeancestor=False): """ Perform a merge between the working directory and the given node node = the node to update to, or None if unspecified branchmerge = whether to merge between branches force = whether to force branch merging or file overwriting partial = a function to filter file lists (dirstate not updated) mergeancestor = whether it is merging with an ancestor. If true, we should accept the incoming changes for any prompts that occur. If false, merging with an ancestor (fast-forward) is only allowed between different named branches. This flag is used by rebase extension as a temporary fix and should be avoided in general. The table below shows all the behaviors of the update command given the -c and -C or no options, whether the working directory is dirty, whether a revision is specified, and the relationship of the parent rev to the target rev (linear, on the same named branch, or on another named branch). This logic is tested by test-update-branches.t. -c -C dirty rev | linear same cross n n n n | ok (1) x n n n y | ok ok ok n n y * | merge (2) (2) n y * * | --- discard --- y n y * | --- (3) --- y n n * | --- ok --- y y * * | --- (4) --- x = can't happen * = don't-care 1 = abort: crosses branches (use 'hg merge' or 'hg update -c') 2 = abort: crosses branches (use 'hg merge' to merge or use 'hg update -C' to discard changes) 3 = abort: uncommitted local changes 4 = incompatible options (checked in commands.py) Return the same tuple as applyupdates(). """ onode = node wlock = repo.wlock() try: wc = repo[None] if node is None: # tip of current branch try: node = repo.branchtip(wc.branch()) except error.RepoLookupError: if wc.branch() == "default": # no default branch! node = repo.lookup("tip") # update to tip else: raise util.Abort(_("branch %s not found") % wc.branch()) overwrite = force and not branchmerge pl = wc.parents() p1, p2 = pl[0], repo[node] if ancestor: pa = repo[ancestor] else: pa = p1.ancestor(p2) fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2) ### check phase if not overwrite and len(pl) > 1: raise util.Abort(_("outstanding uncommitted merges")) if branchmerge: if pa == p2: raise util.Abort(_("merging with a working directory ancestor" " has no effect")) elif pa == p1: if not mergeancestor and p1.branch() == p2.branch(): raise util.Abort(_("nothing to merge"), hint=_("use 'hg update' " "or check 'hg heads'")) if not force and (wc.files() or wc.deleted()): raise util.Abort(_("outstanding uncommitted changes"), hint=_("use 'hg status' to list changes")) for s in sorted(wc.substate): if wc.sub(s).dirty(): raise util.Abort(_("outstanding uncommitted changes in " "subrepository '%s'") % s) elif not overwrite: if pa not in (p1, p2): # nolinear dirty = wc.dirty(missing=True) if dirty or onode is None: # Branching is a bit strange to ensure we do the minimal # amount of call to obsolete.background. foreground = obsolete.foreground(repo, [p1.node()]) # note: the <node> variable contains a random identifier if repo[node].node() in foreground: pa = p1 # allow updating to successors elif dirty: msg = _("crosses branches (merge branches or use" " --clean to discard changes)") raise util.Abort(msg) else: # node is none msg = _("crosses branches (merge branches or update" " --check to force update)") raise util.Abort(msg) else: # Allow jumping branches if clean and specific rev given pa = p1 ### calculate phase actions = calculateupdates(repo, wc, p2, pa, branchmerge, force, partial, mergeancestor) ### apply phase if not branchmerge: # just jump to the new rev fp1, fp2, xp1, xp2 = fp2, nullid, xp2, '' if not partial: repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2) # note that we're in the middle of an update repo.vfs.write('updatestate', p2.hex()) stats = applyupdates(repo, actions, wc, p2, pa, overwrite) if not partial: repo.setparents(fp1, fp2) recordupdates(repo, actions, branchmerge) # update completed, clear state util.unlink(repo.join('updatestate')) if not branchmerge: repo.dirstate.setbranch(p2.branch()) finally: wlock.release() if not partial: repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3]) return stats
def update(repo, node, branchmerge, force, partial, ancestor=None, mergeancestor=False): """ Perform a merge between the working directory and the given node node = the node to update to, or None if unspecified branchmerge = whether to merge between branches force = whether to force branch merging or file overwriting partial = a function to filter file lists (dirstate not updated) mergeancestor = whether it is merging with an ancestor. If true, we should accept the incoming changes for any prompts that occur. If false, merging with an ancestor (fast-forward) is only allowed between different named branches. This flag is used by rebase extension as a temporary fix and should be avoided in general. The table below shows all the behaviors of the update command given the -c and -C or no options, whether the working directory is dirty, whether a revision is specified, and the relationship of the parent rev to the target rev (linear, on the same named branch, or on another named branch). This logic is tested by test-update-branches.t. -c -C dirty rev | linear same cross n n n n | ok (1) x n n n y | ok ok ok n n y n | merge (2) (2) n n y y | merge (3) (3) n y * * | --- discard --- y n y * | --- (4) --- y n n * | --- ok --- y y * * | --- (5) --- x = can't happen * = don't-care 1 = abort: not a linear update (merge or update --check to force update) 2 = abort: uncommitted changes (commit and merge, or update --clean to discard changes) 3 = abort: uncommitted changes (commit or update --clean to discard changes) 4 = abort: uncommitted changes (checked in commands.py) 5 = incompatible options (checked in commands.py) Return the same tuple as applyupdates(). """ onode = node wlock = repo.wlock() try: wc = repo[None] if node is None: # tip of current branch try: node = repo.branchtip(wc.branch()) except error.RepoLookupError: if wc.branch() == "default": # no default branch! node = repo.lookup("tip") # update to tip else: raise util.Abort(_("branch %s not found") % wc.branch()) overwrite = force and not branchmerge pl = wc.parents() p1, p2 = pl[0], repo[node] if ancestor: pa = repo[ancestor] else: pa = p1.ancestor(p2) fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2) ### check phase if not overwrite and len(pl) > 1: raise util.Abort(_("outstanding uncommitted merges")) if branchmerge: if pa == p2: raise util.Abort(_("merging with a working directory ancestor" " has no effect")) elif pa == p1: if not mergeancestor and p1.branch() == p2.branch(): raise util.Abort(_("nothing to merge"), hint=_("use 'hg update' " "or check 'hg heads'")) if not force and (wc.files() or wc.deleted()): raise util.Abort(_("uncommitted changes"), hint=_("use 'hg status' to list changes")) for s in sorted(wc.substate): if wc.sub(s).dirty(): raise util.Abort(_("uncommitted changes in " "subrepository '%s'") % s) elif not overwrite: if pa not in (p1, p2): # nolinear dirty = wc.dirty(missing=True) if dirty or onode is None: # Branching is a bit strange to ensure we do the minimal # amount of call to obsolete.background. foreground = obsolete.foreground(repo, [p1.node()]) # note: the <node> variable contains a random identifier if repo[node].node() in foreground: pa = p1 # allow updating to successors elif dirty: msg = _("uncommitted changes") if onode is None: hint = _("commit and merge, or update --clean to" " discard changes") else: hint = _("commit or update --clean to discard" " changes") raise util.Abort(msg, hint=hint) else: # node is none msg = _("not a linear update") hint = _("merge or update --check to force update") raise util.Abort(msg, hint=hint) else: # Allow jumping branches if clean and specific rev given pa = p1 ### calculate phase actions = calculateupdates(repo, wc, p2, pa, branchmerge, force, partial, mergeancestor) ### apply phase if not branchmerge: # just jump to the new rev fp1, fp2, xp1, xp2 = fp2, nullid, xp2, '' if not partial: repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2) # note that we're in the middle of an update repo.vfs.write('updatestate', p2.hex()) stats = applyupdates(repo, actions, wc, p2, pa, overwrite) if not partial: repo.setparents(fp1, fp2) recordupdates(repo, actions, branchmerge) # update completed, clear state util.unlink(repo.join('updatestate')) if not branchmerge: repo.dirstate.setbranch(p2.branch()) finally: wlock.release() if not partial: repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3]) return stats
def applyupdates(repo, action, wctx, mctx, actx, overwrite): """apply the merge action list to the working directory wctx is the working copy context mctx is the context to be merged into the working copy actx is the context of the common ancestor """ updated, merged, removed, unresolved = 0, 0, 0, 0 ms = mergestate(repo) ms.reset(wctx.parents()[0].node()) moves = [] action.sort(key=actionkey) substate = wctx.substate # prime # prescan for merges u = repo.ui for a in action: f, m = a[:2] if m == 'm': # merge f2, fd, flags, move = a[2:] if f == '.hgsubstate': # merged internally continue repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd)) fcl = wctx[f] fco = mctx[f2] if mctx == actx: # backwards, use working dir parent as ancestor if fcl.parents(): fca = fcl.parents()[0] else: fca = repo.filectx(f, fileid=nullrev) else: fca = fcl.ancestor(fco, actx) if not fca: fca = repo.filectx(f, fileid=nullrev) ms.add(fcl, fco, fca, fd, flags) if f != fd and move: moves.append(f) # remove renamed files after safely stored for f in moves: if os.path.lexists(repo.wjoin(f)): repo.ui.debug("removing %s\n" % f) os.unlink(repo.wjoin(f)) audit_path = util.path_auditor(repo.root) numupdates = len(action) for i, a in enumerate(action): f, m = a[:2] u.progress(_('updating'), i + 1, item=f, total=numupdates, unit=_('files')) if f and f[0] == "/": continue if m == "r": # remove repo.ui.note(_("removing %s\n") % f) audit_path(f) if f == '.hgsubstate': # subrepo states need updating subrepo.submerge(repo, wctx, mctx, wctx, overwrite) try: util.unlink(repo.wjoin(f)) except OSError, inst: if inst.errno != errno.ENOENT: repo.ui.warn( _("update failed to remove %s: %s!\n") % (f, inst.strerror)) removed += 1 elif m == "m": # merge if f == '.hgsubstate': # subrepo states need updating subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite) continue f2, fd, flags, move = a[2:] r = ms.resolve(fd, wctx, mctx) if r is not None and r > 0: unresolved += 1 else: if r is None: updated += 1 else: merged += 1 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags) if f != fd and move and os.path.lexists(repo.wjoin(f)): repo.ui.debug("removing %s\n" % f) os.unlink(repo.wjoin(f))
fp = opener(f, 'a') fp.truncate(o) fp.close() except IOError: report(_("failed to truncate %s\n") % f) raise else: try: fp = opener(f) fn = fp.name fp.close() util.unlink(fn) except (IOError, OSError), inst: if inst.errno != errno.ENOENT: raise util.unlink(journal) class transaction(object): def __init__(self, report, opener, journal, after=None, createmode=None): self.count = 1 self.usages = 1 self.report = report self.opener = opener self.after = after self.entries = [] self.map = {} self.journal = journal self._queue = [] self.file = util.posixfile(self.journal, "w") if createmode is not None:
if f != fd and move and util.lexists(repo.wjoin(f)): repo.ui.debug(_("removing %s\n") % f) os.unlink(repo.wjoin(f)) elif m == "g": # get flags = a[2] repo.ui.note(_("getting %s\n") % f) t = mctx.filectx(f).data() repo.wwrite(f, t, flags) updated += 1 elif m == "d": # directory rename f2, fd, flags = a[2:] if f: repo.ui.note(_("moving %s to %s\n") % (f, fd)) t = wctx.filectx(f).data() repo.wwrite(fd, t, flags) util.unlink(repo.wjoin(f)) if f2: repo.ui.note(_("getting %s to %s\n") % (f2, fd)) t = mctx.filectx(f2).data() repo.wwrite(fd, t, flags) updated += 1 elif m == "dr": # divergent renames fl = a[2] repo.ui.warn("warning: detected divergent renames of %s to:\n" % f) for nf in fl: repo.ui.warn(" %s\n" % nf) elif m == "e": # exec flags = a[2] util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags) return updated, merged, removed, unresolved
def update(repo, node, branchmerge, force, partial, ancestor=None, mergeancestor=False): """ Perform a merge between the working directory and the given node node = the node to update to, or None if unspecified branchmerge = whether to merge between branches force = whether to force branch merging or file overwriting partial = a function to filter file lists (dirstate not updated) mergeancestor = whether it is merging with an ancestor. If true, we should accept the incoming changes for any prompts that occur. If false, merging with an ancestor (fast-forward) is only allowed between different named branches. This flag is used by rebase extension as a temporary fix and should be avoided in general. The table below shows all the behaviors of the update command given the -c and -C or no options, whether the working directory is dirty, whether a revision is specified, and the relationship of the parent rev to the target rev (linear, on the same named branch, or on another named branch). This logic is tested by test-update-branches.t. -c -C dirty rev | linear same cross n n n n | ok (1) x n n n y | ok ok ok n n y n | merge (2) (2) n n y y | merge (3) (3) n y * * | --- discard --- y n y * | --- (4) --- y n n * | --- ok --- y y * * | --- (5) --- x = can't happen * = don't-care 1 = abort: not a linear update (merge or update --check to force update) 2 = abort: uncommitted changes (commit and merge, or update --clean to discard changes) 3 = abort: uncommitted changes (commit or update --clean to discard changes) 4 = abort: uncommitted changes (checked in commands.py) 5 = incompatible options (checked in commands.py) Return the same tuple as applyupdates(). """ onode = node wlock = repo.wlock() try: wc = repo[None] pl = wc.parents() p1 = pl[0] pas = [None] if ancestor: pas = [repo[ancestor]] if node is None: # Here is where we should consider bookmarks, divergent bookmarks, # foreground changesets (successors), and tip of current branch; # but currently we are only checking the branch tips. try: node = repo.branchtip(wc.branch()) except error.RepoLookupError: if wc.branch() == "default": # no default branch! node = repo.lookup("tip") # update to tip else: raise util.Abort(_("branch %s not found") % wc.branch()) if p1.obsolete() and not p1.children(): # allow updating to successors successors = obsolete.successorssets(repo, p1.node()) # behavior of certain cases is as follows, # # divergent changesets: update to highest rev, similar to what # is currently done when there are more than one head # (i.e. 'tip') # # replaced changesets: same as divergent except we know there # is no conflict # # pruned changeset: no update is done; though, we could # consider updating to the first non-obsolete parent, # similar to what is current done for 'hg prune' if successors: # flatten the list here handles both divergent (len > 1) # and the usual case (len = 1) successors = [n for sub in successors for n in sub] # get the max revision for the given successors set, # i.e. the 'tip' of a set node = repo.revs("max(%ln)", successors)[0] pas = [p1] overwrite = force and not branchmerge p2 = repo[node] if pas[0] is None: if repo.ui.config("merge", "preferancestor") == '*': cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node()) pas = [repo[anc] for anc in (sorted(cahs) or [nullid])] else: pas = [p1.ancestor(p2, warn=True)] fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2) ### check phase if not overwrite and len(pl) > 1: raise util.Abort(_("outstanding uncommitted merges")) if branchmerge: if pas == [p2]: raise util.Abort(_("merging with a working directory ancestor" " has no effect")) elif pas == [p1]: if not mergeancestor and p1.branch() == p2.branch(): raise util.Abort(_("nothing to merge"), hint=_("use 'hg update' " "or check 'hg heads'")) if not force and (wc.files() or wc.deleted()): raise util.Abort(_("uncommitted changes"), hint=_("use 'hg status' to list changes")) for s in sorted(wc.substate): if wc.sub(s).dirty(): raise util.Abort(_("uncommitted changes in " "subrepository '%s'") % s) elif not overwrite: if p1 == p2: # no-op update # call the hooks and exit early repo.hook('preupdate', throw=True, parent1=xp2, parent2='') repo.hook('update', parent1=xp2, parent2='', error=0) return 0, 0, 0, 0 if pas not in ([p1], [p2]): # nonlinear dirty = wc.dirty(missing=True) if dirty or onode is None: # Branching is a bit strange to ensure we do the minimal # amount of call to obsolete.background. foreground = obsolete.foreground(repo, [p1.node()]) # note: the <node> variable contains a random identifier if repo[node].node() in foreground: pas = [p1] # allow updating to successors elif dirty: msg = _("uncommitted changes") if onode is None: hint = _("commit and merge, or update --clean to" " discard changes") else: hint = _("commit or update --clean to discard" " changes") raise util.Abort(msg, hint=hint) else: # node is none msg = _("not a linear update") hint = _("merge or update --check to force update") raise util.Abort(msg, hint=hint) else: # Allow jumping branches if clean and specific rev given pas = [p1] followcopies = False if overwrite: pas = [wc] elif pas == [p2]: # backwards pas = [wc.p1()] elif not branchmerge and not wc.dirty(missing=True): pass elif pas[0] and repo.ui.configbool("merge", "followcopies", True): followcopies = True ### calculate phase actions = calculateupdates(repo, wc, p2, pas, branchmerge, force, partial, mergeancestor, followcopies) ### apply phase if not branchmerge: # just jump to the new rev fp1, fp2, xp1, xp2 = fp2, nullid, xp2, '' if not partial: repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2) # note that we're in the middle of an update repo.vfs.write('updatestate', p2.hex()) stats = applyupdates(repo, actions, wc, p2, overwrite) if not partial: repo.setparents(fp1, fp2) recordupdates(repo, actions, branchmerge) # update completed, clear state util.unlink(repo.join('updatestate')) if not branchmerge: repo.dirstate.setbranch(p2.branch()) finally: wlock.release() if not partial: repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3]) return stats
fp = opener(f, 'a') fp.truncate(o) fp.close() except IOError: report(_("failed to truncate %s\n") % f) raise else: try: fp = opener(f) fn = fp.name fp.close() util.unlink(fn) except (IOError, OSError), inst: if inst.errno != errno.ENOENT: raise util.unlink(journal) class transaction(object): def __init__(self, report, opener, journal, after=None, createmode=None): self.count = 1 self.usages = 1 self.report = report self.opener = opener self.after = after self.entries = [] self.map = {} self.journal = journal self._queue = [] self.file = util.posixfile(self.journal, "w")
def applyupdates(repo, action, wctx, mctx, actx, overwrite): """apply the merge action list to the working directory wctx is the working copy context mctx is the context to be merged into the working copy actx is the context of the common ancestor """ updated, merged, removed, unresolved = 0, 0, 0, 0 ms = mergestate(repo) ms.reset(wctx.parents()[0].node()) moves = [] action.sort(key=actionkey) substate = wctx.substate # prime # prescan for merges u = repo.ui for a in action: f, m = a[:2] if m == 'm': # merge f2, fd, flags, move = a[2:] if f == '.hgsubstate': # merged internally continue repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd)) fcl = wctx[f] fco = mctx[f2] if mctx == actx: # backwards, use working dir parent as ancestor if fcl.parents(): fca = fcl.parents()[0] else: fca = repo.filectx(f, fileid=nullrev) else: fca = fcl.ancestor(fco, actx) if not fca: fca = repo.filectx(f, fileid=nullrev) ms.add(fcl, fco, fca, fd, flags) if f != fd and move: moves.append(f) # remove renamed files after safely stored for f in moves: if os.path.lexists(repo.wjoin(f)): repo.ui.debug("removing %s\n" % f) os.unlink(repo.wjoin(f)) audit_path = util.path_auditor(repo.root) numupdates = len(action) for i, a in enumerate(action): f, m = a[:2] u.progress(_('updating'), i + 1, item=f, total=numupdates, unit=_('files')) if f and f[0] == "/": continue if m == "r": # remove repo.ui.note(_("removing %s\n") % f) audit_path(f) if f == '.hgsubstate': # subrepo states need updating subrepo.submerge(repo, wctx, mctx, wctx, overwrite) try: util.unlink(repo.wjoin(f)) except OSError, inst: if inst.errno != errno.ENOENT: repo.ui.warn(_("update failed to remove %s: %s!\n") % (f, inst.strerror)) removed += 1 elif m == "m": # merge if f == '.hgsubstate': # subrepo states need updating subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), overwrite) continue f2, fd, flags, move = a[2:] r = ms.resolve(fd, wctx, mctx) if r is not None and r > 0: unresolved += 1 else: if r is None: updated += 1 else: merged += 1 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags) if f != fd and move and os.path.lexists(repo.wjoin(f)): repo.ui.debug("removing %s\n" % f) os.unlink(repo.wjoin(f))
host, pid = locker.split(":", 1) except ValueError: return locker if host != lock._host: return locker try: pid = int(pid) except ValueError: return locker if util.testpid(pid): return locker # if locker dead, break lock. must do this with another lock # held, or can race and break valid lock. try: l = lock(self.f + '.break', timeout=0) util.unlink(self.f) l.release() except error.LockError: return locker def release(self): """release the lock and execute callback function if any If the lock has been acquired multiple times, the actual release is delayed to the last release call.""" if self.held > 1: self.held -= 1 elif self.held == 1: self.held = 0 if os.getpid() != self.pid: # we forked, and are not the parent