async def on_message(message): if client.sleeping: if message.content == '!wake': client.sleeping = False await client.send_message(message.channel, 'SolBot online!') else: if message.content == '!sleep': client.sleeping = True await client.send_message(message.channel, 'Going to sleep...') elif message.content == '!update': g = Git(os.path.dirname(os.path.abspath(__file__))) tmp = await client.send_message(message.channel, 'Pulling new code...') g.pull() await client.edit_message(tmp, 'Code pulled. Restarting...') client.logout() os.execv(sys.executable, ['python3.5'] + sys.argv) elif message.content == '!gitstatus': g = Git(os.path.dirname(os.path.abspath(__file__))) tmp = await client.send_message(message.channel, 'Checking status...') g.fetch() p = re.compile('Your branch is.*by (\d+) commits.*') m = p.match(g.status()) if m: await client.edit_message(tmp, 'I am behind by {} commits'.format(m.group(1))) else: await client.edit_message(tmp, 'I am up to date!')
def branches(): g = Git(PROJECT_DIR) send('重新获取远程分支中.....') g.fetch(REMOTE_NAME) send('获取成功') branch_names = g.branch('-a').split('\n') return jsonify(branch_names)
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 fetch_and_checkout(config, repo): """Try to fetch the remote ref in the personal gerrit branch for the running user.""" g = Git(repo.working_tree_dir) try: g.fetch(['origin', config._state_ref]) g.checkout('FETCH_HEAD') except git.exc.GitCommandError as e: if "couldn't find remote ref refs/personal" in e.stderr: pass else: print(e) del g
def cloneOrCheckout(base_path, repo): git = Git() repo_path = os.path.join(base_path, repo['name']) print (repo_path) try: os.chdir(repo_path) except OSError: os.chdir(base_path) git.clone(repo['base_url'] + repo['name']) os.chdir(repo_path) cloned = True finally: git.fetch() git.checkout(repo['branch']) os.chdir(base_path)
class GitRepo: def __init__(self, directory, url, branch, update_freq=1, tag=None): self.dir = directory self.url = url self.branch = branch self.update_freq = update_freq # Hours self.tag = tag self.git = Git(self.dir) def run_inside(self, command): cur_path = getcwd() if command.startswith('./'): command = join(self.dir, command[2:]) try: chdir(self.dir) if isinstance(command, str): call(command.split(' ')) else: call(command) finally: chdir(cur_path) def _clone(self): if self.tag is None: Git().clone(self.url, self.dir, branch=self.branch, single_branch=True, depth=1) else: self.git.init() self.git.remote('add', 'origin', self.url) self.git.fetch('origin', self.tag, depth=1) self.git.reset('FETCH_HEAD', hard=True) def try_pull(self): if not isdir(self.dir): self._clone() return True git_folder = join(self.dir, '.git') stat = os.stat(git_folder) if time() - stat.st_mtime > self.update_freq * 60 * 60: # Touch folder os.utime(git_folder) if not self.tag: self.git.pull(ff_only=True) return True return False
def addSuperModuleCommit(self, id, hash, url, who, branch, project): self.log.debug("branch: " + branch + ", project:" + project) hasSuperModule = False isSuperModuleBr = False self.log.debug("Project names: " + str(self.config.projects)) projectNames = self.config.projects.keys() for proj in projectNames: self.log.debug("project: " + project + " proj: " + proj) if project.lower() == proj: hasSuperModule = True break for br in self.config.branches: if branch == br: isSuperModuleBr = True break self.log.debug("isSuperModuleBr: " + str(isSuperModuleBr) + " hasSuperModule: " + str(hasSuperModule)) if isSuperModuleBr and hasSuperModule: self.log.debug("Git Profile Path: " + str(self.config.profile)) git = Git(self.config.profile) self.checkoutTrackingBranch(git, branch) git.pull() git.submodule("update","--init") gitSubmoduleProfile = {'git':self.config.superRepoPath + self.config.projects[project.lower()]} gitSubmodule = Git(gitSubmoduleProfile) self.log.debug("checking out hash: " + hash) gitSubmodule.fetch() if self.isOptOut(gitSubmodule, hash): return gitSubmodule.checkout(hash, True) git.add(".") commitMsg = "Auto checkin: " + self.getCommitMessage(gitSubmodule, hash) + "\nuser:"******"\nhash:" + hash + "\nproject: " + project self.log.debug("commiting in super module: " + commitMsg) git.commit(commitMsg) self.log.debug("pushing super module to branch: " + branch) git.push(branch) else: self.log.debug("No super module commit is required.")
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 TempClone: """Create a clone in a temp dir used to write and push file changes""" def __init__(self, url: str, branch: str=None): import posixpath self.path = join(gettempdir(), posixpath.basename(url)) if not isdir(self.path): Repo.clone_from(url, self.path) self.git = Git(self.path) self.git.fetch() if branch: self.git.checkout(branch) def write(self, path: str, content: str): with open(join(self.path, path), 'w') as f: f.write(content) self.git.add(path) self.git.commit(message="Automatic update of skill-metadata.json") self.git.push() def delete(self): shutil.rmtree(self.path)
def temporary_macro(tag, macro, app, nevents): app_map = {'BACCARAT': 'Bacc'} macro_extras = Template(dedent(""" /control/getEnv SEED /$app/randomSeed {SEED} /$app/beamOn $nevents exit """)) lzprod_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) git_dir = os.path.join(lzprod_root, 'git', 'TDRAnalysis') macro = os.path.join(git_dir, macro) git = Git(git_dir) git.fetch('origin') git.checkout(tag) if not os.path.isfile(macro): raise Exception("Macro file '%s' doesn't exist in tag %s" % (macro, tag)) with NamedTemporaryFile(prefix=os.path.splitext(os.path.basename(macro))[0] + '_', suffix='.mac') as tmpfile: with open(macro, 'rb') as macro_file: tmpfile.write(macro_file.read()) tmpfile.write(macro_extras.safe_substitute(app=app_map.get(app, app), nevents=nevents)) tmpfile.flush() yield tmpfile
class GitWrapper: """ A wrapper for repo.git providing better stdout handling + better exeptions. It is preferred to repo.git because it doesn't print to stdout in real time. In addition, this wrapper provides better error handling (it provides stdout messages inside the exception, too). """ def __init__(self, repo): if repo: #: :type: git.Repo self.repo = repo #: :type: git.Git self.git = self.repo.git else: #: :type: git.Git self.git = Git() def __del__(self): # Is the following true? # GitPython runs persistent git processes in the working directory. # Therefore, when we use 'git up' in something like a test environment, # this might cause troubles because of the open file handlers (like # trying to remove the directory right after the test has finished). # 'clear_cache' kills the processes... if platform.system() == 'Windows': # pragma: no cover pass # ... or rather "should kill", because but somehow it recently # started to not kill cat_file_header out of the blue (I even # tried running old code, but the once working code failed). # Thus, we kill it manually here. if self.git.cat_file_header is not None: subprocess.call(("TASKKILL /F /T /PID {} 2>nul 1>nul".format( str(self.git.cat_file_header.proc.pid))), shell=True) if self.git.cat_file_all is not None: subprocess.call(("TASKKILL /F /T /PID {} 2>nul 1>nul".format( str(self.git.cat_file_all.proc.pid))), shell=True) self.git.clear_cache() def _run(self, name, *args, **kwargs): """ Run a git command specified by name and args/kwargs. """ stdout = b'' cmd = getattr(self.git, name) # Ask cmd(...) to return a (status, stdout, stderr) tuple kwargs['with_extended_output'] = True # Execute command try: (_, stdout, _) = cmd(*args, **kwargs) except GitCommandError as error: # Add more meta-information to errors message = "'{}' returned exit status {}".format( ' '.join(str(c) for c in error.command), error.status) raise GitError(message, stderr=error.stderr, stdout=stdout) return stdout.strip() def __getattr__(self, name): return lambda *args, **kwargs: self._run(name, *args, **kwargs) ########################################################################### # Overwrite some methods and add new ones ########################################################################### @contextmanager def stasher(self): """ A stashing contextmanager. """ # nonlocal for python2 stashed = [False] clean = [False] def stash(): if clean[0] or not self.repo.is_dirty(submodules=False): clean[0] = True return if stashed[0]: return if self.change_count > 1: message = 'stashing {0} changes' else: message = 'stashing {0} change' print(colored(message.format(self.change_count), 'magenta')) try: self._run('stash') except GitError as e: raise StashError(stderr=e.stderr, stdout=e.stdout) stashed[0] = True yield stash if stashed[0]: print(colored('unstashing', 'magenta')) try: self._run('stash', 'pop') except GitError as e: raise UnstashError(stderr=e.stderr, stdout=e.stdout) def checkout(self, branch_name): """ Checkout a branch by name. """ try: find(self.repo.branches, lambda b: b.name == branch_name).checkout() except OrigCheckoutError as e: raise CheckoutError(branch_name, details=e) def rebase(self, target_branch): """ Rebase to target branch. """ current_branch = self.repo.active_branch arguments = (([self.config('git-up.rebase.arguments')] or []) + [target_branch.name]) try: self._run('rebase', *arguments) except GitError as e: raise RebaseError(current_branch.name, target_branch.name, **e.__dict__) def fetch(self, *args, **kwargs): """ Fetch remote commits. """ # Unlike the other git commands, we want to output `git fetch`'s # output in real time. Therefore we use a different implementation # from `GitWrapper._run` which buffers all output. # In theory this may deadlock if `git fetch` prints more than 8 KB # to stderr which is here assumed to not happen in day-to-day use. stdout = b'' # Execute command cmd = self.git.fetch(as_process=True, *args, **kwargs) # Capture output while True: output = cmd.stdout.read(1) sys.stdout.write(output.decode('utf-8')) sys.stdout.flush() stdout += output # Check for EOF if output == b"": break # Wait for the process to quit try: cmd.wait() except GitCommandError as error: # Add more meta-information to errors message = "'{}' returned exit status {}".format( ' '.join(str(c) for c in error.command), error.status) raise GitError(message, stderr=error.stderr, stdout=stdout) return stdout.strip() def push(self, *args, **kwargs): ''' Push commits to remote ''' stdout = b'' # Execute command cmd = self.git.push(as_process=True, *args, **kwargs) # Capture output while True: output = cmd.stdout.read(1) sys.stdout.write(output.decode('utf-8')) sys.stdout.flush() stdout += output # Check for EOF if output == b"": break # Wait for the process to quit try: cmd.wait() except GitCommandError as error: # Add more meta-information to errors message = "'{}' returned exit status {}".format( ' '.join(str(c) for c in error.command), error.status) raise GitError(message, stderr=error.stderr, stdout=stdout) return stdout.strip() def config(self, key): """ Return `git config key` output or None. """ try: return self.git.config(key) except GitCommandError: return None @property def change_count(self): """ The number of changes in the working directory. """ status = self.git.status(porcelain=True, untracked_files='no').strip() if not status: return 0 else: return len(status.split('\n')) @property def version(self): """ Return git's version as a list of numbers. The original repo.git.version_info has problems with tome types of git version strings. """ return re.search(r'\d+(\.\d+)+', self.git.version()).group(0) def is_version_min(self, required_version): """ Does git's version match the requirements? """ return self.version.split('.') >= required_version.split('.')
class _Updater(object): def __init__(self): 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 Exception as e: print_warning('Git repo error' + str(e)) 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 str(self.repo.active_branch).split()[-1] except Exception as e: print_error( "Failed to get current branch, is there a git repo setup?" + str(e)) return "" def get_current_revision(self): """ Retrieves the current git revision of the repository. :return: current git revision. :rtype: string """ if self.git is None: return False try: # Request latest commit revision return str(self.git.log("--pretty=%h", "-1")) except Exception as e: print_error( "Failed to get current revision, is there a git repo setup?" + str(e)) 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 Exception as e: print_warning( "Failed to get remote branches, is there a git repo setup and do you have internet?" + str(e)) pass return branches def is_update_available(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 Exception as e: print_error('Failed to fetch: ' + str(e)) 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): """ 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)
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 GitWrapper(object): """ A wrapper for repo.git providing better stdout handling + better exeptions. It is preferred to repo.git because it doesn't print to stdout in real time. In addition, this wrapper provides better error handling (it provides stdout messages inside the exception, too). """ def __init__(self, repo): if repo: #: :type: git.Repo self.repo = repo #: :type: git.Git self.git = self.repo.git else: #: :type: git.Git self.git = Git() def __del__(self): # Is the following true? # GitPython runs persistent git processes in the working directory. # Therefore, when we use 'git up' in something like a test environment, # this might cause troubles because of the open file handlers (like # trying to remove the directory right after the test has finished). # 'clear_cache' kills the processes... if platform.system() == 'Windows': # pragma: no cover pass # ... or rather "should kill", because but somehow it recently # started to not kill cat_file_header out of the blue (I even # tried running old code, but the once working code failed). # Thus, we kill it manually here. if self.git.cat_file_header is not None: subprocess.call(("TASKKILL /F /T /PID {0} 2>nul 1>nul".format( str(self.git.cat_file_header.proc.pid) )), shell=True) if self.git.cat_file_all is not None: subprocess.call(("TASKKILL /F /T /PID {0} 2>nul 1>nul".format( str(self.git.cat_file_all.proc.pid) )), shell=True) self.git.clear_cache() def _run(self, name, *args, **kwargs): """ Run a git command specified by name and args/kwargs. """ stdout = six.b('') cmd = getattr(self.git, name) # Ask cmd(...) to return a (status, stdout, stderr) tuple kwargs['with_extended_output'] = True # Execute command try: (_, stdout, _) = cmd(*args, **kwargs) except GitCommandError as error: # Add more meta-information to errors message = "'{0}' returned exit status {1}".format( ' '.join(str(c) for c in error.command), error.status ) raise GitError(message, stderr=error.stderr, stdout=stdout) return stdout.strip() def __getattr__(self, name): return lambda *args, **kwargs: self._run(name, *args, **kwargs) ########################################################################### # Overwrite some methods and add new ones ########################################################################### @contextmanager def stasher(self): """ A stashing contextmanager. """ # nonlocal for python2 stashed = [False] clean = [False] def stash(): if clean[0] or not self.repo.is_dirty(submodules=False): clean[0] = True return if stashed[0]: return if self.change_count > 1: message = 'stashing {0} changes' else: message = 'stashing {0} change' print(colored( message.format(self.change_count), 'magenta' )) try: self._run('stash') except GitError as e: raise StashError(stderr=e.stderr, stdout=e.stdout) stashed[0] = True yield stash if stashed[0]: print(colored('unstashing', 'magenta')) try: self._run('stash', 'pop') except GitError as e: raise UnstashError(stderr=e.stderr, stdout=e.stdout) def checkout(self, branch_name): """ Checkout a branch by name. """ try: find( self.repo.branches, lambda b: b.name == branch_name ).checkout() except OrigCheckoutError as e: raise CheckoutError(branch_name, details=e) def rebase(self, target_branch): """ Rebase to target branch. """ current_branch = self.repo.active_branch arguments = ( ([self.config('git-up.rebase.arguments')] or []) + [target_branch.name] ) try: self._run('rebase', *arguments) except GitError as e: raise RebaseError(current_branch.name, target_branch.name, **e.__dict__) def fetch(self, *args, **kwargs): """ Fetch remote commits. """ # Unlike the other git commands, we want to output `git fetch`'s # output in real time. Therefore we use a different implementation # from `GitWrapper._run` which buffers all output. # In theory this may deadlock if `git fetch` prints more than 8 KB # to stderr which is here assumed to not happen in day-to-day use. stdout = six.b('') # Execute command cmd = self.git.fetch(as_process=True, *args, **kwargs) # Capture output while True: output = cmd.stdout.read(1) sys.stdout.write(output.decode('utf-8')) sys.stdout.flush() stdout += output # Check for EOF if output == six.b(""): break # Wait for the process to quit try: cmd.wait() except GitCommandError as error: # Add more meta-information to errors message = "'{0}' returned exit status {1}".format( ' '.join(str(c) for c in error.command), error.status ) raise GitError(message, stderr=error.stderr, stdout=stdout) return stdout.strip() def push(self, *args, **kwargs): ''' Push commits to remote ''' stdout = six.b('') # Execute command cmd = self.git.push(as_process=True, *args, **kwargs) # Capture output while True: output = cmd.stdout.read(1) sys.stdout.write(output.decode('utf-8')) sys.stdout.flush() stdout += output # Check for EOF if output == six.b(""): break # Wait for the process to quit try: cmd.wait() except GitCommandError as error: # Add more meta-information to errors message = "'{0}' returned exit status {1}".format( ' '.join(str(c) for c in error.command), error.status ) raise GitError(message, stderr=error.stderr, stdout=stdout) return stdout.strip() def config(self, key): """ Return `git config key` output or None. """ try: return self.git.config(key) except GitCommandError: return None @property def change_count(self): """ The number of changes in the working directory. """ status = self.git.status(porcelain=True, untracked_files='no').strip() if not status: return 0 else: return len(status.split('\n')) @property def version(self): """ Return git's version as a list of numbers. The original repo.git.version_info has problems with tome types of git version strings. """ return re.search(r'\d+(\.\d+)+', self.git.version()).group(0) def is_version_min(self, required_version): """ Does git's version match the requirements? """ return self.version.split('.') >= required_version.split('.')