def currentVersion() -> str: versions = getVersions() g = Git(path) for i in versions: if i in g.branch(): return i if "* master" == g.branch(): return "master" return g.branch()
def branches(): g = Git(PROJECT_DIR) send('重新获取远程分支中.....') g.fetch(REMOTE_NAME) send('获取成功') branch_names = g.branch('-a').split('\n') return jsonify(branch_names)
def __init__(self): # Prevent working as root uname = puke.utils.env.get("PUKE_OS", puke.system.uname).lower() id = puke.utils.env.get("PUKE_LOGIN", puke.system.login) if id == "root": puke.display.error("Root detected! Panic!") puke.log.critical( "Running puke as root without a PUKE_LOGIN is frown upon") # Load chained config files r = Config( {}, "~/.pukerc", ["package.json", "package-%s-%s.json" % (id, uname)]) self.man = r r = r.content # Map to older format for lazyness reasons :) clean = re.sub('[.]git$', '', r.repository["url"]) r.package = { "name": r.name, "version": r.version } r.rights = { "license": '<a href="%s">%s</a>' % (r.licenses[0]["url"], r.licenses[0]["type"]), "copyright": 'All rights reserved <a href="http://www.webitup.fr">copyright %s</a>' % r.author, "author": r.author } r.git = { "root": '%s/blob/master/src' % clean } r.paths = r.directories r.config = r.config # Git in the yanks try: g = Git() r.git.root = r.git.root.replace( '/master/', '/%s/' % g.branch()) r.git.revision = '#' + g.nb() + '-' + g.hash() except: r.git.revision = '#no-git-information' puke.display.warning( "FAILED fetching git information - locations won't be accurate") for (key, path) in r.paths.items(): puke.fs.mkdir(path) self.config = r # Bower wrapping try: self.bower = Bower(self.config.bower) except Exception as e: puke.sh.npm.install() self.bower = Bower(self.config.bower)
def findGitBranch(commit_id=None): SHA = commit_id repo = Git() branchs = repo.branch(contains=SHA).split() # print(repo.branch(contains=SHA).split()) pattern = re.compile('^(release|prerelease)\/.*$') match = filter(pattern.search, branchs) return list(match)[0]
def _get_branches(self): c_git = Git(str(self._conf.get("path_to_repo"))) branches = set() args = ["--contains", self.hash] if self._conf.get("include_remotes"): args = ["-r"] + args if self._conf.get("include_refs"): args = ["-a"] + args for branch in set(c_git.branch(*args).split("\n")): branches.add(branch.strip().replace("* ", "")) return branches
def branches(self) -> Set[str]: """ Return the set of branches that contain the commit. :return: set(str) branches """ git = Git(self._path) branches = set() for branch in set(git.branch('--contains', self.hash).split('\n')): branches.add(branch.strip().replace('* ', '')) return branches
def update(self): """ Updates the opsoro software thru git """ if self.git is None: return False # Create file to let deamon know it has to update before starting the server # file = open(self.dir + 'update.var', 'w+') backup_dir = '/home/pi/OPSORO/backup/' print('Updating...') if os.path.exists(backup_dir): # remove previous backup try: shutil.rmtree(backup_dir) except Exception as e: print_error('Remove backup failed: ' + str(e)) pass try: shutil.copytree(self.dir, backup_dir) except Exception as e: print_error('Backup copy failed: ' + str(e)) pass # Link git & update try: g = Git(self.dir) g.fetch('--all') g.reset('--hard', 'origin/' + g.branch().split()[-1]) # g.pull() except Exception as e: print_error('Git failed to update: ' + str(e)) pass # Set script executable for deamon try: st = os.stat(os.path.join(self.dir, 'run')) os.chmod(os.path.join(self.dir, 'run'), st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) except Exception as e: print_error('Exec state set failed: ' + str(e)) pass # Clear update var file # os.remove(os.path.join(self.dir, 'update.var')) # restart service command = ['/usr/sbin/service', 'opsoro', 'restart'] #shell=FALSE for sudo to work. subprocess.call(command, shell=False)
def get_version_from_branch(repository: git.Git) -> Optional[str]: """Extract version from release branch name Parameters ---------- repository Returns ------- version """ current_remote_branch = repository.branch("--remote", "--contains") if "releases/" in current_remote_branch: return current_remote_branch.split("/")[-1] return None
def get_branches(path): os.chdir(path) ## end new branches = [] try: g = Git() branches = g.branch() print('branches object {}'.format(branches.replace('g', ''))) names = branches.split(' ') print(names) except Exception as e: print(e)
def branch(branch_name): # branch_name = 'remotes/ssh/' + branch_name g = Git(PROJECT_DIR) send('重新获取远程分支中.....') g.fetch(REMOTE_NAME) send('获取成功') branch_names = g.branch('-a').split('\n') branch_names = [_.strip() for _ in branch_names] if branch_name not in branch_names: return '你的这个branch啊,鹅母鸡鸭' try: send('重置分支') g.reset('--hard') send('切换分支中') g.checkout(branch_name) send('切换分支完成') except Exception as e: return str(e) return branch_name
class GitFlow(object): """ Creates a :class:`GitFlow` instance. :param working_dir: The directory where the Git repo is located. If not specified, the current working directory is used. When a :class:`GitFlow` class is instantiated, it auto-discovers all subclasses of :class:`gitflow.branches.BranchManager`, so there is no explicit registration required. """ def _discover_branch_managers(self): managers = {} for cls in itersubclasses(BranchManager): # TODO: Initialize managers with the gitflow branch prefixes managers[cls.identifier] = cls(self) return managers def __init__(self, working_dir='.'): # Allow Repos to be passed in instead of strings self.repo = None if isinstance(working_dir, Repo): self.working_dir = working_dir.working_dir else: self.working_dir = working_dir self.git = Git(self.working_dir) try: self.repo = Repo(self.working_dir) except InvalidGitRepositoryError: pass self.managers = self._discover_branch_managers() self.defaults = { 'gitflow.branch.master': 'master', 'gitflow.branch.develop': 'develop', 'gitflow.prefix.versiontag': '', 'gitflow.origin': 'origin', 'gitflow.release.versionmatcher': '[0-9]+([.][0-9]+){2}', } for identifier, manager in self.managers.items(): self.defaults['gitflow.prefix.%s' % identifier] = manager.DEFAULT_PREFIX def _init_config(self, master=None, develop=None, prefixes={}, names={}, force_defaults=False): for setting, default in self.defaults.items(): if force_defaults: value = default elif setting == 'gitflow.branch.master': value = master elif setting == 'gitflow.branch.develop': value = develop elif setting.startswith('gitflow.prefix.'): name = setting[len('gitflow.prefix.'):] value = prefixes.get(name, None) else: name = setting[len('gitflow.'):] value = names.get(name, None) if value is None: value = self.get(setting, default) self.set(setting, value) def _init_initial_commit(self): master = self.master_name() if master in self.repo.branches: # local `master` branch exists return elif self.origin_name(master) in self.repo.refs: # the origin branch counterpart exists origin = self.repo.refs[self.origin_name(master)] branch = self.repo.create_head(master, origin) branch.set_tracking_branch(origin) elif self.repo.heads: raise NotImplementedError('Local and remote branches exist, ' 'but neither %s nor %s' % (master, self.origin_name(master) )) else: # Create 'master' branch info('Creating branch %r' % master) c = self.repo.index.commit('Initial commit', head=False) self.repo.create_head(master, c) def _init_develop_branch(self): # assert master already exists assert self.master_name() in self.repo.refs develop = self.develop_name() if develop in self.repo.branches: # local `develop` branch exists, but do not switch there return if self.origin_name(develop) in self.repo.refs: # the origin branch counterpart exists origin = self.repo.refs[self.origin_name(develop)] branch = self.repo.create_head(develop, origin) branch.set_tracking_branch(origin) else: # Create 'develop' branch info('Creating branch %r' % develop) branch = self.repo.create_head(develop, self.master()) # switch to develop branch if its newly created info('Switching to branch %s' % branch) branch.checkout() def _enforce_git_repo(self): """ Ensure a (maybe empty) repository exists we can work on. This is to be used by the `init` sub-command. """ if self.repo is None: self.git.init(self.working_dir) self.repo = Repo(self.working_dir) def _enforce_services(self): err = False for option in ('gitflow.pt.token', 'reviewboard.url', 'reviewboard.server'): try: value = self.get(option) if option == 'reviewboard.url' and not value.endswith('/'): err = True print """ Git config key 'reviewboard.url' must contain a trailing slash. Update your configuration by executing $ git config [--global] reviewboard.url %s """ % (value + '/') except Exception: try: if option == 'gitflow.pt.token': value = self.get('workflow.token') self.set('gitflow.pt.token', value) continue except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): pass err = True print """ Git config '%s' missing, please fill it in by executing $ git config [--global] %s <value> """ % (option, option) if err: raise SystemExit('Operation canceled.') def init(self, master=None, develop=None, prefixes={}, names={}, force_defaults=False): self._enforce_git_repo() self._init_config(master, develop, prefixes, names, force_defaults) self._init_initial_commit() self._init_develop_branch() return self def is_initialized(self): return (self.repo and self.is_set('gitflow.branch.master') and self.is_set('gitflow.branch.develop') and self.is_set('gitflow.prefix.feature') and self.is_set('gitflow.prefix.release') and self.is_set('gitflow.prefix.hotfix') and self.is_set('gitflow.prefix.support') and self.is_set('gitflow.prefix.versiontag') and self.is_set('gitflow.release.versionmatcher')) def _parse_setting(self, setting): groups = setting.split('.', 2) if len(groups) == 2: section, option = groups elif len(groups) == 3: section, subsection, option = groups section = '%s "%s"' % (section, subsection) else: raise ValueError('Invalid setting name: %s' % setting) return (section, option) @requires_repo def get(self, setting, default=_NONE): section, option = self._parse_setting(setting) try: return self.repo.config_reader().get_value(section, option) except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): if default is not _NONE: return default raise def get_prefix(self, identifier): return self._safe_get('gitflow.prefix.%s' % (identifier,)) @requires_repo def set(self, setting, value): section, option = self._parse_setting(setting) self.repo.config_writer().set_value(section, option, value) def is_set(self, setting): return self.get(setting, None) is not None @requires_repo def _safe_get(self, setting_name): try: return self.get(setting_name) except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): raise NotInitialized('This repo has not yet been initialized.') def master_name(self): return self._safe_get('gitflow.branch.master') def develop_name(self): return self._safe_get('gitflow.branch.develop') def origin_name(self, name=None): origin = self.get('gitflow.origin', self.defaults['gitflow.origin']) if name is not None: return origin + '/' + name else: return origin @requires_repo def require_remote(self, name): try: remote = self.repo.remotes[name] except IndexError: raise NoSuchRemoteError("Remote '{0}' was not found.".format(name)) # Monkey patch remote.fetch to fix a false negative assertion in GitPython. class RemoteWrapper(object): def __init__(self, remote): self._remote = remote def __str__(self): return self._remote.__str__() def __repr__(self): return self._remote.__repr__() def __eq__(self, other): return self._remote.__eq__(other) def __ne__(self, other): return self._remote.__ne__(other) def __hash__(self): return self._remote.__hash__() # For some reason __getattribute__ is making infinite recursion, # so let's use __getattr__. def __getattr__(self, name): return self._remote.__getattribute__(name) # This is the main monkey patching part. def fetch(self, refspec=None, progress=None, **kwargs): # Let's try 3 times... err = None for x in range(3): try: return self._remote.fetch(refspec=refspec, progress=progress, **kwargs) except AssertionError as e: err = e # If we are in '_get_fetch_info_from_stderr', # it's the broken assertion and we skip it. func_name = traceback.extract_stack()[-1][2] if func_name != '_get_fetch_info_from_stderr': raise e err = None # If we somehow get the same exception 3 times, just raise anyway. raise err return RemoteWrapper(remote) @requires_repo def require_origin_branch(self, branch_name): origin_name = self.origin_name(branch_name) refs = [r for r in self.repo.refs if isinstance(r, RemoteReference) and r.name == origin_name] if len(refs) != 0: assert len(refs) == 1 return refs[0] raise NoSuchBranchError( "Remote branch '{0}' was not found".format(origin_name)) def origin(self): return self.require_remote(self.origin_name()) @requires_repo def develop(self): return self.repo.branches[self.develop_name()] @requires_repo def master(self): return self.repo.branches[self.master_name()] @requires_repo def branch_names(self, remote=False): if remote: return [r.name for r in self.repo.refs if isinstance(r, RemoteReference)] else: return [r.name for r in self.repo.branches] @requires_repo def nameprefix_or_current(self, identifier, prefix): """ :param identifier: The identifier for the type of branch to create. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param prefix: If the empty, see if the current branch is of type `identifier`. If so, returns the current branches short name, otherwise raises :exc:`NoSuchBranchError`. If exactly one branch of type `identifier` starts with the given name `prefix`, returns that branches short name. Raises :exc:`NoSuchBranchError` in case no branch exists with the given prefix, or :exc:`PrefixNotUniqueError` in case multiple matches are found. """ repo = self.repo manager = self.managers[identifier] if not prefix: if repo.active_branch.name.startswith(manager.prefix): return manager.shorten(repo.active_branch.name) else: raise NoSuchBranchError('The current branch is no %s branch. ' 'Please specify one explicitly.' % identifier) branch = None try: branch = manager.by_name_prefix(prefix).name except NoSuchBranchError as ex: try: remote_branch = manager.by_name_prefix(prefix, remote=True).name branch = str(remote_branch)[len(self.origin_name())+1:] self.git.branch(branch, remote_branch) except NoSuchBranchError: raise ex return manager.shorten(branch) @requires_repo def name_or_current(self, identifier, name, must_exist=True): """ :param identifier: The identifier for the type of branch to create. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: If the `name` is empty, see if the current branch is of type `identifier`. If so, returns the current branches short name, otherwise raises :exc:`NoSuchBranchError`. :param must_exist: If `True` (the default), raises :exc:`NoSuchBranchError` in case no branch exists with the given `name`. Otherwise return the `name` unchanged. """ repo = self.repo manager = self.managers[identifier] if not name: if repo.active_branch.name.startswith(manager.prefix): return manager.shorten(repo.active_branch.name) else: raise NoSuchBranchError('The current branch is no %s branch. ' 'Please specify one explicitly.' % identifier) elif must_exist and not manager.full_name(name) in (b.name for b in manager.list()): raise NoSuchBranchError('There is no %s branch named %s.' % (identifier, name)) return name @requires_repo def status(self): result = [] for b in self.repo.branches: tup = self.branch_info(b.name) result.append(tup) return result @requires_repo def branch_info(self, name): active_branch = self.repo.active_branch b = self.repo.heads[name] return (name, b.commit.hexsha, b == active_branch) @requires_repo def is_dirty(self): """ Returns whether or not the current working directory contains uncommitted changes. """ return self.repo.is_dirty() @requires_repo def has_staged_commits(self): """ Returns whether or not the current repo contains local changes checked into the index but not committed. """ return len(self.repo.index.diff(self.repo.head.commit)) > 0 @requires_repo def require_no_merge_conflict(self): """ Raises :exc:`MergeConflict` if the current working directory contains a merge conflict. """ try: git.Reference(self.repo, 'MERGE_HEAD', check_path=False).commit # reference exists, so there is a merge conflict raise MergeConflict() except ValueError: # no such reference, so there is no merge conflict pass def is_merged_into(self, commit, target_branch): """ Checks whether `commit` is successfully merged into branch `target_branch`. :param commit: The commit or branch that ought to be merged. This may be a full branch-name, a commit-hexsha or any of branch-, head-, reference- or commit-object. :param target_branch: The branch which should contain the commit. This may be a full branch-name, or any of branch-, head- or reference-object. """ try: commit = self.repo.rev_parse(str(commit)) except git.BadObject: raise BadObjectError(commit) if isinstance(target_branch, git.RemoteReference): target_branch = 'remotes/' + target_branch.name elif isinstance(target_branch, git.SymbolicReference): target_branch = target_branch.name # :todo: implement this more efficiently return target_branch in [ b.lstrip('* ') for b in self.git.branch('-a', '--contains', commit).splitlines()] def must_be_uptodate(self, branch): remote_branch = self.origin_name(branch) if remote_branch in self.branch_names(remote=True): self.require_branches_equal(branch, remote_branch) @requires_repo def _compare_branches(self, branch1, branch2): """ Tests whether branches and their 'origin' counterparts have diverged and need merging first. It returns error codes to provide more detail, like so: 0 Branch heads point to the same commit 1 First given branch needs fast-forwarding 2 Second given branch needs fast-forwarding 3 Branch needs a real merge 4 There is no merge base, i.e. the branches have no common ancestors """ try: commit1 = self.repo.rev_parse(branch1) commit2 = self.repo.rev_parse(branch2) except git.BadObject, e: raise NoSuchBranchError('Branch {0} not found'.format(e.args[0])) if commit1 == commit2: return 0 try: base = self.repo.git.merge_base(commit1, commit2) except GitCommandError: return 4 if base == commit1: return 1 elif base == commit2: return 2 else: return 3
def _get_branches(self): git = Git(str(self.project_path)) branches = set() for branch in set(git.branch('--contains', self.hash).split('\n')): branches.add(branch.strip().replace('* ', '')) return branches
def main(): # Determine what json-ld file to compare which = 'locations' if len(sys.argv) == 2: which = sys.argv[1] # Initialize git client and store current branch name git = Git() currentBranch = None for branch in git.branch().split('\n'): if branch[0] == '*': currentBranch = branch[2:] break print(f'Comparing {which} in {currentBranch} to master') # Get current working copy of the JSON-LD file newFile = openJsonFile(which) try: # Checkout master and get the current version git.checkout('master') masterFile = openJsonFile(which) # Compare the two objects for item in newFile['@graph']: if not 'skos:notation' in item: print('Item found without a skos:notation!', item) newLocDict = { item['skos:notation']: item for item in newFile['@graph'] } masterLocDict = { item['skos:notation']: item for item in masterFile['@graph'] } newKeys = newLocDict.keys() - masterLocDict.keys() deletedKeys = masterLocDict.keys() - newLocDict.keys() alteredKeys = list( filter(lambda x: x[1], [ (key, DeepDiff( masterLocDict[key], newLocDict[key], ignore_order=True)) for key in set(newLocDict.keys()) & set(masterLocDict.keys()) ])) # Output comparison results print('Keys Added: {}'.format(len(newKeys))) print('Keys Deleted: {}'.format(len(deletedKeys))) print('Keys Altered: {}'.format(len(alteredKeys))) displayAlterations( alteredKeys) # Provide details on altered mapping objects except Exception as e: template = "An exception of type {0} occurred. Arguments:\n{1!r}" message = template.format(type(e).__name__, e.args) print(message) # Reset to current working branch git.checkout(currentBranch)
class GitFlow(object): """ Creates a :class:`GitFlow` instance. :param working_dir: The directory where the Git repo is located. If not specified, the current working directory is used. When a :class:`GitFlow` class is instantiated, it auto-discovers all subclasses of :class:`gitflow.branches.BranchManager`, so there is no explicit registration required. """ def _discover_branch_managers(self): managers = {} for cls in itersubclasses(BranchManager): # TODO: Initialize managers with the gitflow branch prefixes managers[cls.identifier] = cls(self) return managers def __init__(self, working_dir='.'): # Allow Repos to be passed in instead of strings self.repo = None if isinstance(working_dir, Repo): self.working_dir = working_dir.working_dir else: self.working_dir = working_dir self.git = Git(self.working_dir) try: self.repo = Repo(self.working_dir) except InvalidGitRepositoryError: pass self.managers = self._discover_branch_managers() self.defaults = { 'gitflow.branch.master': 'master', 'gitflow.branch.develop': 'develop', 'gitflow.prefix.versiontag': '', 'gitflow.origin': 'origin', } for identifier, manager in self.managers.items(): self.defaults['gitflow.prefix.%s' % identifier] = manager.DEFAULT_PREFIX def _init_config(self, master=None, develop=None, prefixes={}, names={}, force_defaults=False): for setting, default in self.defaults.items(): if force_defaults: value = default elif setting == 'gitflow.branch.master': value = master elif setting == 'gitflow.branch.develop': value = develop elif setting.startswith('gitflow.prefix.'): name = setting[len('gitflow.prefix.'):] value = prefixes.get(name, None) else: name = setting[len('gitflow.'):] value = names.get(name, None) if value is None: value = self.get(setting, default) self.set(setting, value) def _init_initial_commit(self): master = self.master_name() if master in self.repo.branches: # local `master` branch exists return elif self.origin_name(master) in self.repo.refs: # the origin branch counterpart exists origin = self.repo.refs[self.origin_name(master)] branch = self.repo.create_head(master, origin) branch.set_tracking_branch(origin) elif self.repo.heads: raise NotImplementedError('Local and remote branches exist, ' 'but neither %s nor %s' % (master, self.origin_name(master))) else: # Create 'master' branch info('Creating branch %r' % master) c = self.repo.index.commit('Initial commit', head=False) self.repo.create_head(master, c) def _init_develop_branch(self): # assert master already exists assert self.master_name() in self.repo.refs develop = self.develop_name() if develop in self.repo.branches: # local `develop` branch exists, but do not switch there return if self.origin_name(develop) in self.repo.refs: # the origin branch counterpart exists origin = self.repo.refs[self.origin_name(develop)] branch = self.repo.create_head(develop, origin) branch.set_tracking_branch(origin) else: # Create 'develop' branch info('Creating branch %r' % develop) branch = self.repo.create_head(develop, self.master()) # switch to develop branch if its newly created info('Switching to branch %s' % branch) branch.checkout() def _enforce_git_repo(self): """ Ensure a (maybe empty) repository exists we can work on. This is to be used by the `init` sub-command. """ if self.repo is None: self.git.init(self.working_dir) self.repo = Repo(self.working_dir) def init(self, master=None, develop=None, prefixes={}, names={}, force_defaults=False): self._enforce_git_repo() self._init_config(master, develop, prefixes, names, force_defaults) self._init_initial_commit() self._init_develop_branch() return self def is_initialized(self): return (self.repo and self.is_set('gitflow.branch.master') and self.is_set('gitflow.branch.develop') and self.is_set('gitflow.prefix.feature') and self.is_set('gitflow.prefix.release') and self.is_set('gitflow.prefix.hotfix') and self.is_set('gitflow.prefix.support') and self.is_set('gitflow.prefix.versiontag')) def _parse_setting(self, setting): groups = setting.split('.', 2) if len(groups) == 2: section, option = groups elif len(groups) == 3: section, subsection, option = groups section = '%s "%s"' % (section, subsection) else: raise ValueError('Invalid setting name: %s' % setting) return (section, option) @requires_repo def get(self, setting, default=_NONE): section, option = self._parse_setting(setting) try: return self.repo.config_reader().get_value(section, option) except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): if default is not _NONE: return default raise def get_prefix(self, identifier): return self._safe_get('gitflow.prefix.%s' % (identifier, )) @requires_repo def set(self, setting, value): section, option = self._parse_setting(setting) self.repo.config_writer().set_value(section, option, value) def is_set(self, setting): return self.get(setting, None) is not None @requires_repo def _safe_get(self, setting_name): try: return self.get(setting_name) except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): raise NotInitialized('This repo has not yet been initialized.') def master_name(self): return self._safe_get('gitflow.branch.master') def develop_name(self): return self._safe_get('gitflow.branch.develop') def origin_name(self, name=None): origin = self.get('gitflow.origin', self.defaults['gitflow.origin']) if name is not None: return origin + '/' + name else: return origin @requires_repo def require_remote(self, name): try: return self.repo.remotes[name] except IndexError: raise NoSuchRemoteError(name) def origin(self): return self.require_remote(self.origin_name()) @requires_repo def develop(self): return self.repo.branches[self.develop_name()] @requires_repo def master(self): return self.repo.branches[self.master_name()] @requires_repo def branch_names(self, remote=False): if remote: return [ r.name for r in self.repo.refs if isinstance(r, RemoteReference) ] else: return [r.name for r in self.repo.branches] @requires_repo def nameprefix_or_current(self, identifier, prefix): """ :param identifier: The identifier for the type of branch to create. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param prefix: If the empty, see if the current branch is of type `identifier`. If so, returns the current branches short name, otherwise raises :exc:`NoSuchBranchError`. If exactly one branch of type `identifier` starts with the given name `prefix`, returns that branches short name. Raises :exc:`NoSuchBranchError` in case no branch exists with the given prefix, or :exc:`PrefixNotUniqueError` in case multiple matches are found. """ repo = self.repo manager = self.managers[identifier] if not prefix: if repo.active_branch.name.startswith(manager.prefix): return manager.shorten(repo.active_branch.name) else: raise NoSuchBranchError('The current branch is no %s branch. ' 'Please specify one explicitly.' % identifier) return manager.shorten(manager.by_name_prefix(prefix).name) @requires_repo def name_or_current(self, identifier, name, must_exist=True): """ :param identifier: The identifier for the type of branch to create. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: If the `name` is empty, see if the current branch is of type `identifier`. If so, returns the current branches short name, otherwise raises :exc:`NoSuchBranchError`. :param must_exist: If `True` (the default), raises :exc:`NoSuchBranchError` in case no branch exists with the given `name`. Otherwise return the `name` unchanged. """ repo = self.repo manager = self.managers[identifier] if not name: if repo.active_branch.name.startswith(manager.prefix): return manager.shorten(repo.active_branch.name) else: raise NoSuchBranchError('The current branch is no %s branch. ' 'Please specify one explicitly.' % identifier) elif must_exist and not manager.full_name(name) in ( b.name for b in manager.list()): raise NoSuchBranchError('There is no %s branch named %s.' % (identifier, name)) return name @requires_repo def status(self): result = [] for b in self.repo.branches: tup = self.branch_info(b.name) result.append(tup) return result @requires_repo def branch_info(self, name): active_branch = self.repo.active_branch b = self.repo.heads[name] return (name, b.commit.hexsha, b == active_branch) @requires_repo def is_dirty(self): """ Returns whether or not the current working directory contains uncommitted changes. """ return self.repo.is_dirty() @requires_repo def has_staged_commits(self): """ Returns whether or not the current repo contains local changes checked into the index but not committed. """ return len(self.repo.index.diff(self.repo.head.commit)) > 0 @requires_repo def require_no_merge_conflict(self): """ Raises :exc:`MergeConflict` if the current working directory contains a merge conflict. """ try: git.Reference(self.repo, 'MERGE_HEAD', check_path=False).commit # reference exists, so there is a merge conflict raise MergeConflict() except ValueError: # no such reference, so there is no merge conflict pass def is_merged_into(self, commit, target_branch): """ Checks whether `commit` is successfully merged into branch `target_branch`. :param commit: The commit or branch that ought to be merged. This may be a full branch-name, a commit-hexsha or any of branch-, head-, reference- or commit-object. :param target_branch: The branch which should contain the commit. This may be a full branch-name, or any of branch-, head- or reference-object. """ try: commit = self.repo.rev_parse(str(commit)) except git.BadObject: raise BadObjectError(commit) if isinstance(target_branch, git.RemoteReference): target_branch = 'remotes/' + target_branch.name elif isinstance(target_branch, git.SymbolicReference): target_branch = target_branch.name # :todo: implement this more efficiently return target_branch in [ b.lstrip('* ') for b in self.git.branch('-a', '--contains', commit).splitlines() ] def must_be_uptodate(self, branch, fetch): remote_branch = self.origin_name(branch) if remote_branch in self.branch_names(remote=True): if fetch: self.origin().fetch(branch) self.require_branches_equal(branch, remote_branch) @requires_repo def _compare_branches(self, branch1, branch2): """ Tests whether branches and their 'origin' counterparts have diverged and need merging first. It returns error codes to provide more detail, like so: 0 Branch heads point to the same commit 1 First given branch needs fast-forwarding 2 Second given branch needs fast-forwarding 3 Branch needs a real merge 4 There is no merge base, i.e. the branches have no common ancestors """ try: commit1 = self.repo.rev_parse(branch1) commit2 = self.repo.rev_parse(branch2) except git.BadObject, e: raise NoSuchBranchError(e.args[0]) if commit1 == commit2: return 0 try: base = self.repo.git.merge_base(commit1, commit2) except GitCommandError: return 4 if base == commit1: return 1 elif base == commit2: return 2 else: return 3
def _get_branches(self): c_git = Git(str(self._conf.get('path_to_repo'))) branches = set() for branch in set(c_git.branch('--contains', self.hash).split('\n')): branches.add(branch.strip().replace('* ', '')) return branches
repos = [ repo for repo in get_public_repos(args.username, fork) if fork or not repo['fork'] ] # TODO: Add failsafe mechanism to print partial emails for repo in repos: printv('Cloning {}...'.format(repo['name'])) repo_authors = {} Git().clone(repo['ssh_url'] if fix else repo['clone_url']) g = Git(repo['repo_path']) # Hacky way of getting remote branches branches = g.branch('-r').split('\n') # Remove references e.g. "origin/HEAD -> origin/master" branches = filter(filter_branch, branches) # Strip empty spaces branches = [branch.strip() for branch in branches] if fix: chdir(repo['repo_path']) printv(getcwd()) # Get emails from each branch for branch in branches: system('git branch --track "{btrim}" "{b}" >/dev/null 2>/dev/null'. format(btrim=branch.replace('origin/', ''), b=branch))
class GitFlow(object): """ Creates a :class:`GitFlow` instance. :param working_dir: The directory where the Git repo is located. If not specified, the current working directory is used. When a :class:`GitFlow` class is instantiated, it auto-discovers all subclasses of :class:`gitflow.branches.BranchManager`, so there is no explicit registration required. """ def _discover_branch_managers(self): managers = {} for cls in itersubclasses(BranchManager): # TODO: Initialize managers with the gitflow branch prefixes managers[cls.identifier] = cls(self) return managers def __init__(self, working_dir='.'): # Allow Repos to be passed in instead of strings self.repo = None if isinstance(working_dir, Repo): self.working_dir = working_dir.working_dir else: self.working_dir = working_dir self.git = Git(self.working_dir) try: self.repo = Repo(self.working_dir) except InvalidGitRepositoryError: pass self.managers = self._discover_branch_managers() self.defaults = { 'gitflow.branch.master': 'master', 'gitflow.branch.develop': 'develop', 'gitflow.prefix.versiontag': '', 'gitflow.origin': 'origin', } for identifier, manager in self.managers.items(): self.defaults['gitflow.prefix.%s' % identifier] = manager.DEFAULT_PREFIX def _init_config(self, master=None, develop=None, prefixes={}, names={}, force_defaults=False): for setting, default in self.defaults.items(): if force_defaults: value = default elif setting == 'gitflow.branch.master': value = master elif setting == 'gitflow.branch.develop': value = develop elif setting.startswith('gitflow.prefix.'): name = setting[len('gitflow.prefix.'):] value = prefixes.get(name, None) else: name = setting[len('gitflow.'):] value = names.get(name, None) if value is None: value = self.get(setting, default) self.set(setting, value) def _init_initial_commit(self): master = self.master_name() if master in self.repo.branches: # local `master` branch exists return elif self.origin_name(master) in self.repo.refs: # the origin branch counterpart exists origin = self.repo.refs[self.origin_name(master)] branch = self.repo.create_head(master, origin) branch.set_tracking_branch(origin) elif self.repo.heads: raise NotImplementedError( 'Local and remote branches exist, but neither %s nor %s' % (master, self.origin_name(master))) else: # Create 'master' branch info('Creating branch %r' % master) c = self.repo.index.commit('Initial commit', head=False) self.repo.create_head(master, c) def _init_develop_branch(self): # assert master already exists assert self.master_name() in self.repo.refs develop = self.develop_name() if develop in self.repo.branches: # local `develop` branch exists, but do not switch there return if self.origin_name(develop) in self.repo.refs: # the origin branch counterpart exists origin = self.repo.refs[self.origin_name(develop)] branch = self.repo.create_head(develop, origin) branch.set_tracking_branch(origin) else: # Create 'develop' branch info('Creating branch %r' % develop) branch = self.repo.create_head(develop, self.master()) # switch to develop branch if its newly created info('Switching to branch %s' % branch) branch.checkout() def _enforce_git_repo(self): """ Ensure a (maybe empty) repository exists we can work on. This is to be used by the `init` sub-command. """ if self.repo is None: self.git.init(self.working_dir) self.repo = Repo(self.working_dir) def init(self, master=None, develop=None, prefixes={}, names={}, force_defaults=False): self._enforce_git_repo() self._init_config(master, develop, prefixes, names, force_defaults) self._init_initial_commit() self._init_develop_branch() return self def is_initialized(self): return (self.repo and self.is_set('gitflow.branch.master') and self.is_set('gitflow.branch.develop') and self.is_set('gitflow.prefix.feature') and self.is_set('gitflow.prefix.release') and self.is_set('gitflow.prefix.hotfix') and self.is_set('gitflow.prefix.support') and self.is_set('gitflow.prefix.versiontag')) def _parse_setting(self, setting): groups = setting.split('.', 2) if len(groups) == 2: section, option = groups elif len(groups) == 3: section, subsection, option = groups section = '%s "%s"' % (section, subsection) else: raise ValueError('Invalid setting name: %s' % setting) return (section, option) @requires_repo def get(self, setting, default=_NONE): section, option = self._parse_setting(setting) try: return self.repo.config_reader().get_value(section, option) except (NoSectionError, NoOptionError): if default is not _NONE: return default raise def get_prefix(self, identifier): return self._safe_get('gitflow.prefix.%s' % (identifier, )) @requires_repo def set(self, setting, value): section, option = self._parse_setting(setting) writer = self.repo.config_writer() writer.set_value(section, option, value) writer.release() del writer def is_set(self, setting): return self.get(setting, None) is not None @requires_repo def _safe_get(self, setting_name): try: return self.get(setting_name) except (NoSectionError, NoOptionError): raise NotInitialized('This repo has not yet been initialized.') def master_name(self): return self._safe_get('gitflow.branch.master') def develop_name(self): return self._safe_get('gitflow.branch.develop') def origin_name(self, name=None): origin = self.get('gitflow.origin', self.defaults['gitflow.origin']) if name is not None: return origin + '/' + name else: return origin @requires_repo def require_remote(self, name): try: return self.repo.remotes[name] except IndexError: raise NoSuchRemoteError(name) def origin(self): return self.require_remote(self.origin_name()) @requires_repo def develop(self): return self.repo.branches[self.develop_name()] @requires_repo def master(self): return self.repo.branches[self.master_name()] @requires_repo def branch_names(self, remote=False): if remote: return [ r.name for r in self.repo.refs if isinstance(r, RemoteReference) ] else: return [r.name for r in self.repo.branches] @requires_repo def nameprefix_or_current(self, identifier, prefix): """ :param identifier: The identifier for the type of branch to work on. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param prefix: If the empty, see if the current branch is of type `identifier`. If so, returns the current branches short name, otherwise raises :exc:`NoSuchBranchError`. :returns: If exactly one branch of type `identifier` starts with the given name `prefix`, returns that branches short name. Raises :exc:`NoSuchBranchError` in case no branch exists with the given prefix, or :exc:`PrefixNotUniqueError` in case multiple matches are found. """ repo = self.repo manager = self.managers[identifier] if not prefix: if repo.active_branch.name.startswith(manager.prefix): return manager.shorten(repo.active_branch.name) else: raise NoSuchBranchError('The current branch is no %s branch. ' 'Please specify one explicitly.' % identifier) return manager.shorten(manager.by_name_prefix(prefix).name) @requires_repo def name_or_current(self, identifier, name, must_exist=True): """ :param identifier: The identifier for the type of branch to work on. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: If the `name` is empty, see if the current branch is of type `identifier`. If so, returns the current branches short name, otherwise raises :exc:`NoSuchBranchError`. :param must_exist: If `True` (the default), raises :exc:`NoSuchBranchError` in case no branch exists with the given `name`. Otherwise return the `name` unchanged. """ repo = self.repo manager = self.managers[identifier] if not name: if repo.active_branch.name.startswith(manager.prefix): return manager.shorten(repo.active_branch.name) else: raise NoSuchBranchError('The current branch is no %s branch. ' 'Please specify one explicitly.' % identifier) elif must_exist and not manager.full_name(name) in ( b.name for b in manager.list()): raise NoSuchBranchError('There is no %s branch named %s.' % (identifier, name)) return name @requires_repo def status(self): result = [] for b in self.repo.branches: tup = self.branch_info(b.name) result.append(tup) return result @requires_repo def branch_info(self, name): active_branch = self.repo.active_branch b = self.repo.heads[name] return (name, b.commit.hexsha, b == active_branch) @requires_repo def is_dirty(self): """ Returns whether or not the current working directory contains uncommitted changes. """ return self.repo.is_dirty() @requires_repo def has_staged_commits(self): """ Returns whether or not the current repo contains local changes checked into the index but not committed. """ return len(self.repo.index.diff(self.repo.head.commit)) > 0 @requires_repo def require_no_merge_conflict(self): """ Raises :exc:`MergeConflict` if the current working directory contains a merge conflict. """ try: git.Reference(self.repo, 'MERGE_HEAD', check_path=False).commit # reference exists, so there is a merge conflict raise MergeConflict() except ValueError: # no such reference, so there is no merge conflict pass def is_merged_into(self, commit, target_branch): """ Checks whether `commit` is successfully merged into branch `target_branch`. :param commit: The commit or branch that ought to be merged. This may be a full branch-name, a commit-hexsha or any of branch-, head-, reference- or commit-object. :param target_branch: The branch which should contain the commit. This may be a full branch-name, or any of branch-, head- or reference-object. """ try: commit = self.repo.rev_parse(str(commit)) except (git.BadObject, git.BadName): raise BadObjectError(commit) if isinstance(target_branch, git.RemoteReference): target_branch = 'remotes/' + target_branch.name elif isinstance(target_branch, git.SymbolicReference): target_branch = target_branch.name # :todo: implement this more efficiently return target_branch in [ b.lstrip('* ') for b in self.git.branch('-a', '--contains', commit).splitlines() ] def must_be_uptodate(self, branch, fetch): remote_branch = self.origin_name(branch) if remote_branch in self.branch_names(remote=True): if fetch: self.origin().fetch(branch) self.require_branches_equal(branch, remote_branch) @requires_repo def _compare_branches(self, branch1, branch2): """ Tests whether branches and their 'origin' counterparts have diverged and need merging first. It returns error codes to provide more detail, like so: 0 Branch heads point to the same commit 1 First given branch needs fast-forwarding 2 Second given branch needs fast-forwarding 3 Branch needs a real merge 4 There is no merge base, i.e. the branches have no common ancestors """ try: commit1 = self.repo.rev_parse(branch1) commit2 = self.repo.rev_parse(branch2) except (git.BadObject, git.BadName) as e: raise NoSuchBranchError(e.args[0]) if commit1 == commit2: return 0 try: # merge_base() returns a list of Commit objects # this list will have at max one Commit # or it will be empty if no common merge base exists base = self.repo.merge_base(commit1, commit2)[0] except (GitCommandError, IndexError): return 4 if base == commit1: return 1 elif base == commit2: return 2 else: return 3 @requires_repo def require_branches_equal(self, branch1, branch2): status = self._compare_branches(branch1, branch2) if status == 0: # branches are equal return else: warn("Branches '%s' and '%s' have diverged." % (branch1, branch2)) if status == 1: raise SystemExit("And branch '%s' may be fast-forwarded." % branch1) elif status == 2: # Warn here, since there is no harm in being ahead warn("And local branch '%s' is ahead of '%s'." % (branch1, branch2)) else: raise SystemExit("Branches need merging first.") @requires_repo def start_transaction(self, message=None): if message: info(message) @requires_initialized def tag(self, tagname, commit, message=None, sign=False, signingkey=None): kwargs = {} if sign: kwargs['s'] = True if signingkey: kwargs['u'] = signingkey self.repo.create_tag(tagname, commit, message=message or None, **kwargs) # # ====== sub commands ===== # @requires_repo def list(self, identifier, arg0_name, verbose, use_tagname): """ List the all branches of the given type. If there are not branches of this type, raises :exc:`Usage` with an explanation on how to start a branch of this type. :param identifier: The identifier for the type of branch to work on. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param arg0_name: Name of the first argument for the command line to be put into the explanation on how to start a branch of this type. This typically is `name` or `version`. :param verbose: If True, give more information about the state of the branch: Whether it's ahead or behind it's default base, may be rebased, etc. :param use_tagname: If True, try to describe the state based on the next tag. """ repo = self.repo manager = self.managers[identifier] branches = manager.list() if not branches: raise Usage( 'No %s branches exist.' % identifier, 'You can start a new %s branch with the command:' % identifier, ' git flow %s start <%s> [<base>]' % (identifier, arg0_name)) # determine the longest branch name width = max(len(b.name) for b in branches) - len(manager.prefix) + 1 basebranch_sha = repo.branches[manager.default_base()].commit.hexsha for branch in branches: if repo.active_branch == branch: prefix = '* ' else: prefix = ' ' name = manager.shorten(branch.name) extra_info = '' if verbose: name = name.ljust(width) branch_sha = branch.commit.hexsha base_sha = repo.git.merge_base(basebranch_sha, branch_sha) if branch_sha == basebranch_sha: extra_info = '(no commits yet)' elif use_tagname: try: extra_info = self.git.name_rev('--tags', '--name-only', '--no-undefined', base_sha) extra_info = '(based on %s)' % extra_info except GitCommandError: pass if not extra_info: if base_sha == branch_sha: extra_info = '(is behind %s, may ff)' % manager.default_base( ) elif base_sha == basebranch_sha: extra_info = '(based on latest %s)' % manager.default_base( ) else: extra_info = '(may be rebased)' info(prefix + name + extra_info) @requires_initialized def create(self, identifier, name, base, fetch): """ Creates a branch of the given type, with the given short name. :param identifier: The identifier for the type of branch to create. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to create. :param base: The alternative base to branch off from. If not given, the default base for the given branch type is used. :returns: The newly created :class:`git.refs.Head` branch. """ return self.managers[identifier].create(name, base, fetch=fetch) @requires_initialized def finish(self, identifier, name, fetch, rebase, keep, force_delete, tagging_info): """ Finishes a branch of the given type, with the given short name. :param identifier: The identifier for the type of branch to finish. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to finish. """ mgr = self.managers[identifier] branch = mgr.by_name_prefix(name) try: self.require_no_merge_conflict() except MergeConflict as e: raise Usage( e, "You can then complete the finish by running it again:", " git flow %s finish %s" % (identifier, name)) return mgr.finish(mgr.shorten(branch.name), fetch=fetch, rebase=rebase, keep=keep, force_delete=force_delete, tagging_info=tagging_info) @requires_initialized def checkout(self, identifier, name): """ Checkout a branch of the given type, with the given short name. :param identifier: The identifier for the type of branch to checkout. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to checkout. :returns: The checked out :class:`git.refs.Head` branch. """ mgr = self.managers[identifier] branch = mgr.by_name_prefix(name) return branch.checkout() @requires_initialized def diff(self, identifier, name): """ Print the diff of changes since this branch branched off. :param identifier: The identifier for the type of branch to work on. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to work on. """ mgr = self.managers[identifier] full_name = mgr.full_name(name) base = self.git.merge_base(mgr.default_base(), full_name) print(self.git.diff('%s..%s' % (base, full_name))) @requires_initialized def rebase(self, identifier, name, interactive): """ Rebase a branch of the given type, with the given short name, on top of it's default base. :param identifier: The identifier for the type of branch to rebase. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to rebase. :param interactive: If True, do an interactive rebase. """ warn("Will try to rebase %s branch '%s' ..." % (identifier, name)) mgr = self.managers[identifier] mgr.full_name(name) # :todo: require_clean_working_tree self.checkout(identifier, name) args = [] if interactive: args.append('-i') args.append(mgr.default_base()) self.git.rebase(*args) @requires_initialized def publish(self, identifier, name): """ Publish a branch of the given type, with the given short name, to `origin` (or whatever is configured as `remote` for gitflow.) :param identifier: The identifier for the type of branch to publish. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to publish. :returns: The full name of the published branch. """ repo = self.repo mgr = self.managers[identifier] # sanity checks # :todo: require_clean_working_tree full_name = mgr.full_name(name) remote_name = self.origin_name(full_name) if full_name not in repo.branches: raise NoSuchBranchError(full_name) if remote_name in repo.refs: raise BranchExistsError(remote_name) # :todo: check if full_name already has a tracking branch # :todo: check if full_name already has the same tracking branch # create remote branch origin = self.origin() info = origin.push('%s:refs/heads/%s' % (full_name, full_name))[0] origin.fetch() # configure remote tracking repo.branches[full_name].set_tracking_branch(info.remote_ref) return full_name @requires_initialized def pull(self, identifier, remote, name): """ Pull a branch of the given type, with the given short name, from the given remote peer. :param identifier: The identifier for the type of branch to pull. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param remote: The remote to pull from. This must have been configured by `git remote add ...`. :param name: The friendly (short) name to pull. """ def avoid_accidental_cross_branch_action(branch_name): current_branch = repo.active_branch if branch_name != current_branch.name: warn("Trying to pull from '%s' while currently on branch '%s'." % (branch_name, current_branch)) raise SystemExit( "To avoid unintended merges, git-flow aborted.") repo = self.repo mgr = self.managers[identifier] full_name = mgr.full_name(name) # To avoid accidentally merging different feature branches # into each other, die if the current feature branch differs # from the requested $NAME argument. if repo.active_branch.name.startswith(self.get_prefix(identifier)): # We are on a local `identifier` branch already, so `full_name` # must be equal to the current branch. avoid_accidental_cross_branch_action(full_name) # :todo: require_clean_working_tree if full_name in self.repo.branches: # Again, avoid accidental merges avoid_accidental_cross_branch_action(full_name) # We already have a local branch called like this, so # simply pull the remote changes in self.require_remote(remote).pull(full_name) # :fixme: why is the branch not checked out here? info("Pulled %s's changes into %s." % (remote, full_name)) else: # Setup the non-tracking local branch clone for the first time self.require_remote(remote).fetch(full_name + ':' + full_name) repo.heads[full_name].checkout() info("Created local branch %s based on %s's %s." % (full_name, remote, full_name)) @requires_initialized def track(self, identifier, name): """ Track a branch of the given type, with the given short name, from `origin` (or whatever is configured as `remote` for gitflow.) :param identifier: The identifier for the type of branch to track. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to track. :param base: The alternative base to branch off from. If not given, the default base for the given branch type is used. :returns: The newly created :class:`git.refs.Head` branch. """ repo = self.repo mgr = self.managers[identifier] # sanity checks # :todo: require_clean_working_tree full_name = mgr.full_name(name) if full_name in repo.branches: raise BranchExistsError(full_name) self.origin().fetch(full_name) remote_branch = self.origin().refs[full_name] branch = repo.create_head(full_name, remote_branch) branch.set_tracking_branch(remote_branch) return branch.checkout()
class GitFlow(object): """ Creates a :class:`GitFlow` instance. :param working_dir: The directory where the Git repo is located. If not specified, the current working directory is used. When a :class:`GitFlow` class is instantiated, it auto-discovers all subclasses of :class:`gitflow.branches.BranchManager`, so there is no explicit registration required. """ def _discover_branch_managers(self): managers = {} for cls in itersubclasses(BranchManager): # TODO: Initialize managers with the gitflow branch prefixes managers[cls.identifier] = cls(self) return managers def __init__(self, working_dir='.'): # Allow Repos to be passed in instead of strings self.repo = None if isinstance(working_dir, Repo): self.working_dir = working_dir.working_dir else: self.working_dir = working_dir self.git = Git(self.working_dir) try: self.repo = Repo(self.working_dir) except InvalidGitRepositoryError: pass self.managers = self._discover_branch_managers() self.defaults = { 'gitflow.branch.master': 'master', 'gitflow.branch.develop': 'develop', 'gitflow.prefix.versiontag': '', 'gitflow.origin': 'origin', 'gitflow.release.versionmatcher': '[0-9]+([.][0-9]+){2}', 'gitflow.circleci.enabled': 'y', 'gitflow.pagination': '10', } for identifier, manager in self.managers.items(): self.defaults['gitflow.prefix.%s' % identifier] = manager.DEFAULT_PREFIX def _init_config(self, master=None, develop=None, prefixes={}, names={}, force_defaults=False): for setting, default in self.defaults.items(): if force_defaults: value = default elif setting == 'gitflow.branch.master': value = master elif setting == 'gitflow.branch.develop': value = develop elif setting.startswith('gitflow.prefix.'): name = setting[len('gitflow.prefix.'):] value = prefixes.get(name, None) else: name = setting[len('gitflow.'):] value = names.get(name, None) if value is None: value = self.get(setting, default) self.set(setting, value) def _init_initial_commit(self): master = self.master_name() if master in self.repo.branches: # local `master` branch exists return elif self.origin_name(master) in self.repo.refs: # the origin branch counterpart exists origin = self.repo.refs[self.origin_name(master)] branch = self.repo.create_head(master, origin) branch.set_tracking_branch(origin) elif self.repo.heads: raise NotImplementedError('Local and remote branches exist, ' 'but neither %s nor %s' % (master, self.origin_name(master) )) else: # Create 'master' branch info('Creating branch %r' % master) c = self.repo.index.commit('Initial commit', head=False) self.repo.create_head(master, c) def _init_develop_branch(self): # assert master already exists assert self.master_name() in self.repo.refs develop = self.develop_name() if develop in self.repo.branches: # local `develop` branch exists, but do not switch there return if self.origin_name(develop) in self.repo.refs: # the origin branch counterpart exists origin = self.repo.refs[self.origin_name(develop)] branch = self.repo.create_head(develop, origin) branch.set_tracking_branch(origin) else: # Create 'develop' branch info('Creating branch %r' % develop) branch = self.repo.create_head(develop, self.master()) # switch to develop branch if its newly created info('Switching to branch %s' % branch) branch.checkout() def _ensure_remote_develop_branch(self): # push the develop branch if it does not exist in origin remote_develop = self.origin_name(self.develop_name()) if not remote_develop in self.branch_names(remote=True): develop_name = self.develop_name() sys.stdout.write('Branch {0} not found in origin, pushing it now ... '.format(develop_name)) self.origin().push([develop_name]) print('OK') def _enforce_git_repo(self): """ Ensure a (maybe empty) repository exists we can work on. This is to be used by the `init` sub-command. """ if self.repo is None: self.git.init(self.working_dir) self.repo = Repo(self.working_dir) def _enforce_services(self): err = False for option in ('gitflow.pt.token', 'reviewboard.url', 'reviewboard.server'): try: value = self.get(option) if option == 'reviewboard.url' and not value.endswith('/'): err = True print """ Git config key 'reviewboard.url' must contain a trailing slash. Update your configuration by executing $ git config [--global] reviewboard.url %s """ % (value + '/') except Exception: try: if option == 'gitflow.pt.token': value = self.get('workflow.token') self.set('gitflow.pt.token', value) continue except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): pass err = True print """ Git config '%s' missing, please fill it in by executing $ git config [--global] %s <value> """ % (option, option) if err: raise SystemExit('Operation canceled.') def init(self, master=None, develop=None, prefixes={}, names={}, force_defaults=False): self._enforce_git_repo() self._init_config(master, develop, prefixes, names, force_defaults) self._init_initial_commit() self._init_develop_branch() self._ensure_remote_develop_branch() return self def is_initialized(self): return (self.repo and self.is_set('gitflow.branch.master') and self.is_set('gitflow.branch.develop') and self.is_set('gitflow.prefix.feature') and self.is_set('gitflow.prefix.release') and self.is_set('gitflow.prefix.hotfix') and self.is_set('gitflow.prefix.support') and self.is_set('gitflow.prefix.versiontag') and self.is_set('gitflow.release.versionmatcher') and self.is_set('gitflow.circleci.enabled') and self.is_set('gitflow.pagination')) def _parse_setting(self, setting): groups = setting.split('.', 2) if len(groups) == 2: section, option = groups elif len(groups) == 3: section, subsection, option = groups section = '%s "%s"' % (section, subsection) else: raise ValueError('Invalid setting name: %s' % setting) return (section, option) @requires_repo def get(self, setting, default=_NONE): section, option = self._parse_setting(setting) try: value = self.repo.config_reader().get_value(section, option) # git config value cannot contain '-', so it is encoded as # MAGIC_STRING and we have to replace it with '-' when we read a # value. if isinstance(value, basestring): value = value.replace(MAGIC_STRING, '-') return value except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): if default is not _NONE: return default raise def get_prefix(self, identifier): return self._safe_get('gitflow.prefix.%s' % (identifier,)) @requires_repo def set(self, setting, value): # git config value cannot contain '-', so we have to encode it. if isinstance(value, basestring): value = value.replace('-', MAGIC_STRING) section, option = self._parse_setting(setting) self.repo.config_writer().set_value(section, option, value) def is_set(self, setting): return self.get(setting, None) is not None @requires_repo def delete(self, setting): section, option = self._parse_setting(setting) self.repo.config_writer().remove(section, option) @requires_repo def _safe_get(self, setting_name): try: return self.get(setting_name) except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): raise NotInitialized('This repo has not yet been initialized.') def master_name(self): return self._safe_get('gitflow.branch.master') def develop_name(self): return self._safe_get('gitflow.branch.develop') def client_name(self): return 'client' def client_exists(self): client = self.client_name() return client in self.branch_names() or \ self.origin_name(client) in self.branch_names(remote=True) def origin_name(self, name=None): origin = self.get('gitflow.origin', self.defaults['gitflow.origin']) if name is not None: return origin + '/' + name else: return origin @requires_repo def current_branch(self): return self.repo.head.ref.name @requires_initialized def is_circleci_enabled(self): return self.get('gitflow.circleci.enabled').lower() != 'n' @requires_repo def require_remote(self, name): try: remote = self.repo.remotes[name] except IndexError: raise NoSuchRemoteError("Remote '{0}' was not found.".format(name)) # Monkey patch remote.fetch to fix a false negative assertion in GitPython. class RemoteWrapper(object): def __init__(self, remote): self._remote = remote def __str__(self): return self._remote.__str__() def __repr__(self): return self._remote.__repr__() def __eq__(self, other): return self._remote.__eq__(other) def __ne__(self, other): return self._remote.__ne__(other) def __hash__(self): return self._remote.__hash__() # For some reason __getattribute__ is making infinite recursion, # so let's use __getattr__. def __getattr__(self, name): return self._remote.__getattribute__(name) # This is the main monkey patching part. def fetch(self, refspec=None, progress=None, **kwargs): # Let's try 3 times... err = None for x in range(3): try: return self._remote.fetch(refspec=refspec, progress=progress, **kwargs) except AssertionError as e: err = e # If we are in '_get_fetch_info_from_stderr', # it's the broken assertion and we skip it. func_name = traceback.extract_stack()[-1][2] if func_name != '_get_fetch_info_from_stderr': raise e err = None # If we somehow get the same exception 3 times, just raise anyway. raise err return RemoteWrapper(remote) @requires_repo def require_origin_branch(self, branch_name): origin_name = self.origin_name(branch_name) refs = [r for r in self.repo.refs if isinstance(r, RemoteReference) and r.name == origin_name] if len(refs) != 0: assert len(refs) == 1 return refs[0] raise NoSuchBranchError( "Remote branch '{0}' was not found".format(origin_name)) def origin(self): return self.require_remote(self.origin_name()) @requires_repo def develop(self): return self.repo.branches[self.develop_name()] @requires_repo def master(self): return self.repo.branches[self.master_name()] @requires_repo def branch_names(self, remote=False): if remote: return [r.name for r in self.repo.refs if isinstance(r, RemoteReference)] else: return [r.name for r in self.repo.branches] @requires_repo def nameprefix_or_current(self, identifier, prefix): """ :param identifier: The identifier for the type of branch to create. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param prefix: If the empty, see if the current branch is of type `identifier`. If so, returns the current branches short name, otherwise raises :exc:`NoSuchBranchError`. If exactly one branch of type `identifier` starts with the given name `prefix`, returns that branches short name. Raises :exc:`NoSuchBranchError` in case no branch exists with the given prefix, or :exc:`PrefixNotUniqueError` in case multiple matches are found. """ repo = self.repo manager = self.managers[identifier] if not prefix: if repo.active_branch.name.startswith(manager.prefix): return manager.shorten(repo.active_branch.name) else: raise NoSuchBranchError('The current branch is no %s branch. ' 'Please specify one explicitly.' % identifier) branch = None try: branch = manager.by_name_prefix(prefix).name except NoSuchBranchError as ex: try: remote_branch = manager.by_name_prefix(prefix, remote=True).name branch = str(remote_branch)[len(self.origin_name())+1:] self.git.branch(branch, remote_branch) except NoSuchBranchError: raise ex return manager.shorten(branch) @requires_repo def name_or_current(self, identifier, name, must_exist=True): """ :param identifier: The identifier for the type of branch to create. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: If the `name` is empty, see if the current branch is of type `identifier`. If so, returns the current branches short name, otherwise raises :exc:`NoSuchBranchError`. :param must_exist: If `True` (the default), raises :exc:`NoSuchBranchError` in case no branch exists with the given `name`. Otherwise return the `name` unchanged. """ repo = self.repo manager = self.managers[identifier] if not name: if repo.active_branch.name.startswith(manager.prefix): return manager.shorten(repo.active_branch.name) else: raise NoSuchBranchError('The current branch is no %s branch. ' 'Please specify one explicitly.' % identifier) elif must_exist and not manager.full_name(name) in (b.name for b in manager.list()): raise NoSuchBranchError('There is no %s branch named %s.' % (identifier, name)) return name @requires_repo def status(self): result = [] for b in self.repo.branches: tup = self.branch_info(b.name) result.append(tup) return result @requires_repo def branch_info(self, name): active_branch = self.repo.active_branch b = self.repo.heads[name] return (name, b.commit.hexsha, b == active_branch) @requires_repo def is_dirty(self): """ Returns whether or not the current working directory contains uncommitted changes. """ return self.repo.is_dirty() @requires_repo def has_staged_commits(self): """ Returns whether or not the current repo contains local changes checked into the index but not committed. """ return len(self.repo.index.diff(self.repo.head.commit)) > 0 @requires_repo def require_no_merge_conflict(self): """ Raises :exc:`MergeConflict` if the current working directory contains a merge conflict. """ try: git.Reference(self.repo, 'MERGE_HEAD', check_path=False).commit # reference exists, so there is a merge conflict raise MergeConflict() except ValueError: # no such reference, so there is no merge conflict pass def is_merged_into(self, commit, target_branch): """ Checks whether `commit` is successfully merged into branch `target_branch`. :param commit: The commit or branch that ought to be merged. This may be a full branch-name, a commit-hexsha or any of branch-, head-, reference- or commit-object. :param target_branch: The branch which should contain the commit. This may be a full branch-name, or any of branch-, head- or reference-object. """ try: commit = self.repo.rev_parse(str(commit)) except git.BadObject: raise BadObjectError(commit) if isinstance(target_branch, git.RemoteReference): target_branch = 'remotes/' + target_branch.name elif isinstance(target_branch, git.SymbolicReference): target_branch = target_branch.name # :todo: implement this more efficiently return target_branch in [ b.lstrip('* ') for b in self.git.branch('-a', '--contains', commit).splitlines()] def must_be_uptodate(self, branch): remote_branch = self.origin_name(branch) if remote_branch in self.branch_names(remote=True): self.require_branches_equal(branch, remote_branch) @requires_repo def _compare_branches(self, branch1, branch2): """ Tests whether branches and their 'origin' counterparts have diverged and need merging first. It returns error codes to provide more detail, like so: 0 Branch heads point to the same commit 1 First given branch needs fast-forwarding 2 Second given branch needs fast-forwarding 3 Branch needs a real merge 4 There is no merge base, i.e. the branches have no common ancestors """ try: commit1 = self.repo.rev_parse(branch1) commit2 = self.repo.rev_parse(branch2) except git.BadObject, e: raise NoSuchBranchError('Branch {0} not found'.format(e.args[0])) if commit1 == commit2: return 0 try: base = self.repo.git.merge_base(commit1, commit2) except GitCommandError: return 4 if base == commit1: return 1 elif base == commit2: return 2 else: return 3
def main(): #make sure the directory is empty if not os.listdir(gitdirectory): repo = git.Repo.clone_from(giturl, gitdirectory, branch='master') else: repo = git.Repo(gitdirectory) repo2 = git.Repo(localdirectory) originalrepo = Git(gitdirectory) commits = list(repo.iter_commits("master", max_count=1000000)) tree = repo.head.commit.tree print(originalrepo.branch()) stay = True current_original_commit = 0 print( "quit to quit, current to check current commit sha, < for past one, > for future one, number for type commit number you want, \"complete\" to go back to a commit and update commits until up to newest and newest feature \"100commit\"" ) iterate = 0 iterate100 = 0 iteratevalue = 0 while stay: if iterate == 0 and iterate100 == 0: userinput = input("input: ") if userinput == "quit": stay = False elif userinput == "current": print(repo.head.commit) print(originalrepo.committed_date) print("commits in the past = " + str(current_original_commit)) continue elif userinput == ">": if current_original_commit > 0: current_original_commit -= 1 else: print( "you tried to go out of range, this is the newest commit") continue elif userinput == "<": if current_original_commit < len(commits) - 1: current_original_commit += 1 else: print( "you tried to go out of range, this is the oldest commit") continue elif userinput.isdigit(): if int(userinput) < len(commits) and int(userinput) >= 0: current_original_commit = int(userinput) else: print("you tried to go out of range, max range is: " + str(len(commits))) continue elif userinput == "complete" or iterate == 1: if iterate == 0: firstcommitnumber = int( input( "How far back would you like to go in commits? Input: " )) if firstcommitnumber > len(commits): print( "sorry, you have gone out of the scope of the project. There are " + str(len(commits)) + " total commits") else: start_time = time.time() current_original_commit = firstcommitnumber - 1 iterate = 1 if iterate == 1: if current_original_commit > 0: current_original_commit -= 1 else: time_elapsed = time.time() - start_time print( "You have reached the final newest commit (shown below) in " + str(time_elapsed)) iterate = 0 elif userinput == "100commit" or iterate100 == 1: if iterate100 == 0: firstcommitnumber = int( input( "How far back would you like to go in commits? Input: " )) if firstcommitnumber > len(commits): print( "sorry, you have gone out of the scope of the project. There are " + str(len(commits)) + " total commits") else: start_time = time.time() current_original_commit = firstcommitnumber - 1 iterate100 = 1 if iterate100 == 1: if current_original_commit > 0: if iteratevalue < 100: current_original_commit -= 1 iteratevalue += 1 else: confirmation = input( "type \"confirm\" to run the next 50 commits: ") if confirmation == "confirm": iteratevalue = 0 current_original_commit -= 1 else: time_elapsed = time.time() - start_time print( "You have reached the final newest commit (shown below) in " + str(time_elapsed)) iterate100 = 0 else: print("sorry, not recognised try again") continue originalrepo.checkout(commits[current_original_commit]) dcmp = dircmp(localdirectory, gitdirectory) #print("files and folders added:") add_diff_files(dcmp) #print("files and folders removed:") delete_diff_files(dcmp) #print("files and folders replaced:") merge_diff_files(dcmp) #print("DIFFERENCES" + str(dcmp.left_only) + str(dcmp.right_only) +str(dcmp.diff_files)) print("changes complete, starting commit number: " + str(current_original_commit) + " commit(s) from the newest commit. hash: " + str(commits[current_original_commit])) #try: repo2 = Repo(localdirectory) #repo2.git.push(force=True) #repo2.index.add('.') #repo2.git.add(update=True) repo2.git.add("-A") repo2.index.commit(str(current_original_commit)) #repo2.git.commit('-m', 'test commit', author='*****@*****.**') origin = repo2.remote(name='origin') origin.push() print("commit successful, pushing")
class _Preferences(object): def __init__(self): """ Preferences class to store and retrieve settings. """ self.data = {} self.load_prefs() self.dir = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..')) + '/' self.git = None self.repo = None try: self.git = Git(self.dir) self.repo = Repo(self.dir) except: pass def get_current_branch(self): """ Retrieves the current git branch of the repository. :return: current git branch. :rtype: string """ if self.git is None: return False try: return self.git.branch().split()[-1] except: print_error( "Failed to get current branch, is there a git repo setup?") return "" def get_remote_branches(self): """ Retrieve all git branches of the repository. :return: branches of the repository. :rtype: list """ if self.git is None: return False branches = [] try: # Get all remote branches (not only local) returnvalue = self.git.ls_remote('--heads').split() # Strip data for i in range(len(returnvalue)): if i % 2 != 0: # Get only branch name (last value) branches.append(returnvalue[i].split("/")[-1]) except: print_warning( "Failed to get remote branches, is there a git repo setup and do you have internet?" ) pass return branches def check_if_update(self): """ Checks git repository for available changes. :return: True if update is available, False if the command failed or no update available. :rtype: bool """ if self.git is None: return False try: # Update local git data self.git.fetch() except: print_warning( "Failed to fetch, is there a git repo setup and do you have internet?" ) return False # Retrieve git remote <-> local difference status status = self.git.status() # easy check to see if local is behind if status.find('behind') > 0: return True return False def update(self): """ Creates a update file flag and restarts the service. """ if self.git is None: return False # Create file to let deamon know it has to update before starting the server file = open(self.dir + 'update.var', 'w+') # restart service command = ['/usr/sbin/service', 'opsoro', 'restart'] #shell=FALSE for sudo to work. subprocess.call(command, shell=False) python = sys.executable os.execl(python, python, *sys.argv) # # Reboot system used for user development server run # os.system('/sbin/shutdown -r now') def load_prefs(self): """ Load preferences into data. """ try: with open(get_path("config/preferences.yaml")) as f: self.data = yaml.load(f, Loader=Loader) except IOError: self.data = {} print_warning("Could not open config/preferences.yaml") print_info("Preferences loaded") def get(self, section, item, default): """ Retrieve preference value. :param string section: category in which the item is defined. :param string item: item to retrieve. :param default: default value to return if the value is not available. :return: preference value """ return self.data.get(section, {}).get(item, default) def set(self, section, item, value): """ Set preference value. :param string section: category in which the item is defined. :param string item: item to set. :param value: value to set. """ if value is None: return try: self.data[section][item] = value except KeyError: self.data[section] = {item: value} def save_prefs(self): """ Saves preferences to yaml file. """ try: with open(get_path("config/preferences.yaml"), "w") as f: f.write( yaml.dump(self.data, default_flow_style=False, Dumper=Dumper)) except IOError: print_warning("Could not save config/preferences.yaml") def apply_prefs(self, update_audio=False, update_wireless=False, restart_wireless=False, update_dns=False): """ Apply preferences to the system. :param bool update_audio: True if audio settings have changed and needs to update. :param bool update_wireless: True if wireless settings have changed and the wireless interface needs to update. :param bool restart_wireless: True if wireless settings have changed and the wireless interface needs to restart. :param bool update_dns: True if DNS settings have changed and needs to update. """ def change_conf_setting(txt, name, val): pattern = "^%s([ \t]*)=([ \t]*).*$" % name new = "%s=%s" % (name, val) txt, subs = re.subn(pattern, new, txt, flags=re.MULTILINE) if subs == 0: if txt[-1:] != "\n": txt = txt + "\n" txt = txt + new return txt FNULL = open(os.devnull, "w") if update_audio: vol = self.get("audio", "master_volume", 66) vol = constrain(vol, 0, 100) subprocess.Popen(["amixer", "sset", "'Master'", "%d%%" % vol], stdout=FNULL, stderr=subprocess.STDOUT) if update_wireless: with open("/etc/hostapd/hostapd.conf", "r+") as f: lines = f.read() ssid = self.get("wireless", "ssid", "OPSORO-bot") password = self.get("wireless", "password", "opsoro123") channel = self.get("wireless", "channel", 6) channel = constrain(int(channel), 1, 11) lines = change_conf_setting(lines, "ssid", ssid) lines = change_conf_setting(lines, "wpa_passphrase", password) lines = change_conf_setting(lines, "channel", channel) f.seek(0) f.write(lines) f.truncate() if restart_wireless: subprocess.Popen(["service", "hostapd", "restart"], stdout=FNULL, stderr=subprocess.STDOUT) if update_dns: with open("/etc/dnsmasq.d/dnsmasq.opsoro.conf", "r+") as f: lines = f.read() # redirect = self.get("wireless", "ssid", "OPSORO-bot") # password = self.get("wireless", "password", "opsoro123") # channel = self.get("wireless", "channel", 6) # channel = constrain(int(channel), 1, 11) address = "/play.opsoro.be/192.168.42.1" dhcp = "interface:wlan0,192.168.42.100,192.168.42.200,infinite" lines = change_conf_setting(lines, "address", address) lines = change_conf_setting(lines, "dhcp-range", dhcp) f.seek(0) f.write(lines) f.truncate() subprocess.Popen(["service", "dnsmasq", "restart"], stdout=FNULL, stderr=subprocess.STDOUT)
class GitFlow(object): """ Creates a :class:`GitFlow` instance. :param working_dir: The directory where the Git repo is located. If not specified, the current working directory is used. When a :class:`GitFlow` class is instantiated, it auto-discovers all subclasses of :class:`gitflow.branches.BranchManager`, so there is no explicit registration required. """ def _discover_branch_managers(self): managers = {} for cls in itersubclasses(BranchManager): # TODO: Initialize managers with the gitflow branch prefixes managers[cls.identifier] = cls(self) return managers def __init__(self, working_dir='.'): # Allow Repos to be passed in instead of strings self.repo = None if isinstance(working_dir, Repo): self.working_dir = working_dir.working_dir else: self.working_dir = working_dir self.git = Git(self.working_dir) try: self.repo = Repo(self.working_dir) except InvalidGitRepositoryError: pass self.managers = self._discover_branch_managers() self.defaults = { 'gitflow.branch.master': 'master', 'gitflow.branch.develop': 'develop', 'gitflow.prefix.versiontag': '', 'gitflow.origin': 'origin', } for identifier, manager in self.managers.items(): self.defaults['gitflow.prefix.%s' % identifier] = manager.DEFAULT_PREFIX def _init_config(self, master=None, develop=None, prefixes={}, names={}, force_defaults=False): for setting, default in self.defaults.items(): if force_defaults: value = default elif setting == 'gitflow.branch.master': value = master elif setting == 'gitflow.branch.develop': value = develop elif setting.startswith('gitflow.prefix.'): name = setting[len('gitflow.prefix.'):] value = prefixes.get(name, None) else: name = setting[len('gitflow.'):] value = names.get(name, None) if value is None: value = self.get(setting, default) self.set(setting, value) def _init_initial_commit(self): master = self.master_name() if master in self.repo.branches: # local `master` branch exists return elif self.origin_name(master) in self.repo.refs: # the origin branch counterpart exists origin = self.repo.refs[self.origin_name(master)] branch = self.repo.create_head(master, origin) branch.set_tracking_branch(origin) elif self.repo.heads: raise NotImplementedError( 'Local and remote branches exist, but neither %s nor %s' % ( master, self.origin_name(master) )) else: # Create 'master' branch info('Creating branch %r' % master) c = self.repo.index.commit('Initial commit', head=False) self.repo.create_head(master, c) def _init_develop_branch(self): # assert master already exists assert self.master_name() in self.repo.refs develop = self.develop_name() if develop in self.repo.branches: # local `develop` branch exists, but do not switch there return if self.origin_name(develop) in self.repo.refs: # the origin branch counterpart exists origin = self.repo.refs[self.origin_name(develop)] branch = self.repo.create_head(develop, origin) branch.set_tracking_branch(origin) else: # Create 'develop' branch info('Creating branch %r' % develop) branch = self.repo.create_head(develop, self.master()) # switch to develop branch if its newly created info('Switching to branch %s' % branch) branch.checkout() def _enforce_git_repo(self): """ Ensure a (maybe empty) repository exists we can work on. This is to be used by the `init` sub-command. """ if self.repo is None: self.git.init(self.working_dir) self.repo = Repo(self.working_dir) def init(self, master=None, develop=None, prefixes={}, names={}, force_defaults=False): self._enforce_git_repo() self._init_config(master, develop, prefixes, names, force_defaults) self._init_initial_commit() self._init_develop_branch() return self def is_initialized(self): return (self.repo and self.is_set('gitflow.branch.master') and self.is_set('gitflow.branch.develop') and self.is_set('gitflow.prefix.feature') and self.is_set('gitflow.prefix.release') and self.is_set('gitflow.prefix.hotfix') and self.is_set('gitflow.prefix.support') and self.is_set('gitflow.prefix.versiontag')) def _parse_setting(self, setting): groups = setting.split('.', 2) if len(groups) == 2: section, option = groups elif len(groups) == 3: section, subsection, option = groups section = '%s "%s"' % (section, subsection) else: raise ValueError('Invalid setting name: %s' % setting) return (section, option) @requires_repo def get(self, setting, default=_NONE): section, option = self._parse_setting(setting) try: return self.repo.config_reader().get_value(section, option) except (NoSectionError, NoOptionError): if default is not _NONE: return default raise def get_prefix(self, identifier): return self._safe_get('gitflow.prefix.%s' % (identifier,)) @requires_repo def set(self, setting, value): section, option = self._parse_setting(setting) writer = self.repo.config_writer() writer.set_value(section, option, value) writer.release() del writer def is_set(self, setting): return self.get(setting, None) is not None @requires_repo def _safe_get(self, setting_name): try: return self.get(setting_name) except (NoSectionError, NoOptionError): raise NotInitialized('This repo has not yet been initialized.') def master_name(self): return self._safe_get('gitflow.branch.master') def develop_name(self): return self._safe_get('gitflow.branch.develop') def origin_name(self, name=None): origin = self.get('gitflow.origin', self.defaults['gitflow.origin']) if name is not None: return origin + '/' + name else: return origin @requires_repo def require_remote(self, name): try: return self.repo.remotes[name] except IndexError: raise NoSuchRemoteError(name) def origin(self): return self.require_remote(self.origin_name()) @requires_repo def develop(self): return self.repo.branches[self.develop_name()] @requires_repo def master(self): return self.repo.branches[self.master_name()] @requires_repo def branch_names(self, remote=False): if remote: return [r.name for r in self.repo.refs if isinstance(r, RemoteReference)] else: return [r.name for r in self.repo.branches] @requires_repo def nameprefix_or_current(self, identifier, prefix): """ :param identifier: The identifier for the type of branch to work on. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param prefix: If the empty, see if the current branch is of type `identifier`. If so, returns the current branches short name, otherwise raises :exc:`NoSuchBranchError`. :returns: If exactly one branch of type `identifier` starts with the given name `prefix`, returns that branches short name. Raises :exc:`NoSuchBranchError` in case no branch exists with the given prefix, or :exc:`PrefixNotUniqueError` in case multiple matches are found. """ repo = self.repo manager = self.managers[identifier] if not prefix: if repo.active_branch.name.startswith(manager.prefix): return manager.shorten(repo.active_branch.name) else: raise NoSuchBranchError( 'The current branch is no %s branch. ' 'Please specify one explicitly.' % identifier) return manager.shorten(manager.by_name_prefix(prefix).name) @requires_repo def name_or_current(self, identifier, name, must_exist=True): """ :param identifier: The identifier for the type of branch to work on. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: If the `name` is empty, see if the current branch is of type `identifier`. If so, returns the current branches short name, otherwise raises :exc:`NoSuchBranchError`. :param must_exist: If `True` (the default), raises :exc:`NoSuchBranchError` in case no branch exists with the given `name`. Otherwise return the `name` unchanged. """ repo = self.repo manager = self.managers[identifier] if not name: if repo.active_branch.name.startswith(manager.prefix): return manager.shorten(repo.active_branch.name) else: raise NoSuchBranchError( 'The current branch is no %s branch. ' 'Please specify one explicitly.' % identifier) elif must_exist and not manager.full_name(name) in (b.name for b in manager.list()): raise NoSuchBranchError('There is no %s branch named %s.' % (identifier, name)) return name @requires_repo def status(self): result = [] for b in self.repo.branches: tup = self.branch_info(b.name) result.append(tup) return result @requires_repo def branch_info(self, name): active_branch = self.repo.active_branch b = self.repo.heads[name] return (name, b.commit.hexsha, b == active_branch) @requires_repo def is_dirty(self): """ Returns whether or not the current working directory contains uncommitted changes. """ return self.repo.is_dirty() @requires_repo def has_staged_commits(self): """ Returns whether or not the current repo contains local changes checked into the index but not committed. """ return len(self.repo.index.diff(self.repo.head.commit)) > 0 @requires_repo def require_no_merge_conflict(self): """ Raises :exc:`MergeConflict` if the current working directory contains a merge conflict. """ try: git.Reference(self.repo, 'MERGE_HEAD', check_path=False).commit # reference exists, so there is a merge conflict raise MergeConflict() except ValueError: # no such reference, so there is no merge conflict pass def is_merged_into(self, commit, target_branch): """ Checks whether `commit` is successfully merged into branch `target_branch`. :param commit: The commit or branch that ought to be merged. This may be a full branch-name, a commit-hexsha or any of branch-, head-, reference- or commit-object. :param target_branch: The branch which should contain the commit. This may be a full branch-name, or any of branch-, head- or reference-object. """ try: commit = self.repo.rev_parse(str(commit)) except (git.BadObject, git.BadName): raise BadObjectError(commit) if isinstance(target_branch, git.RemoteReference): target_branch = 'remotes/' + target_branch.name elif isinstance(target_branch, git.SymbolicReference): target_branch = target_branch.name # :todo: implement this more efficiently return target_branch in [ b.lstrip('* ') for b in self.git.branch('-a', '--contains', commit).splitlines()] def must_be_uptodate(self, branch, fetch): remote_branch = self.origin_name(branch) if remote_branch in self.branch_names(remote=True): if fetch: self.origin().fetch(branch) self.require_branches_equal(branch, remote_branch) @requires_repo def _compare_branches(self, branch1, branch2): """ Tests whether branches and their 'origin' counterparts have diverged and need merging first. It returns error codes to provide more detail, like so: 0 Branch heads point to the same commit 1 First given branch needs fast-forwarding 2 Second given branch needs fast-forwarding 3 Branch needs a real merge 4 There is no merge base, i.e. the branches have no common ancestors """ try: commit1 = self.repo.rev_parse(branch1) commit2 = self.repo.rev_parse(branch2) except (git.BadObject, git.BadName) as e: raise NoSuchBranchError(e.args[0]) if commit1 == commit2: return 0 try: # merge_base() returns a list of Commit objects # this list will have at max one Commit # or it will be empty if no common merge base exists base = self.repo.merge_base(commit1, commit2)[0] except (GitCommandError, IndexError): return 4 if base == commit1: return 1 elif base == commit2: return 2 else: return 3 @requires_repo def require_branches_equal(self, branch1, branch2): status = self._compare_branches(branch1, branch2) if status == 0: # branches are equal return else: warn("Branches '%s' and '%s' have diverged." % (branch1, branch2)) if status == 1: raise SystemExit("And branch '%s' may be fast-forwarded." % branch1) elif status == 2: # Warn here, since there is no harm in being ahead warn("And local branch '%s' is ahead of '%s'." % (branch1, branch2)) else: raise SystemExit("Branches need merging first.") @requires_repo def start_transaction(self, message=None): if message: info(message) @requires_initialized def tag(self, tagname, commit, message=None, sign=False, signingkey=None): kwargs = {} if sign: kwargs['s'] = True if signingkey: kwargs['u'] = signingkey self.repo.create_tag(tagname, commit, message=message or None, **kwargs) # # ====== sub commands ===== # @requires_repo def list(self, identifier, arg0_name, verbose, use_tagname): """ List the all branches of the given type. If there are not branches of this type, raises :exc:`Usage` with an explanation on how to start a branch of this type. :param identifier: The identifier for the type of branch to work on. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param arg0_name: Name of the first argument for the command line to be put into the explanation on how to start a branch of this type. This typically is `name` or `version`. :param verbose: If True, give more information about the state of the branch: Whether it's ahead or behind it's default base, may be rebased, etc. :param use_tagname: If True, try to describe the state based on the next tag. """ repo = self.repo manager = self.managers[identifier] branches = manager.list() if not branches: raise Usage( 'No %s branches exist.' % identifier, 'You can start a new %s branch with the command:' % identifier, ' git flow %s start <%s> [<base>]' % (identifier, arg0_name) ) # determine the longest branch name width = max(len(b.name) for b in branches) - len(manager.prefix) + 1 basebranch_sha = repo.branches[manager.default_base()].commit.hexsha for branch in branches: if repo.active_branch == branch: prefix = '* ' else: prefix = ' ' name = manager.shorten(branch.name) extra_info = '' if verbose: name = name.ljust(width) branch_sha = branch.commit.hexsha base_sha = repo.git.merge_base(basebranch_sha, branch_sha) if branch_sha == basebranch_sha: extra_info = '(no commits yet)' elif use_tagname: try: extra_info = self.git.name_rev('--tags', '--name-only', '--no-undefined', base_sha) extra_info = '(based on %s)' % extra_info except GitCommandError: pass if not extra_info: if base_sha == branch_sha: extra_info = '(is behind %s, may ff)' % manager.default_base() elif base_sha == basebranch_sha: extra_info = '(based on latest %s)' % manager.default_base() else: extra_info = '(may be rebased)' info(prefix + name + extra_info) @requires_initialized def create(self, identifier, name, base, fetch): """ Creates a branch of the given type, with the given short name. :param identifier: The identifier for the type of branch to create. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to create. :param base: The alternative base to branch off from. If not given, the default base for the given branch type is used. :returns: The newly created :class:`git.refs.Head` branch. """ return self.managers[identifier].create(name, base, fetch=fetch) @requires_initialized def finish(self, identifier, name, fetch, rebase, keep, force_delete, tagging_info): """ Finishes a branch of the given type, with the given short name. :param identifier: The identifier for the type of branch to finish. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to finish. """ mgr = self.managers[identifier] branch = mgr.by_name_prefix(name) try: self.require_no_merge_conflict() except MergeConflict as e: raise Usage(e, "You can then complete the finish by running it again:", " git flow %s finish %s" % (identifier, name) ) return mgr.finish(mgr.shorten(branch.name), fetch=fetch, rebase=rebase, keep=keep, force_delete=force_delete, tagging_info=tagging_info) @requires_initialized def checkout(self, identifier, name): """ Checkout a branch of the given type, with the given short name. :param identifier: The identifier for the type of branch to checkout. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to checkout. :returns: The checked out :class:`git.refs.Head` branch. """ mgr = self.managers[identifier] branch = mgr.by_name_prefix(name) return branch.checkout() @requires_initialized def diff(self, identifier, name): """ Print the diff of changes since this branch branched off. :param identifier: The identifier for the type of branch to work on. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to work on. """ mgr = self.managers[identifier] full_name = mgr.full_name(name) base = self.git.merge_base(mgr.default_base(), full_name) print(self.git.diff('%s..%s' % (base, full_name))) @requires_initialized def rebase(self, identifier, name, interactive): """ Rebase a branch of the given type, with the given short name, on top of it's default base. :param identifier: The identifier for the type of branch to rebase. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to rebase. :param interactive: If True, do an interactive rebase. """ warn("Will try to rebase %s branch '%s' ..." % (identifier, name)) mgr = self.managers[identifier] mgr.full_name(name) # :todo: require_clean_working_tree self.checkout(identifier, name) args = [] if interactive: args.append('-i') args.append(mgr.default_base()) self.git.rebase(*args) @requires_initialized def publish(self, identifier, name): """ Publish a branch of the given type, with the given short name, to `origin` (or whatever is configured as `remote` for gitflow.) :param identifier: The identifier for the type of branch to publish. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to publish. :returns: The full name of the published branch. """ repo = self.repo mgr = self.managers[identifier] # sanity checks # :todo: require_clean_working_tree full_name = mgr.full_name(name) remote_name = self.origin_name(full_name) if full_name not in repo.branches: raise NoSuchBranchError(full_name) if remote_name in repo.refs: raise BranchExistsError(remote_name) # :todo: check if full_name already has a tracking branch # :todo: check if full_name already has the same tracking branch # create remote branch origin = self.origin() info = origin.push('%s:refs/heads/%s' % (full_name, full_name))[0] origin.fetch() # configure remote tracking repo.branches[full_name].set_tracking_branch(info.remote_ref) return full_name @requires_initialized def pull(self, identifier, remote, name): """ Pull a branch of the given type, with the given short name, from the given remote peer. :param identifier: The identifier for the type of branch to pull. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param remote: The remote to pull from. This must have been configured by `git remote add ...`. :param name: The friendly (short) name to pull. """ def avoid_accidental_cross_branch_action(branch_name): current_branch = repo.active_branch if branch_name != current_branch.name: warn("Trying to pull from '%s' while currently on branch '%s'." % (branch_name, current_branch)) raise SystemExit("To avoid unintended merges, git-flow aborted.") repo = self.repo mgr = self.managers[identifier] full_name = mgr.full_name(name) # To avoid accidentally merging different feature branches # into each other, die if the current feature branch differs # from the requested $NAME argument. if repo.active_branch.name.startswith(self.get_prefix(identifier)): # We are on a local `identifier` branch already, so `full_name` # must be equal to the current branch. avoid_accidental_cross_branch_action(full_name) # :todo: require_clean_working_tree if full_name in self.repo.branches: # Again, avoid accidental merges avoid_accidental_cross_branch_action(full_name) # We already have a local branch called like this, so # simply pull the remote changes in self.require_remote(remote).pull(full_name) # :fixme: why is the branch not checked out here? info("Pulled %s's changes into %s." % (remote, full_name)) else: # Setup the non-tracking local branch clone for the first time self.require_remote(remote).fetch(full_name + ':' + full_name) repo.heads[full_name].checkout() info("Created local branch %s based on %s's %s." % (full_name, remote, full_name)) @requires_initialized def track(self, identifier, name): """ Track a branch of the given type, with the given short name, from `origin` (or whatever is configured as `remote` for gitflow.) :param identifier: The identifier for the type of branch to track. A :class:`BranchManager <git.branches.BranchManager>` for the given identifier must exist in the :attr:`self.managers`. :param name: The friendly (short) name to track. :param base: The alternative base to branch off from. If not given, the default base for the given branch type is used. :returns: The newly created :class:`git.refs.Head` branch. """ repo = self.repo mgr = self.managers[identifier] # sanity checks # :todo: require_clean_working_tree full_name = mgr.full_name(name) if full_name in repo.branches: raise BranchExistsError(full_name) self.origin().fetch(full_name) remote_branch = self.origin().refs[full_name] branch = repo.create_head(full_name, remote_branch) branch.set_tracking_branch(remote_branch) return branch.checkout()
'--list-of-pos', help= 'A list of dvc-cc indizes that you want include in the display. You can also use slicing for example: 12:15:2 to use 12, 14.', nargs="+", type=str) parser.add_argument( '-e', '--print-error', help= 'If this parameter is set, it will print the error message, why a file or folder could not be found.', action='store_true') args = parser.parse_args() repo = DVCRepo() g = Git() starting_branch = g.branch().split('*')[1].split('\n')[0][1:] # Set the password only once! remote_name = repo.config['core']['remote'] remote_settings = repo.config['remote'][remote_name] if 'ask_password' in remote_settings and remote_settings['ask_password']: remote_settings['password'] = getpass.getpass('Password for ' + remote_settings['url'] + ': ') remote_settings['ask_password'] = False path_to_output_clean = args.path_to_output.replace('./', '_').replace( '/', '_').replace('\\\\', '_') outputdir = create_output_dir(repo.root_dir, path_to_output_clean) print('##################', outputdir) if outputdir is None:
def build_command(config_file, strict, site_dir, branches, default_branch, latest, logging_level): """Build the MkDocs documentation""" # cli.configure_logging(level=logging.INFO) global release_branches logging.basicConfig( stream=sys.stdout, level=get_logging_level(logging_level), format= '%(asctime)s %(levelname)s [%(threadName)s] [%(filename)s:%(lineno)d] %(message)s' ) g = Git() repo = Repo() branches = branches or g.branch('-r').splitlines() all_branch_names = list( map(lambda branch: branch.split("origin/")[1], branches)) active_branch = repo.active_branch.name print("Active branch %s", active_branch) print("Default branch %s", default_branch) print("Latest branch %s", latest) start_stashes_count = len( re.findall("stash@{[0-9]{1,3}}:", repo.git.stash("list"))) repo.git.stash("save") if active_branch != latest: print("Checkout Default %s", active_branch) g.checkout(default_branch) default_config = _load_config(config_file, strict, site_dir) versions = default_config.get("extra").get("versions") formatedCSVersions = {} jelasticVersions = [] for version in versions: formatedCSVersions[unicode(version)] = versions[version] if formatedCSVersions is not None: release_branches = formatedCSVersions.keys() jelasticVersions = formatedCSVersions.values() if release_branches is not None: release_branches = sorted(release_branches, key=functools.cmp_to_key(version_compare)) jelasticVersions = sorted(jelasticVersions, key=functools.cmp_to_key(version_compare)) default_version = next(iter(release_branches), None) # release_branches[-1] print("Default version %s", default_version) print("Building %s to /", default_version) _build(default_config, default_version, release_branches, jelasticVersions) for branch in release_branches: if branch in all_branch_names: #branch != default_version and g.checkout(branch) g.pull() if not os.path.exists("mkdocs.yml"): log.warning( "Unable to build %s, as no mkdocs.yml was found", branch) print("Unable to build %s, as no mkdocs.yml was found", branch) continue site_dir = "{0}".format(branch) log.info("Building %s to /%s", branch, "site/" + site_dir) print("Building %s to /%s", branch, "site/" + site_dir) _build(_load_config(config_file, strict, site_dir), branch, release_branches, jelasticVersions, "site/" + site_dir) # print("Selected Branches %s", default_config.get("versions").get("releases")) print("Checkout branch %s", active_branch) g.checkout("master") end_stashes_count = len( re.findall("stash@{[0-9]{1,3}}:", repo.git.stash("list"))) if end_stashes_count > start_stashes_count: repo.git.stash("pop") print("pop latest stash")
def _get_branches(self, git: Git, commit_hash: str) -> set(): branches = set() for branch in set(git.branch('--contains', commit_hash).split('\n')): branches.add(branch.strip().replace('* ', '')) return branches
def main(): parser = ArgumentParser(description=DESCRIPTION) parser.add_argument( 'path_to_output', help='The path to the output/metric file or folder that you want get.', type=str) parser.add_argument( '-p', '--list-of-pos', help= 'A list of dvc-cc indizes that you want include in the display. You can also use slicing for example: 12:15:2 to use 12, 14.', nargs="+", type=str) parser.add_argument( '-e', '--print-error', help= 'If this parameter is set, it will print the error message, why a file or folder could not be found.', action='store_true') args = parser.parse_args() repo = DVCRepo() g = Git() starting_branch = g.branch().split('*')[1].split('\n')[0][1:] # Set the password only once! remote_name = repo.config['core']['remote'] remote_settings = repo.config['remote'][remote_name] if 'ask_password' in remote_settings and remote_settings['ask_password']: remote_settings['password'] = getpass.getpass('Password for ' + remote_settings['url'] + ': ') remote_settings['ask_password'] = False path_to_output_clean = args.path_to_output.replace('./', '_').replace( '/', '_').replace('\\\\', '_') outputdir = create_output_dir(repo.root_dir, path_to_output_clean) print('##################', outputdir) if outputdir is None: exit(1) list_of_allowed_dvccc_ids = None if args.list_of_pos is not None: list_of_allowed_dvccc_ids = [] for pos in args.list_of_pos: try: if pos.find(':') > -1: pos = np.array(pos.split(':'), dtype=int) list_of_allowed_dvccc_ids.extend(np.arange(*pos)) else: pos = int(pos) if pos >= 0: list_of_allowed_dvccc_ids.append(pos) else: raise ValueError( 'ERROR: The parameters ' + str(pos) + ' from --list-of-pos must be positive.') except: raise ValueError( 'ERROR: The parameters ' + str(pos) + ' from --list-of-pos must be an integer or a slicings. i.e.1: 12 14 i.e.2: 12:15:2' ) list_of_allowed_dvccc_ids = np.array(list_of_allowed_dvccc_ids) all_branches = [ b.split('/')[-1] for b in g.branch('-a').split() if b.startswith('rcc_') ] all_branches = np.unique(all_branches) for b in all_branches: if list_of_allowed_dvccc_ids is None or int( b.split('_')[1]) in list_of_allowed_dvccc_ids: print('dvc get : ', '.', args.path_to_output, str(Path(outputdir + '/' + b)), b) try: if b.endswith('.dvc'): path_to_output = str(Path(outputdir + '/' + b[:-4])) print(path_to_output) os.mkdir(path_to_output) repo.get('.', args.path_to_output, out=str( Path(path_to_output + '/' + path_to_output_clean)), rev=b) else: path_to_output = str(Path(outputdir + '/' + b)) print(path_to_output) os.mkdir(path_to_output) repo.get('.', args.path_to_output, out=str( Path(path_to_output + '/' + path_to_output_clean)), rev=b) for p in os.listdir('/tmp'): if p.endswith('dvc-erepo'): shutil.rmtree('/tmp/' + p) except Exception as ex: print('File was not found.') os.rmdir(path_to_output) if args.print_error: print(ex) print() print('Found ' + str(len(os.listdir(str(Path(outputdir))))) + ' files or folders.') if len(os.listdir(str(Path(outputdir)))) == 0: print('Folder was removed.') os.rmdir(str(Path(outputdir))) print( 'If files are missing, please use "dvc-cc git sync" to get new result branches and repeat this command.' )
def main(): parser = ArgumentParser(description=DESCRIPTION) parser.add_argument( '-f', '--regex-name-of-file', type=str, default=None, help='A regex of the name of the files that you want to find.') parser.add_argument( '-ef', '--exclude-regex-name-of-file', type=str, default=None, help='A regex of the name of the file that are excluded.') parser.add_argument( '-b', '--regex-name-of-branch', type=str, default=None, help='A regex of the name of the branches to be included in the search.' ) parser.add_argument( '-eb', '--exclude-regex-name-of-branch', type=str, default=None, help='A regex of the name of the branch that are excluded.') parser.add_argument( '-pos', '--list-of-pos', help= 'A list of dvc-cc indizes that you want include in the display. You can also use slicing for example: 12:15:2 to use 12, 14.', nargs="+", type=str) parser.add_argument('-p', '--path-to-output', type=str, default=None, help='The path where you want save the files.') parser.add_argument( '-o', '--original-name', dest='original_name', action='store_true', default=False, help= 'In default, the branch name is added to the file or folder name. If this parameter is ' 'set, it will use the original name of the file or folder. If the file exists multiple' 'times and this parameter is set, then it will use indices at the end of the file or folder names.' ) parser.add_argument('--debug', dest='debug', action='store_true', default=False, help='Print all files that are copied.') parser.add_argument( '-d', '--download-stages', dest='download_stages', action='store_true', default=False, help='Download a stage if the file is not in the local cache.') parser.add_argument( '-fd', '--forbid-dir', dest='forbid_dir', action='store_true', default=False, help='If this parameter is set, then it will ignore output folders.') parser.add_argument( '-ns', '--no-save', dest='no_save', action='store_true', default=False, help= 'If true, it will not create a folder or link the file. This parameter is helpfull if it is used with --debug to test your regular expressions.' ) parser.add_argument( '-nw', '--no-print-of-warnings', dest='no_warning', action='store_true', default=False, help= 'If true, it will not print warning if a file is not created or not in the local cache.' ) args = parser.parse_args() repo = DVCRepo() g = Git() starting_branch = g.branch().split('*')[1].split('\n')[0][1:] # Set the password only once! if args.download_stages: remote_name = repo.config['core']['remote'] remote_settings = repo.config['remote'][remote_name] if 'ask_password' in remote_settings and remote_settings[ 'ask_password']: remote_settings['password'] = getpass.getpass( 'Password for ' + remote_settings['url'] + ': ') remote_settings['ask_password'] = False if not args.no_save: path_to_output = create_output_dir(repo.root_dir, args.path_to_output) if path_to_output is None: exit(1) else: path_to_output = 'NONE' list_of_allowed_dvccc_ids = None if args.list_of_pos is not None: list_of_allowed_dvccc_ids = [] for pos in args.list_of_pos: try: if pos.find(':') > -1: pos = np.array(pos.split(':'), dtype=int) list_of_allowed_dvccc_ids.extend(np.arange(*pos)) else: pos = int(pos) if pos >= 0: list_of_allowed_dvccc_ids.append(pos) else: raise ValueError( 'ERROR: The parameters ' + str(pos) + ' from --list-of-pos must be positive.') except: raise ValueError( 'ERROR: The parameters ' + str(pos) + ' from --list-of-pos must be an integer or a slicings. i.e.1: 12 14 i.e.2: 12:15:2' ) list_of_allowed_dvccc_ids = np.array(list_of_allowed_dvccc_ids) try: file_counter = 0 saved_files = {} for branch in repo.brancher(all_branches=True): outs = [] branch_names = [] if branch.lower() != 'working tree': # check if this is a result branch: is_dvccc_result_branch = branch.startswith('rcc_') # search for all output files in the current branch is_branch_of_interest1 = args.regex_name_of_branch is None or re.match( args.regex_name_of_branch, branch) is_branch_of_interest2 = args.exclude_regex_name_of_branch is None or not re.match( args.exclude_regex_name_of_branch, branch) is_allowed_dvccc_id = True if list_of_allowed_dvccc_ids is not None and is_dvccc_result_branch: if not int( branch.split('_')[1]) in list_of_allowed_dvccc_ids: is_allowed_dvccc_id = False if is_branch_of_interest1 and is_branch_of_interest2 and is_dvccc_result_branch and is_allowed_dvccc_id: print(branch) g.checkout(branch) #TODO: This would be nice, but its too sloow! try: repo.checkout() except: print('Some files are missing.') print('\tIt is a branch of interest!') #TODO: repo.stages is very slow! for stage in repo.stages: for out in stage.outs: valid_msg = check_out_if_its_valid( out, args.regex_name_of_file, args.exclude_regex_name_of_file, not args.forbid_dir) print('\t\t\t', out, valid_msg) if valid_msg == 'not_in_local_cache' and args.download_stages: g.pull() try: repo.pull(stage.relpath) except: print('Some files are missing.') time.sleep(1) valid_msg = check_out_if_its_valid( out, args.regex_name_of_file, args.exclude_regex_name_of_file, not args.forbid_dir) print(valid_msg) if valid_msg == 'valid': outs.append(out) branch_names.append(branch) elif valid_msg == 'not_created' and args.no_warning == False: print( 'Warning: A output file of interest has not yet been created. ' + '(file: ' + str(out) + '; branch: ' + branch + ')') elif valid_msg == 'not_in_local_cache' and args.no_warning == False: print( 'Warning: A output file of interest is not on the local cache. ' + '(file: ' + out.cache_path + '; branch: ' + branch + ')\n You can use this script with -d and it will download the missing stage.' ) # create a link for each output file of interest in the current branch for out, branch_name in zip(outs, branch_names): # create the output file name if not args.original_name: out_filename = branch_name + '_' + str(out).replace( '/', '_').replace('\\\\', '_') else: out_filename = str(out).replace('/', '_').replace( '\\\\', '_') out_filepath = os.path.join(repo.root_dir, path_to_output, out_filename) file_was_already_saved = False renamer_index = 2 file_can_be_saved = False tmp_out_filepath = out_filepath while not file_can_be_saved and not file_was_already_saved: if tmp_out_filepath not in saved_files: file_can_be_saved = True out_filepath = tmp_out_filepath saved_files[out_filepath] = out.checksum elif saved_files[tmp_out_filepath] == out.checksum: file_was_already_saved = True else: tmp_out_filepath = out_filepath + '_' + str( renamer_index) renamer_index += 1 if file_can_be_saved: if args.debug: print(out.cache_path, ' -> ', out_filepath) if args.no_save is False: if out.isfile(): os.link(out.cache_path, out_filepath) elif out.isdir(): os.mkdir(out_filepath) for cache in out.dir_cache: dirfile_cache_path = repo.cache.local.get( cache['md5']) dirfile_outpath = os.path.join( out_filepath, cache['relpath']) os.makedirs( os.path.dirname(dirfile_outpath), exist_ok=True) os.link(dirfile_cache_path, dirfile_outpath) file_counter += 1 print( str(file_counter) + ' files are linked to ' + path_to_output + '.') # return always to the starting branch! finally: g.checkout(starting_branch) try: repo.checkout() except: print('Some files are missing.')