class LogFetcher: def __init__(self, repo_path, user=None): self.repo_path = repo_path self.git = Git(repo_path) self.name = self.get_repo_name() self.user = user if user else self.get_default_user() self.headers = ['repo', 'sha', 'author', 'date', 'subject', 'body'] def get_repo_name(self): repo = Repo(self.repo_path) url = urlparse(repo.remote().url) basename = url.path.split('/')[-1] return basename.replace('.git', '') def get_default_user(self): name = self.git.config('--get', 'user.name') email = self.git.config('--get', 'user.email') return '{} <{}>'.format(name, email) def get_entries(self): entries = [] output = self.git.log( '--author={}'.format(self.user), '--pretty=format:%H|%an <%ae>|%ai|%s|%b~~~', '--no-merges' ) for line in output.split('~~~')[:-1]: entry = [self.name] data = line.split('|') for item in data: entry.append(item.strip()) entries.append(entry) return entries
def get_credentials(cls, repo=None): """ Get credentials from the github.user and github.token config values """ if repo: _git = repo.git else: _git = Git(os.getcwd()) return cls(user=_git.config('github.user', with_exceptions=False), token=_git.config('github.token', with_exceptions=False))
def get_credentials(cls, repo=None): """ Get credentials from the github.user and github.token config values """ if repo: _git = repo.git else: _git = Git(os.getcwd()) return cls( user=_git.config("github.user", with_exceptions=False), token=_git.config("github.token", with_exceptions=False), )
def get_credentials(cls, repo=None): """ Get credentials from the github.user and github.token config values """ if repo: _git = repo.git else: _git = Git(os.getcwd()) return cls( user=_git.config('github.user'), token=_git.config('github.token') )
class MyGit(): def __init__(self, path): self.g = Git(path) def update_force(self, config=None): """ 强制更新 buckets """ if 'http.proxy' in config: info_1 = self.g.config("--global", "http.proxy", config['git']['http.proxy']) self.g.checkout('*') pull_info = self.g.pull("--no-rebase") self.g.config("--global", "--unset", "http.proxy") else: self.g.checkout('*') pull_info = self.g.pull("--no-rebase") return pull_info
def push_to_github(local_dir, repo_path, commit_message='No commit message was set.'): repo_list = repo_path.split('/') try: n = repo_list.index('tree') except ValueError: n = len(repo_list) repo_name = os.path.join(*repo_list[:n]) account = my_account repo_url = '/'.join(['https://' + os.environ.get('GITHUB_USERNAME', None) + ':' + os.environ.get('GITHUB_PASSWORD', None) + '@github.com',account,repo_name]) branch = repo_list[-1] if 'tree' in repo_list else None print(repo_url) os.system('cd') git_dir = os.path.join('dest_repo', repo_name) try: shutil.rmtree(git_dir) shutil.rmtree('dest_repo') except: pass os.system('md dest_repo') kwargs = { 'url': repo_url, 'to_path': git_dir } if branch: kwargs['branch'] = branch repo = Repo.clone_from(**kwargs) os.chdir(git_dir) os.system(' '.join(['cp', '-rf', os.path.join('..', '..', local_dir, '.'), '.'])) origin = repo.remotes[0] gitcmd = Git('.') gitcmd.config('--add', 'user.name', os.environ.get('GITHUB_USERNAME', None)) gitcmd.config('--add', 'user.email', os.environ.get('GITHUB_EMAIL', None)) repo.git.add(A=True) repo.git.add(u=True) repo.index.commit(commit_message) repo.git.push('--set-upstream', 'origin', branch) stats = repo.head.commit.stats.total os.chdir('../..') shutil.rmtree('dest_repo') return stats
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. """ tostdout = kwargs.pop('tostdout', False) stdout = six.b('') # Execute command cmd = getattr(self.git, name)(as_process=True, *args, **kwargs) # Capture output while True: output = cmd.stdout.read(1) # Print to stdout if tostdout: sys.stdout.write(output.decode('utf-8')) sys.stdout.flush() stdout += output 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 __getattr__(self, name): return lambda *args, **kwargs: self.run(name, *args, **kwargs) ########################################################################### # Overwrite some methods and add new ones ########################################################################### @contextmanager def stash(self): """ A stashing contextmanager. It stashes all changes inside and unstashed when done. """ stashed = False if self.repo.is_dirty(submodules=False): if self.change_count > 1: message = 'stashing {0} changes' else: message = 'stashing {0} change' print(colored( message.format(self.change_count), 'magenta' )) self.git.stash() stashed = True yield if stashed: 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 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 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('.')