123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- #!/usr/bin/env python
- """Git helper functions.
- Everything here should be project agnostic: it shouldn't rely on project's
- structure, or make assumptions about the passed arguments or calls' outcomes.
- """
- import os
- import subprocess
- def is_repo_root(path):
- path_exists = os.path.exists(path)
- if not path_exists:
- return False
- git_folder_path = os.path.join(path, '.git')
- git_folder_exists = os.path.exists(git_folder_path)
- return git_folder_exists
- def get_repo_root(path):
- """Finds a closest ancestor folder which is a repo root."""
- norm_path = os.path.normpath(path)
- norm_path_exists = os.path.exists(norm_path)
- if not norm_path_exists:
- return None
- if is_repo_root(norm_path):
- return norm_path
- parent_path = os.path.dirname(norm_path)
- # Check if we're in the root folder already.
- if parent_path == norm_path:
- return None
- return get_repo_root(parent_path)
- def am(repo, patch_data, threeway=False, directory=None, exclude=None,
- committer_name=None, committer_email=None):
- args = []
- if threeway:
- args += ['--3way']
- if directory is not None:
- args += ['--directory', directory]
- if exclude is not None:
- for path_pattern in exclude:
- args += ['--exclude', path_pattern]
- root_args = ['-C', repo]
- if committer_name is not None:
- root_args += ['-c', 'user.name=' + committer_name]
- if committer_email is not None:
- root_args += ['-c', 'user.email=' + committer_email]
- root_args += ['-c', 'commit.gpgsign=false']
- command = ['git'] + root_args + ['am'] + args
- proc = subprocess.Popen(
- command,
- stdin=subprocess.PIPE)
- proc.communicate(patch_data)
- if proc.returncode != 0:
- raise RuntimeError("Command {} returned {}".format(command,
- proc.returncode))
- def apply_patch(repo, patch_path, directory=None, index=False, reverse=False):
- args = ['git', '-C', repo, 'apply',
- '--ignore-space-change',
- '--ignore-whitespace',
- '--whitespace', 'fix'
- ]
- if directory:
- args += ['--directory', directory]
- if index:
- args += ['--index']
- if reverse:
- args += ['--reverse']
- args += ['--', patch_path]
- return_code = subprocess.call(args)
- applied_successfully = (return_code == 0)
- return applied_successfully
- def get_patch(repo, commit_hash):
- args = ['git', '-C', repo, 'diff-tree',
- '-p',
- commit_hash,
- '--' # Explicitly tell Git `commit_hash` is a revision, not a path.
- ]
- return subprocess.check_output(args)
- def get_head_commit(repo):
- args = ['git', '-C', repo, 'rev-parse', 'HEAD']
- return subprocess.check_output(args).strip()
- def update_ref(repo, ref, newvalue):
- args = ['git', '-C', repo, 'update-ref', ref, newvalue]
- return subprocess.check_call(args)
- def reset(repo):
- args = ['git', '-C', repo, 'reset']
- subprocess.check_call(args)
- def commit(repo, author, message):
- """ Commit whatever in the index is now."""
- # Let's setup committer info so git won't complain about it being missing.
- # TODO: Is there a better way to set committer's name and email?
- env = os.environ.copy()
- env['GIT_COMMITTER_NAME'] = 'Anonymous Committer'
- env['GIT_COMMITTER_EMAIL'] = '[email protected]'
- args = ['git', '-C', repo, 'commit',
- '--author', author,
- '--message', message
- ]
- return_code = subprocess.call(args, env=env)
- committed_successfully = (return_code == 0)
- return committed_successfully
|