def rev_list_contains(self, refname, version, fetch=True): """ calls git rev-list with refname and returns True if version can be found in rev-list result :param refname: a git refname :param version: an SHA IDs (if partial, caller is responsible for mismatch) :returns: True if version is an ancestor commit from refname :raises: GitError when call to git fetch fails """ # to avoid listing unnecessarily many rev-ids, we cut off all # those we are definitely not interested in # $ git rev-list foo bar ^baz ^bez # means "list all the commits which are reachable from foo or # bar, but not from baz or bez". We use --parents because # ^baz also excludes baz itself. We could also use git # show --format=%P to get all parents first and use that, # not sure what's more performant if fetch: self._do_fetch() if (refname is not None and refname != '' and version is not None and version != ''): cmd = 'git rev-list %s ^%s --parents' % (sanitized(refname), sanitized(version)) _, output, _ = run_shell_command(cmd, shell=True, cwd=self._path) for line in output.splitlines(): # can have 1, 2 or 3 elements (commit, parent1, parent2) for hashid in line.split(" "): if hashid.startswith(version): return True return False
def checkout(self, url, version='', verbose=False, shallow=False): if self.path_exists(): sys.stderr.write("Error: cannot checkout into existing directory\n") return False # make sure that the parent directory exists for #3497 base_path = os.path.split(self.get_path())[0] try: os.makedirs(base_path) except OSError: # OSError thrown if directory already exists this is ok pass cmd = "hg clone %s %s" % (sanitized(url), self._path) value, _, _ = run_shell_command(cmd, shell=True, no_filter=True) if value != 0: if self.path_exists(): sys.stderr.write("Error: cannot checkout into existing directory\n") return False if version is not None and version.strip() != '': cmd = "hg checkout %s" % sanitized(version) value, _, _ = run_shell_command(cmd, cwd=self._path, shell=True, no_filter=True) if value != 0: return False return True
def checkout(self, url, version='', verbose=False, shallow=False, timeout=None): if url is None or url.strip() == '': raise ValueError('Invalid empty url : "%s"' % url) # make sure that the parent directory exists for #3497 base_path = os.path.split(self.get_path())[0] try: os.makedirs(base_path) except OSError: # OSError thrown if directory already exists this is ok pass cmd = "hg clone %s %s" % (sanitized(url), self._path) value, _, msg = run_shell_command(cmd, shell=True, no_filter=True) if value != 0: if msg: sys.logger.error('%s' % msg) return False if version is not None and version.strip() != '': cmd = "hg checkout %s" % sanitized(version) value, _, msg = run_shell_command(cmd, cwd=self._path, shell=True, no_filter=True) if value != 0: if msg: sys.stderr.write('%s\n' % msg) return False return True
def checkout(self, url, version='', verbose=False, shallow=False, timeout=None): if url is None or url.strip() == '': raise ValueError('Invalid empty url : "%s"' % url) # Need to check as SVN 1.6.17 writes into directory even if not empty if not ensure_dir_notexists(self.get_path()): self.logger.error("Can't remove %s" % self.get_path()) return False if version is not None and version != '': if not version.startswith("-r"): version = "-r%s" % version elif version is None: version = '' cmd = 'svn co %s %s %s' % (sanitized(version), sanitized(url), self._path) value, _, msg = run_shell_command(cmd, shell=True, no_filter=True) if value != 0: if msg: self.logger.error('%s' % msg) return False return True
def _rev_list_contains(self, refname, version, fetch=True): """ calls git rev-list with refname and returns True if version can be found in rev-list result :param refname: a git refname :param version: an SHA IDs (if partial, caller is responsible for mismatch) :returns: True if version is an ancestor commit from refname :raises: GitError when call to git fetch fails """ # to avoid listing unnecessarily many rev-ids, we cut off all # those we are definitely not interested in # $ git rev-list foo bar ^baz ^bez # means "list all the commits which are reachable from foo or # bar, but not from baz or bez". We use --parents because # ^baz also excludes baz itself. We could also use git # show --format=%P to get all parents first and use that, # not sure what's more performant if fetch: self._do_fetch() if (refname is not None and refname != '' and version is not None and version != ''): cmd = 'git rev-list %s ^%s --parents' % (sanitized(refname), sanitized(version)) _, output, _ = run_shell_command(cmd, shell=True, cwd=self._path) for line in output.splitlines(): # can have 1, 2 or 3 elements (commit, parent1, parent2) for hashid in line.split(" "): if hashid.startswith(version): return True return False
def checkout(self, url, version='', verbose=False, shallow=False): if url is None or url.strip() == '': raise ValueError('Invalid empty url : "%s"' % url) # make sure that the parent directory exists for #3497 base_path = os.path.split(self.get_path())[0] try: os.makedirs(base_path) except OSError: # OSError thrown if directory already exists this is ok pass cmd = "hg clone %s %s" % (sanitized(url), self._path) value, _, msg = run_shell_command(cmd, shell=True, no_filter=True) if value != 0: if msg: sys.logger.error('%s' % msg) return False if version is not None and version.strip() != '': cmd = "hg checkout %s" % sanitized(version) value, _, msg = run_shell_command(cmd, cwd=self._path, shell=True, no_filter=True) if value != 0: if msg: sys.stderr.write('%s\n' % msg) return False return True
def get_version(self, spec=None): """ :param spec: (optional) token to identify desired version. For git, this may be anything accepted by git log, e.g. a tagname, branchname, or sha-id. :param fetch: When spec is given, can be used to suppress git fetch call :returns: current SHA-ID of the repository. Or if spec is provided, the SHA-ID of a commit specified by some token if found, else None """ if self.detect_presence(): command = "git log -1" if spec is not None: command += " %s" % sanitized(spec) command += " --format='%H'" repeated = False output = '' #we repeat the call once after fetching if necessary for _ in range(2): _, output, _ = run_shell_command(command, shell=True, cwd=self._path) if (output != '' or spec is None): break # we try again after fetching if given spec had not been found try: self._do_fetch() except GitError: return None # On Windows the version can have single quotes around it output = output.strip("'") return output return None
def get_log(self, relpath=None, limit=None): response = [] if relpath is None: relpath = '' if self.path_exists() and os.path.exists(os.path.join(self._path, relpath)): # Get the log limit_cmd = (("-n %d" % (int(limit))) if limit else "") GIT_COMMIT_FIELDS = ['id', 'author', 'email', 'date', 'message'] GIT_LOG_FORMAT = '%x1f'.join(['%H', '%an', '%ae', '%ad', '%s']) + '%x1e' command = "git --work-tree=%s log --format=\"%s\" %s %s " % (self._path, GIT_LOG_FORMAT, limit_cmd, sanitized(relpath)) return_code, response_str, stderr = run_shell_command(command, shell=True, cwd=self._path) if return_code == 0: # Parse response response = response_str.strip('\n\x1e').split("\x1e") response = [row.strip().split("\x1f") for row in response] response = [dict(zip(GIT_COMMIT_FIELDS, row)) for row in response] # Parse dates for entry in response: entry['date'] = dateutil.parser.parse(entry['date']) return response
def get_log(self, relpath=None, limit=None): response = [] if relpath is None: relpath = "" if self.path_exists() and os.path.exists(os.path.join(self._path, relpath)): # Get the log limit_cmd = ("--limit %d" % (int(limit))) if limit else "" HG_COMMIT_FIELDS = ["id", "author", "email", "date", "message"] HG_LOG_FORMAT = ( "\x1f".join(["{node|short}", "{author|person}", "{autor|email}", "{date|isodate}", "{desc}"]) + "\x1e" ) command = "hg log %s --template '%s' %s" % (sanitized(relpath), HG_LOG_FORMAT, limit_cmd) return_code, response_str, stderr = run_shell_command(command, shell=True, cwd=self._path) if return_code == 0: # Parse response response = response_str.strip("\n\x1e").split("\x1e") response = [row.strip().split("\x1f") for row in response] response = [dict(zip(HG_COMMIT_FIELDS, row)) for row in response] # Parse dates for entry in response: entry["date"] = dateutil.parser.parse(entry["date"]) return response
def get_log(self, relpath=None, limit=None): response = [] if relpath is None: relpath = "" # Compile regexes id_regex = re.compile("^revno: ([0-9]+)$", flags=re.MULTILINE) committer_regex = re.compile("^committer: (.+)$", flags=re.MULTILINE) timestamp_regex = re.compile("^timestamp: (.+)$", flags=re.MULTILINE) message_regex = re.compile("^ (.+)$", flags=re.MULTILINE) if self.path_exists() and os.path.exists(os.path.join(self._path, relpath)): # Get the log limit_cmd = ("--limit=%d" % (int(limit))) if limit else "" command = "bzr log %s %s" % (sanitized(relpath), limit_cmd) return_code, text_response, stderr = run_shell_command(command, shell=True, cwd=self._path) if return_code == 0: revno_match = id_regex.findall(text_response) committer_match = committer_regex.findall(text_response) timestamp_match = timestamp_regex.findall(text_response) message_match = message_regex.findall(text_response) # Extract the entries for revno, committer, timestamp, message in zip( revno_match, committer_match, timestamp_match, message_match ): author, email_address = email.utils.parseaddr(committer) date = dateutil.parser.parse(timestamp) log_data = {"id": revno, "author": author, "email": email_address, "message": message, "date": date} response.append(log_data) return response
def get_version(self, spec=None): """ :param spec: (optional) token to identify desired version. For git, this may be anything accepted by git log, e.g. a tagname, branchname, or sha-id. :param fetch: When spec is given, can be used to suppress git fetch call :returns: current SHA-ID of the repository. Or if spec is provided, the SHA-ID of a commit specified by some token if found, else None """ if self.detect_presence(): command = "git log -1" if spec is not None: command += " %s" % sanitized(spec) command += " --format='%H'" output = '' #we repeat the call once after fetching if necessary for _ in range(2): _, output, _ = run_shell_command(command, shell=True, cwd=self._path) if (output != '' or spec is None): break # we try again after fetching if given spec had not been found try: self._do_fetch() except GitError: return None # On Windows the version can have single quotes around it output = output.strip("'") return output return None
def get_log(self, relpath=None, limit=None): response = [] if relpath is None: relpath = '' if self.path_exists() and os.path.exists(os.path.join(self._path, relpath)): # Get the log limit_cmd = (("--limit %d" % (int(limit))) if limit else "") command = "svn log %s --xml %s" % (limit_cmd, sanitized(relpath) if len(relpath) > 0 else '') return_code, xml_response, stderr = run_shell_command(command, shell=True, cwd=self._path) # Parse response dom = xml.dom.minidom.parseString(xml_response) log_entries = dom.getElementsByTagName("logentry") # Extract the entries for log_entry in log_entries: author_tag = log_entry.getElementsByTagName("author")[0] date_tag = log_entry.getElementsByTagName("date")[0] msg_tags = log_entry.getElementsByTagName("msg") log_data = dict() log_data['id'] = log_entry.getAttribute("revision") log_data['author'] = author_tag.firstChild.nodeValue log_data['email'] = None log_data['date'] = dateutil.parser.parse(str(date_tag.firstChild.nodeValue)) if len(msg_tags) > 0 and msg_tags[0].firstChild: log_data['message'] = msg_tags[0].firstChild.nodeValue else: log_data['message'] = '' response.append(log_data) return response
def get_log(self, relpath=None, limit=None): response = [] if relpath == None: relpath = '' if self.path_exists() and os.path.exists(os.path.join(self._path, relpath)): # Get the log limit_cmd = (("--limit %d" % (int(limit))) if limit else "") HG_COMMIT_FIELDS = ['id', 'author', 'email', 'date', 'message'] HG_LOG_FORMAT = '\x1f'.join(['{node|short}', '{author|person}', '{autor|email}', '{date|isodate}', '{desc}']) + '\x1e' command = "hg log %s --template '%s' %s" % (sanitized(relpath), HG_LOG_FORMAT, limit_cmd) return_code, response_str, stderr = run_shell_command(command, shell=True, cwd=self._path) if return_code == 0: # Parse response response = response_str.strip('\n\x1e').split("\x1e") response = [row.strip().split("\x1f") for row in response] response = [dict(zip(HG_COMMIT_FIELDS, row)) for row in response] print(response) # Parse dates for entry in response: entry['date'] = dateutil.parser.parse(entry['date']) return response
def get_log(self, relpath=None, limit=None): response = [] if relpath == None: relpath = "" if self.path_exists() and os.path.exists(os.path.join(self._path, relpath)): # Get the log limit_cmd = ("-n %d" % (int(limit))) if limit else "" GIT_COMMIT_FIELDS = ["id", "author", "email", "date", "message"] GIT_LOG_FORMAT = "%x1f".join(["%H", "%an", "%ae", "%ad", "%s"]) + "%x1e" command = 'git --work-tree=%s log --format="%s" %s %s ' % ( self._path, GIT_LOG_FORMAT, limit_cmd, sanitized(relpath), ) return_code, response, stderr = run_shell_command(command, shell=True, cwd=self._path) if return_code == 0: # Parse response response = response.strip("\n\x1e").split("\x1e") response = [row.strip().split("\x1f") for row in response] response = [dict(zip(GIT_COMMIT_FIELDS, row)) for row in response] # Parse dates for entry in response: entry["date"] = dateutil.parser.parse(entry["date"]) return response
def get_log(self, relpath=None, limit=None): response = [] if relpath == None: relpath = '' if self.path_exists() and os.path.exists(os.path.join(self._path, relpath)): # Get the log limit_cmd = (("--limit %d" % (int(limit))) if limit else "") command = "svn log %s --xml %s" % (limit_cmd, sanitized(relpath) if len(relpath) > 0 else '') return_code, xml_response, stderr = run_shell_command(command, shell=True, cwd=self._path) # Parse response dom = xml.dom.minidom.parseString(xml_response) log_entries = dom.getElementsByTagName("logentry") # Extract the entries for log_entry in log_entries: author_tag = log_entry.getElementsByTagName("author")[0] date_tag = log_entry.getElementsByTagName("date")[0] msg_tags = log_entry.getElementsByTagName("msg") log_data = dict() log_data['id'] = log_entry.getAttribute("revision") log_data['author'] = author_tag.firstChild.nodeValue log_data['email'] = None log_data['date'] = dateutil.parser.parse(str(date_tag.firstChild.nodeValue)) if len(msg_tags) > 0: log_data['message'] = msg_tags[0].firstChild.nodeValue response.append(log_data) return response
def get_log(self, relpath=None, limit=None): response = [] if relpath is None: relpath = '' if self.path_exists() and os.path.exists( os.path.join(self._path, relpath)): # Get the log limit_cmd = (("-n %d" % (int(limit))) if limit else "") GIT_COMMIT_FIELDS = ['id', 'author', 'email', 'date', 'message'] GIT_LOG_FORMAT = '%x1f'.join(['%H', '%an', '%ae', '%ad', '%s' ]) + '%x1e' command = "git --work-tree=%s log --format=\"%s\" %s %s " % ( self._path, GIT_LOG_FORMAT, limit_cmd, sanitized(relpath)) return_code, response_str, stderr = run_shell_command( command, shell=True, cwd=self._path) if return_code == 0: # Parse response response = response_str.strip('\n\x1e').split("\x1e") response = [row.strip().split("\x1f") for row in response] response = [ dict(zip(GIT_COMMIT_FIELDS, row)) for row in response ] # Parse dates for entry in response: entry['date'] = dateutil.parser.parse(entry['date']) return response
def get_version(self, spec=None): """ :param spec: (optional) revisionspec of desired version. May be any revisionspec as returned by 'bzr help revisionspec', e.g. a tagname or 'revno:<number>' :returns: the current revision number of the repository. Or if spec is provided, the number of a revision specified by some token. """ if self.detect_presence(): if spec is not None: command = ['bzr log -r %s .' % sanitized(spec)] _, output, _ = run_shell_command(command, shell=True, cwd=self._path, us_env=True) if output is None or output.strip() == '' or output.startswith("bzr:"): return None else: matches = [l for l in output.split('\n') if l.startswith('revno: ')] if len(matches) == 1: return matches[0].split()[1] else: _, output, _ = run_shell_command('bzr revno --tree', shell=True, cwd=self._path, us_env=True) return output.strip()
def get_version(self, spec=None): """ :param spec: (optional) revisionspec of desired version. May be any revisionspec as returned by 'bzr help revisionspec', e.g. a tagname or 'revno:<number>' :returns: the current revision number of the repository. Or if spec is provided, the number of a revision specified by some token. """ if self.detect_presence(): if spec is not None: _, output, _ = run_shell_command('bzr log -r %s .' % sanitized(spec), shell=True, cwd=self._path, us_env=True) if output is None or output.strip() == '' or output.startswith( "bzr:"): return None else: matches = [ l for l in output.split('\n') if l.startswith('revno: ') ] if len(matches) == 1: return matches[0].split()[1] else: _, output, _ = run_shell_command('bzr revno --tree', shell=True, cwd=self._path, us_env=True) return output.strip()
def _get_branch_parent(self, fetch=False, current_branch=None): """ :param fetch: if true, performs git fetch first :param current_branch: if not None, this is used as current branch (else extra shell call) :returns: (branch, remote) the name of the branch this branch tracks and its remote :raises: GitError if fetch fails """ if not self.path_exists(): return (None, None) # get name of configured merge ref. branchname = current_branch or self._get_branch() if branchname is None: return (None, None) cmd = 'git config --get %s' % sanitized('branch.%s.merge' % branchname) _, output, _ = run_shell_command(cmd, shell=True, cwd=self._path) if not output: return (None, None) lines = output.splitlines() if len(lines) > 1: sys.stderr.write("vcstools unable to handle multiple merge references for branch %s:\n%s\n" % (branchname, output)) return (None, None) # get name of configured remote cmd = 'git config --get "branch.%s.remote"' % branchname _, output2, _ = run_shell_command(cmd, shell=True, cwd=self._path) remote = output2 or self._get_default_remote() branch_reference = lines[0] # branch_reference is either refname, or /refs/heads/refname, or # heads/refname we would like to return refname however, # user could also have named any branch # "/refs/heads/refname", for some unholy reason check all # known branches on remote for refname, then for the odd # cases, as git seems to do candidate = branch_reference if candidate.startswith('refs/'): candidate = candidate[len('refs/'):] if candidate.startswith('heads/'): candidate = candidate[len('heads/'):] elif candidate.startswith('tags/'): candidate = candidate[len('tags/'):] elif candidate.startswith('remotes/'): candidate = candidate[len('remotes/'):] result = None if self._is_remote_branch(candidate, remote_name=remote, fetch=fetch): result = candidate elif branch_reference != candidate and self._is_remote_branch(branch_reference, remote_name=remote, fetch=False): result = branch_reference if result is not None: return (result, remote) return None, None
def get_log(self, relpath=None, limit=None): response = [] if relpath is None: relpath = '' if self.path_exists() and os.path.exists( os.path.join(self._path, relpath)): # Get the log limit_cmd = (("--limit %d" % (int(limit))) if limit else "") HG_COMMIT_FIELDS = ['id', 'author', 'email', 'date', 'message'] HG_LOG_FORMAT = '\x1f'.join([ '{node|short}', '{author|person}', '{autor|email}', '{date|isodate}', '{desc}' ]) + '\x1e' command = "hg log %s --template '%s' %s" % ( sanitized(relpath), HG_LOG_FORMAT, limit_cmd) return_code, response_str, stderr = run_shell_command( command, shell=True, cwd=self._path) if return_code == 0: # Parse response response = response_str.strip('\n\x1e').split("\x1e") response = [row.strip().split("\x1f") for row in response] response = [ dict(zip(HG_COMMIT_FIELDS, row)) for row in response ] # Parse dates for entry in response: entry['date'] = dateutil.parser.parse(entry['date']) return response
def get_diff(self, basepath=None): response = '' if basepath is None: basepath = self._path if self.path_exists(): rel_path = normalized_rel_path(self._path, basepath) # git needs special treatment as it only works from inside # use HEAD to also show staged changes. Maybe should be option? # injection should be impossible using relpath, but to be sure, we check cmd = "git diff HEAD --src-prefix=%s/ --dst-prefix=%s/ ." % \ (sanitized(rel_path), sanitized(rel_path)) _, response, _ = run_shell_command(cmd, shell=True, cwd=self._path) if LooseVersion(self.gitversion) > LooseVersion('1.7'): cmd = 'git submodule foreach --recursive git diff HEAD' _, output, _ = run_shell_command(cmd, shell=True, cwd=self._path) response += _git_diff_path_submodule_change(output, rel_path) return response
def _get_branch_parent(self, fetch=False, current_branch=None): """ :param fetch: if true, performs git fetch first :param current_branch: if not None, this is used as current branch (else extra shell call) :returns: (branch, remote) the name of the branch this branch tracks and its remote :raises: GitError if fetch fails """ if not self.path_exists(): return (None, None) # get name of configured merge ref. branchname = current_branch or self._get_branch() if branchname is None: return (None, None) cmd = 'git config --get %s' % sanitized('branch.%s.merge' % branchname) _, output, _ = run_shell_command(cmd, shell=True, cwd=self._path) if not output: return (None, None) lines = output.splitlines() if len(lines) > 1: sys.stderr.write( "vcstools unable to handle multiple merge references for branch %s:\n%s\n" % (branchname, output)) return (None, None) # get name of configured remote cmd = 'git config --get "branch.%s.remote"' % branchname _, output2, _ = run_shell_command(cmd, shell=True, cwd=self._path) remote = output2 or self._get_default_remote() branch_reference = lines[0] # branch_reference is either refname, or /refs/heads/refname, or # heads/refname we would like to return refname however, # user could also have named any branch # "/refs/heads/refname", for some unholy reason check all # known branches on remote for refname, then for the odd # cases, as git seems to do candidate = branch_reference if candidate.startswith('refs/'): candidate = candidate[len('refs/'):] if candidate.startswith('heads/'): candidate = candidate[len('heads/'):] elif candidate.startswith('tags/'): candidate = candidate[len('tags/'):] elif candidate.startswith('remotes/'): candidate = candidate[len('remotes/'):] result = None if self._is_remote_branch(candidate, remote_name=remote, fetch=fetch): result = candidate elif branch_reference != candidate and self._is_remote_branch( branch_reference, remote_name=remote, fetch=False): result = branch_reference if result is not None: return (result, remote) return None, None
def get_diff(self, basepath=None): response = None if basepath is None: basepath = self._path if self.path_exists(): rel_path = sanitized(normalized_rel_path(self._path, basepath)) command = "bzr diff %s" % rel_path command += " -p1 --prefix %s/:%s/" % (rel_path, rel_path) _, response, _ = run_shell_command(command, shell=True, cwd=basepath) return response
def _get_version_from_path(self, spec=None, path=None): """ :param spec: (optional) spec can be what 'svn info --help' allows, meaning a revnumber, {date}, HEAD, BASE, PREV, or COMMITTED. :param path: the url to use, default is for this repo :returns: current revision number of the repository. Or if spec provided, the number of a revision specified by some token. """ if not self.path_exists(): return None command = 'svn info ' if spec is not None: if spec.isdigit(): # looking up svn with "-r" takes long, and if spec is # a number, all we get from svn is the same number, # unless we try to look at higher rev numbers (in # which case either get the same number, or an error # if the rev does not exist). So we first do a very # quick svn info, and check revision numbers. currentversion = self.get_version(spec=None) # currentversion is like '-r12345' if currentversion is not None and \ int(currentversion[2:]) > int(spec): # so if we know revision exist, just return the # number, avoid the long call to svn server return '-r' + spec if spec.startswith("-r"): command += sanitized(spec) else: command += sanitized('-r%s' % spec) command += " %s" % path # #3305: parsing not robust to non-US locales _, output, _ = run_shell_command(command, shell=True, us_env=True) if output is not None: matches = \ [l for l in output.splitlines() if l.startswith('Last Changed Rev: ')] if len(matches) == 1: split_str = matches[0].split() if len(split_str) == 4: return '-r' + split_str[3] return None
def get_diff(self, basepath=None): response = None if basepath is None: basepath = self._path if self.path_exists(): rel_path = normalized_rel_path(self._path, basepath) command = "hg diff -g %(path)s --repository %(path)s" % {'path': sanitized(rel_path)} _, response, _ = run_shell_command(command, shell=True, cwd=basepath) response = _hg_diff_path_change(response, rel_path) return response
def get_diff(self, basepath=None): response = None if basepath is None: basepath = self._path if self.path_exists(): rel_path = normalized_rel_path(self._path, basepath) command = 'svn diff %s' % sanitized(rel_path) _, response, _ = run_shell_command(command, shell=True, cwd=basepath) return response
def checkout(self, url, version='', verbose=False, shallow=False): # Need to check as SVN does not care if self.path_exists(): sys.stderr.write("Error: cannot checkout into existing " + "directory %s\n" % self._path) return False if version is not None and version != '': if not version.startswith("-r"): version = "-r%s" % version elif version is None: version = '' cmd = 'svn co %s %s %s' % (sanitized(version), sanitized(url), self._path) value, _, _ = run_shell_command(cmd, shell=True, no_filter=True) if value == 0: return True return False
def get_branch_parent(self, fetch=False, current_branch=None): """ return the name of the branch this branch tracks, if any :raises: GitError if fetch fails """ if self.path_exists(): # get name of configured merge ref. branchname = current_branch or self.get_branch() if branchname is None: return None cmd = 'git config --get %s' % sanitized( 'branch.%s.merge' % branchname) _, output, _ = run_shell_command(cmd, shell=True, cwd=self._path) if not output: return None lines = output.splitlines() if len(lines) > 1: print( "vcstools unable to handle multiple merge references for branch %s:\n%s" % (branchname, output)) return None # get name of configured remote cmd = 'git config --get "branch.%s.remote"' % branchname _, output2, _ = run_shell_command(cmd, shell=True, cwd=self._path) if output2 != "origin": print( "vcstools only handles branches tracking remote 'origin', branch '%s' tracks remote '%s'" % (branchname, output2)) return None output = lines[0] # output is either refname, or /refs/heads/refname, or # heads/refname we would like to return refname however, # user could also have named any branch # "/refs/heads/refname", for some unholy reason check all # known branches on remote for refname, then for the odd # cases, as git seems to do candidate = output if candidate.startswith('refs/'): candidate = candidate[len('refs/'):] if candidate.startswith('heads/'): candidate = candidate[len('heads/'):] elif candidate.startswith('tags/'): candidate = candidate[len('tags/'):] elif candidate.startswith('remotes/'): candidate = candidate[len('remotes/'):] if self.is_remote_branch(candidate, fetch=fetch): return candidate if output != candidate and self.is_remote_branch(output, fetch=False): return output return None
def get_diff(self, basepath=None): response = None if basepath is None: basepath = self._path if self.path_exists(): rel_path = normalized_rel_path(self._path, basepath) command = "hg diff -g %(path)s --repository %(path)s" % { 'path': sanitized(rel_path) } _, response, _ = run_shell_command(command, shell=True, cwd=basepath) response = _hg_diff_path_change(response, rel_path) return response
def is_tag(self, tag_name, fetch=True): """ checks list of tags for match. Set fetch to False if you just fetched already. """ if fetch: self._do_fetch() if self.path_exists(): cmd = "git tag -l %s" % sanitized(tag_name) _, output, _ = run_shell_command(cmd, shell=True, cwd=self._path) lines = output.splitlines() if len(lines) == 1: return True return False
def get_branch_parent(self, fetch=False, current_branch=None): """ return the name of the branch this branch tracks, if any :raises: GitError if fetch fails """ if self.path_exists(): # get name of configured merge ref. branchname = current_branch or self.get_branch() if branchname is None: return None cmd = 'git config --get %s' % sanitized('branch.%s.merge' % branchname) _, output, _ = run_shell_command(cmd, shell=True, cwd=self._path) if not output: return None lines = output.splitlines() if len(lines) > 1: print("vcstools unable to handle multiple merge references for branch %s:\n%s" % (branchname, output)) return None # get name of configured remote cmd = 'git config --get "branch.%s.remote"' % branchname _, output2, _ = run_shell_command(cmd, shell=True, cwd=self._path) if output2 != "origin": print("vcstools only handles branches tracking remote 'origin'," + " branch '%s' tracks remote '%s'" % (branchname, output2)) return None output = lines[0] # output is either refname, or /refs/heads/refname, or # heads/refname we would like to return refname however, # user could also have named any branch # "/refs/heads/refname", for some unholy reason check all # known branches on remote for refname, then for the odd # cases, as git seems to do candidate = output if candidate.startswith('refs/'): candidate = candidate[len('refs/'):] if candidate.startswith('heads/'): candidate = candidate[len('heads/'):] elif candidate.startswith('tags/'): candidate = candidate[len('tags/'):] elif candidate.startswith('remotes/'): candidate = candidate[len('remotes/'):] if self.is_remote_branch(candidate, fetch=fetch): return candidate if output != candidate and self.is_remote_branch(output, fetch=False): return output return None
def checkout(self, url, version='', verbose=False, shallow=False): if url is None or url.strip() == '': raise ValueError('Invalid empty url : "%s"' % url) # Need to check as SVN 1.6.17 writes into directory even if not empty if not ensure_dir_notexists(self.get_path()): self.logger.error("Can't remove %s" % self.get_path()) return False if version is not None and version != '': if not version.startswith("-r"): version = "-r%s" % version elif version is None: version = '' cmd = 'svn co %s %s %s' % (sanitized(version), sanitized(url), self._path) value, _, msg = run_shell_command(cmd, shell=True, no_filter=True) if value != 0: if msg: self.logger.error('%s' % msg) return False return True
def get_version(self, spec=None): """ :param spec: (optional) token for identifying version. spec can be a whatever is allowed by 'hg log -r', e.g. a tagname, sha-ID, revision-number :returns: the current SHA-ID of the repository. Or if spec is provided, the SHA-ID of a revision specified by some token. """ # detect presence only if we need path for cwd in popen if spec is not None: if self.detect_presence(): command = 'hg log -r %s' % sanitized(spec) repeated = False output = '' # we repeat the call once after pullin if necessary while output == '': _, output, _ = run_shell_command(command, shell=True, cwd=self._path, us_env=True) if (output.strip() != '' and not output.startswith("abort") or repeated is True): matches = [ l for l in output.splitlines() if l.startswith('changeset: ') ] if len(matches) == 1: return matches[0].split(':')[2] else: sys.stderr.write( "Warning: found several candidates for hg spec %s" % spec) break self._do_pull() repeated = True return None else: command = 'hg identify -i %s' % self._path _, output, _ = run_shell_command(command, shell=True, us_env=True) if output is None or output.strip() == '' or output.startswith( "abort"): return None # hg adds a '+' to the end if there are uncommited # changes, inconsistent to hg log return output.strip().rstrip('+')
def update(self, version="", verbose=False, timeout=None): verboseflag = "" if verbose: verboseflag = "--verbose" if not self.detect_presence(): sys.stderr.write("Error: cannot update non-existing directory\n") return True if not self._do_pull(): return False if version is not None and version.strip() != "": cmd = "hg checkout %s %s" % (verboseflag, sanitized(version)) else: cmd = "hg update %s --config ui.merge=internal:fail" % verboseflag value, _, _ = run_shell_command(cmd, cwd=self._path, shell=True, no_filter=True) if value != 0: return False return True
def get_status(self, basepath=None, untracked=False): response = None if basepath is None: basepath = self._path if self.path_exists(): rel_path = normalized_rel_path(self._path, basepath) # protect against shell injection command = "hg status %(path)s --repository %(path)s" % {"path": sanitized(rel_path)} if not untracked: command += " -mard" _, response, _ = run_shell_command(command, shell=True, cwd=basepath) if response is not None: if response.startswith("abort"): raise VcsError("Probable Bug; Could not call %s, cwd=%s" % (command, basepath)) if len(response) > 0 and response[-1] != "\n": response += "\n" return response
def get_status(self, basepath=None, untracked=False): response = None if basepath is None: basepath = self._path if self.path_exists(): rel_path = normalized_rel_path(self._path, basepath) command = "bzr status %s -S" % sanitized(rel_path) if not untracked: command += " -V" _, response, _ = run_shell_command(command, shell=True, cwd=basepath) response_processed = "" for line in response.split('\n'): if len(line.strip()) > 0: response_processed += line[0:4] + rel_path + '/' response_processed += line[4:] + '\n' response = response_processed return response
def update(self, version=None, verbose=False): if not self.detect_presence(): sys.stderr.write("Error: cannot update non-existing directory\n") return False # protect against shell injection if version is not None and version != '': if not version.startswith("-r"): version = "-r" + version elif version is None: version = '' cmd = 'svn up %s %s --non-interactive' % (sanitized(version), self._path) value, _, _ = run_shell_command(cmd, shell=True, no_filter=True) if value == 0: return True return False
def get_status(self, basepath=None, untracked=False): response = None if basepath is None: basepath = self._path if self.path_exists(): rel_path = normalized_rel_path(self._path, basepath) # protect against shell injection command = 'svn status %s' % sanitized(rel_path) if not untracked: command += " -q" _, response, _ = run_shell_command(command, shell=True, cwd=basepath) if response is not None and \ len(response) > 0 and \ response[-1] != '\n': response += '\n' return response
def is_tag(self, tag_name, fetch=True): """ checks list of tags for match. Set fetch to False if you just fetched already. :returns: True if tag_name among known tags :raises: GitError when call to git fetch fails """ if fetch: self._do_fetch() if not tag_name: raise ValueError('is_tag requires tag_name, got: "%s"' % tag_name) if self.path_exists(): cmd = 'git tag -l %s' % sanitized(tag_name) _, output, _ = run_shell_command(cmd, shell=True, cwd=self._path) lines = output.splitlines() if len(lines) == 1: return True return False
def get_log(self, relpath=None, limit=None): response = [] if relpath is None: relpath = '' # Compile regexes id_regex = re.compile('^revno: ([0-9]+)$', flags=re.MULTILINE) committer_regex = re.compile('^committer: (.+)$', flags=re.MULTILINE) timestamp_regex = re.compile('^timestamp: (.+)$', flags=re.MULTILINE) message_regex = re.compile('^ (.+)$', flags=re.MULTILINE) if self.path_exists() and os.path.exists( os.path.join(self._path, relpath)): # Get the log limit_cmd = (("--limit=%d" % (int(limit))) if limit else "") command = "bzr log %s %s" % (sanitized(relpath), limit_cmd) return_code, text_response, stderr = run_shell_command( command, shell=True, cwd=self._path) if return_code == 0: revno_match = id_regex.findall(text_response) committer_match = committer_regex.findall(text_response) timestamp_match = timestamp_regex.findall(text_response) message_match = message_regex.findall(text_response) # Extract the entries for revno, committer, timestamp, message in zip( revno_match, committer_match, timestamp_match, message_match): author, email_address = email.utils.parseaddr(committer) date = dateutil.parser.parse(timestamp) log_data = { 'id': revno, 'author': author, 'email': email_address, 'message': message, 'date': date } response.append(log_data) return response
def get_version(self, spec=None): """ :param spec: (optional) token for identifying version. spec can be a whatever is allowed by 'hg log -r', e.g. a tagname, sha-ID, revision-number :returns: the current SHA-ID of the repository. Or if spec is provided, the SHA-ID of a revision specified by some token. """ # detect presence only if we need path for cwd in popen if spec is not None: if self.detect_presence(): command = 'hg log -r %s' % sanitized(spec) repeated = False output = '' # we repeat the call once after pullin if necessary while output == '': _, output, _ = run_shell_command(command, shell=True, cwd=self._path, us_env=True) if (output.strip() != '' and not output.startswith("abort") or repeated is True): matches = [l for l in output.splitlines() if l.startswith('changeset: ')] if len(matches) == 1: return matches[0].split(':')[2] else: sys.stderr.write("Warning: found several candidates for hg spec %s" % spec) break self._do_pull() repeated = True return None else: command = 'hg identify -i %s' % self._path _, output, _ = run_shell_command(command, shell=True, us_env=True) if output is None or output.strip() == '' or output.startswith("abort"): return None # hg adds a '+' to the end if there are uncommited # changes, inconsistent to hg log return output.strip().rstrip('+')
def update(self, version='', verbose=False, timeout=None): verboseflag = '' if verbose: verboseflag = '--verbose' if not self.detect_presence(): sys.stderr.write("Error: cannot update non-existing directory\n") return True if not self._do_pull(): return False if version is not None and version.strip() != '': cmd = "hg checkout %s %s" % (verboseflag, sanitized(version)) else: cmd = "hg update %s --config ui.merge=internal:fail" % verboseflag value, _, _ = run_shell_command(cmd, cwd=self._path, shell=True, no_filter=True) if value != 0: return False return True
def get_status(self, basepath=None, untracked=False): response = None if basepath is None: basepath = self._path if self.path_exists(): rel_path = normalized_rel_path(self._path, basepath) # protect against shell injection command = "hg status %(path)s --repository %(path)s" % { 'path': sanitized(rel_path) } if not untracked: command += " -mard" _, response, _ = run_shell_command(command, shell=True, cwd=basepath) if response is not None: if response.startswith("abort"): raise VcsError("Probable Bug; Could not call %s, cwd=%s" % (command, basepath)) if len(response) > 0 and response[-1] != '\n': response += '\n' return response
def test_sanitized(self): self.assertEqual('', sanitized(None)) self.assertEqual('', sanitized('')) self.assertEqual('"foo"', sanitized('foo')) self.assertEqual('"foo"', sanitized('\"foo\"')) self.assertEqual('"foo"', sanitized('"foo"')) self.assertEqual('"foo"', sanitized('" foo"')) try: sanitized('bla"; foo"') self.fail("Expected Exception") except VcsError: pass try: sanitized('bla";foo"') self.fail("Expected Exception") except VcsError: pass try: sanitized('bla";foo \"bum') self.fail("Expected Exception") except VcsError: pass try: sanitized('bla";foo;"bam') self.fail("Expected Exception") except VcsError: pass try: sanitized('bla"#;foo;"bam') self.fail("Expected Exception") except VcsError: pass