def status(self, repo, options, quick=False): """ Will be called in the actual checkout's directory. Return status text or None if there is no interesting status. """ #just return an error message, don't check anything if quick: return "muddle status -quick is not supported on svn checkouts" text = utils.get_cmd_data("svn status --show-updates --verbose") lines = text.split('\n') stuff = [] seven_spaces = ' '*7 for line in lines: # The first 7 characters and the 9th give us our status if line.startswith(seven_spaces) and line[8] == ' ': continue elif line == '': # typically, a blank final line continue else: stuff.append(line) # Was it just reporting that nothing happened in this particular # revision? if len(stuff) == 1 and stuff[0].startswith('Status against revision'): stuff = [] if stuff: stuff.append('') # add back a cosmetic blank line return '\n'.join(stuff) else: return None
def pull(self, repo, options, upstream=None, verbose=True): """ Will be called in the actual checkout's directory. This runs Subversion's "update", but only if no merging will be needed. That is, first it runs "svn status", and if any lines contain a "C" (for Conflict) in columns 0, 1 or 6, then it will not perform the update. ("svn help status" would call those columns 1, 2 and 7) """ if repo.branch: raise utils.GiveUp("Subversion does not support branch" " in 'pull' (branch='%s')"%repo.branch) text = utils.get_cmd_data("svn status") for line in text: if 'C' in (line[0], line[1], line[6]): raise utils.GiveUp("%s: 'svn status' says there is a Conflict," " refusing to pull:\n%s\nUse 'muddle merge'" " if you want to merge"%(utils.indent(text,' '))) starting_revno = self._just_revno() utils.shell(["svn", "update"] + self._r_option(repo.revision), show_command=verbose) # We could try parsing the output of 'svn update' instead, but this is # simpler to do... ending_revno = self._just_revno() # Did we update anything? return starting_revno != ending_revno
def revision_to_checkout(self, repo, co_leaf, options, force=False, before=None, verbose=False): """ Determine a revision id for this checkout, usable to check it out again. Uses 'svnversion', which I believe is installed as standard with 'svn' For the moment, at lease, the 'force' argument is ignored (so the working copy must be be equivalent to the repository). """ if before: raise utils.GiveUp('%s: "before" argument not currently supported'%co_leaf) revision = utils.get_cmd_data('svnversion', show_command=verbose) revision = revision.strip() if all([x.isdigit() for x in revision]): return revision else: raise utils.GiveUp("%s: 'svnversion' reports checkout has revision" " '%s'"%(co_leaf, revision))
def _do_cmdline(args): original_dir = os.getcwd() original_env = os.environ.copy() dry_run = False verbose = False # TODO: allow switches after args. while args: word = args[0] if word in ("-h", "--help", "-?"): print __doc__ return elif word in ("--dry-run", "-n"): dry_run = True elif word in ("-v", "--verbose"): verbose = True elif word[0] == "-": raise GiveUp, "Unexpected command line option %s" % word else: break args = args[1:] if len(args) != 0: raise GiveUp, "Unexpected non-option arguments given" builder = find_and_load(original_dir, muddle_binary=None) # Don't bother determining muddle_binary: our invocation of find_and_load # doesn't make use of it. (Tibs writes: it's only needed for when # running makefiles, for when they use $(MUDDLE).) if not builder: raise GiveUp("Cannot find a build tree.") rootrepo = builder.db.RootRepository_pathfile.get() rules = builder.all_checkout_rules() rr = [] for r in rules: co_dir = builder.db.get_checkout_path(r.target) if isinstance(r.action.vcs, muddled.vcs.git.Git): if verbose: print "In %s:" % co_dir os.chdir(co_dir) raw = get_cmd_data("git show-ref --heads", verbose=verbose) raw_heads = raw[1].rstrip("\n").split("\n") pat = re.compile("[0-9a-f]+ refs/heads/(.+)") heads = set() for h in raw_heads: m = pat.match(h) if m is None: raise GiveUp("Unparseable output from git: %s" % h) heads.add(m.group(1)) g = r.action.vcs # print "heads is %s"%heads.__str__() if g.branch is not None: if g.branch in heads: if verbose: print "%s: ok (has %s)" % (co_dir, g.branch) else: bfrom = "master" # desired branch not found; if we have a master then try to fixup: if bfrom in heads: # if verbose: print "===\nFixing %s: %s --> %s" % (co_dir, bfrom, g.branch) (rc, lines, igno) = get_cmd_data("git status --porcelain -uall", verbose=verbose) lines = lines.rstrip("\n") if lines != "": if not verbose: print "> git status --porcelain -uall" print ">>%s<<" % lines print ( "Uncommitted changes or untracked files found in %s, deal with these before continuing" % co_dir ) raise GiveUp maybe_run_cmd("git fetch origin %s" % (g.branch), dry_run, verbose) maybe_run_cmd("git fetch origin %s:%s" % (g.branch, g.branch), dry_run, verbose) maybe_run_cmd("git checkout %s" % g.branch, dry_run, verbose) maybe_run_cmd("git config branch.%s.remote origin" % g.branch, dry_run, verbose) maybe_run_cmd("git config branch.%s.merge %s" % (g.branch, g.branch), dry_run, verbose) try: maybe_run_cmd("git branch -d %s" % bfrom, dry_run, verbose) except GiveUp: print "\n* * * HEALTH WARNING * * *" print "Unmerged changes were found committed to the '%s' branch in %s" % (bfrom, co_dir) print "YOU MUST MERGE THESE INTO '%s' YOURSELF OR LOSE THEM!" % g.branch # print "This script will not revisit this checkout." print "The relevant changes are:" run0("git log --oneline --topo-order --graph --decorate=short %s..%s" % (g.branch, bfrom)) raise else: raise GiveUp( "Error: %s wants a branch named '%s', does not have one, and does not have a '%s' either - I don't know how to fix this" % (co_dir, g.branch, bfrom) ) else: # want master, don't care about others if verbose: print "%s heads are: %s" % (co_dir, heads) if not "master" in heads: raise GiveUp( "Error: %s wants a 'master' branch but does not have one, I don't know how to fix this" % co_dir ) else: if verbose: print "Ignoring %s (not powered by git)" % co_dir
def get_stdout(cmd, verbose=True): """Run a command in the shell, and grab its (standard) output. """ return get_cmd_data(cmd, show_command=verbose)
def status(self, repo, options, quick=False): """ Will be called in the actual checkout's directory. Return status text or None if there is no interesting status. """ retcode, text = utils.run2("git status --porcelain", show_command=False) if retcode == 129: print "Warning: Your git does not support --porcelain; you should upgrade it." retcode, text = utils.run2("git status", show_command=False) detached_head = self._is_detached_HEAD() if detached_head: # That's all the user really needs to know note = '\n# Note that this checkout has a detached HEAD' if text: text = '%s\n#%s' % (text, note) else: text = note if text: return text # git status will tell us if there uncommitted changes, etc., but not if # we are ahead of or behind (the local idea of) the remote repository, # or it will, but not with --porcelain if detached_head: head_name = 'HEAD' else: # First, find out what our HEAD actually is retcode, text = utils.run2( "git rev-parse --symbolic-full-name HEAD", show_command=False) head_name = text.strip() # Now we can look up its SHA1, locally retcode, head_revision = utils.run2("git rev-parse %s" % head_name, show_command=False) local_head_ref = head_revision.strip() if quick: branch_name = utils.get_cmd_data("git rev-parse --abbrev-ref HEAD") text = utils.get_cmd_data("git show-ref origin/%s" % branch_name) ref, what = text.split() if ref != local_head_ref: return '\n'.join(( 'After checking local HEAD against our local record of the remote HEAD', '# The local repository does not match the remote:', '#', '# HEAD is %s' % head_name, '# Local is %s' % local_head_ref, '# last known origin/%s is %s' % (branch_name, ref), '#', '# You probably need to push or pull.', '# Use "muddle status" without "-quick" to get a better idea' )) else: return None # So look up the remote equivalents... retcode, text = utils.run2("git ls-remote", show_command=False) lines = text.split('\n') if retcode: # Oh dear - something nasty happened # We know we get this if, for instance, the remote repository does # not actually exist newlines = [] newlines.append( 'Whilst trying to check local HEAD against remote HEAD') for line in lines: newlines.append('# %s' % line) return '\n'.join(newlines) else: for line in lines[1:]: # The first line is the remote repository if not line: continue ref, what = line.split('\t') if what == head_name: if ref != local_head_ref: return '\n'.join(( 'After checking local HEAD against remote HEAD', '# The local repository does not match the remote:', '#', '# HEAD is %s' % head_name, '# Local is %s' % local_head_ref, '# Remote is %s' % ref, '#', '# You probably need to pull with "muddle pull".')) # Should we check to see if we found HEAD? return None
def get_file_content(self, url, verbose=True): """ Retrieve a file's content via Subversion. """ text = utils.get_cmd_data('svn cat %s'%url, show_command=verbose) return text
def _just_revno(self): """ This returns the revision number for the working tree """ revision = utils.get_cmd_data('svnversion') return revision.strip()
def status(self, repo, options, quick=False): """ Will be called in the actual checkout's directory. Return status text or None if there is no interesting status. """ retcode, text = utils.run2("git status --porcelain", show_command=False) if retcode == 129: print "Warning: Your git does not support --porcelain; you should upgrade it." retcode, text = utils.run2("git status", show_command=False) detached_head = self._is_detached_HEAD() if detached_head: # That's all the user really needs to know note = '\n# Note that this checkout has a detached HEAD' if text: text = '%s\n#%s'%(text, note) else: text = note if text: return text # git status will tell us if there uncommitted changes, etc., but not if # we are ahead of or behind (the local idea of) the remote repository, # or it will, but not with --porcelain if detached_head: head_name = 'HEAD' else: # First, find out what our HEAD actually is retcode, text = utils.run2("git rev-parse --symbolic-full-name HEAD", show_command=False) head_name = text.strip() # Now we can look up its SHA1, locally retcode, head_revision = utils.run2("git rev-parse %s"%head_name, show_command=False) local_head_ref = head_revision.strip() if quick: branch_name = utils.get_cmd_data("git rev-parse --abbrev-ref HEAD") text = utils.get_cmd_data("git show-ref origin/%s"%branch_name) ref, what = text.split() if ref != local_head_ref: return '\n'.join( ('After checking local HEAD against our local record of the remote HEAD', '# The local repository does not match the remote:', '#', '# HEAD is %s'%head_name, '# Local is %s'%local_head_ref, '# last known origin/%s is %s'%(branch_name, ref), '#', '# You probably need to push or pull.', '# Use "muddle status" without "-quick" to get a better idea')) else: return None # So look up the remote equivalents... retcode, text = utils.run2("git ls-remote", show_command=False) lines = text.split('\n') if retcode: # Oh dear - something nasty happened # We know we get this if, for instance, the remote repository does # not actually exist newlines = [] newlines.append('Whilst trying to check local HEAD against remote HEAD') for line in lines: newlines.append('# %s'%line) return '\n'.join(newlines) else: for line in lines[1:]: # The first line is the remote repository if not line: continue ref, what = line.split('\t') if what == head_name: if ref != local_head_ref: return '\n'.join(('After checking local HEAD against remote HEAD', '# The local repository does not match the remote:', '#', '# HEAD is %s'%head_name, '# Local is %s'%local_head_ref, '# Remote is %s'%ref, '#', '# You probably need to pull with "muddle pull".')) # Should we check to see if we found HEAD? return None