def create_tree(commits, head): try: repo = Repo('tree') except NoSuchPathError: repo = Repo.init('tree') # TODO Only use tree in dev-mode repo.init() index = repo.index delete_files() index.commit("Clearing index") # Easiest way to clear the index is to commit an empty directory # Switch to temp first in case git-gud-construction exists if repo.head.reference.name != 'temp': repo.head.reference = Head(repo, 'refs/heads/temp') # Delete git-gud-construction so we can guarantee it's an orphan try: repo.delete_head('git-gud-construction') except GitCommandError: pass # Branch doesn't exist repo.head.reference = Head(repo, 'refs/heads/git-gud-construction') try: repo.delete_head('temp') except GitCommandError: pass # If temp didn't exist, we only checked it out as an orphan, so it already disappeared for branch in repo.branches: if branch.name != 'git-gud-construction': repo.delete_head(branch, force=True) repo.delete_tag(*repo.tags) index = repo.index commit_objects = {} for name, parents, branches, tags in commits: # commit = (name, parents, branches, tags) parents = [commit_objects[parent] for parent in parents] if parents: repo.active_branch.set_commit(parents[0]) if len(parents) < 2: # Not a merge add_file_to_index(index, name) commit = index.commit(name, author=actor, committer=actor, parent_commits=parents) commit_objects[name] = commit for branch in branches: repo.create_head(branch, commit) for tag in tags: repo.create_tag(tag, commit) # TODO Log commit hash and info # TODO Checkout using name for branch in repo.branches: if branch.name == head: branch.checkout() repo.delete_head('git-gud-construction')
def get_repo(repo_name): directory = os.path.join(test_dir, repo_name) repo = Repo.init(directory) # Ensure the default branch is using a fixed name. # User config could change that, # breaking tests with implicit assumptions further down the line. repo.head.reference = Head(repo, 'refs/heads/master') # We need to synthesize the time stamps of commits to each be a second # apart, otherwise the commits may be at exactly the same second, which # means they won't always sort in order, and thus the merging of identical # metrics in adjacent commits may not happen correctly. base_time = time.time() base_path = os.path.join(base_dir, repo_name) for i in range(num_commits): files_dir = os.path.join(base_path, str(i)) if not os.path.exists(files_dir): break files = os.listdir(files_dir) for filename in files: print("Copying file " + filename) path = os.path.join(base_path, str(i), filename) destination = os.path.join(directory, filename) shutil.copyfile(path, destination) repo.index.add("*") commit_date = datetime.datetime.fromtimestamp(base_time + i).isoformat() commit_date = commit_date[:commit_date.find('.')] repo.index.commit("Commit {index}".format(index=i), commit_date=commit_date) return directory
def checkout_branch(repo, branch_name, no_pull=False): """Checkout a branch and optionally pull updates. :param repo: Repo object :param branch_name: Name of the branch to checkout :param no_pull: (Default: False) If True, don't pull changes to branch :return: Head object for the checked out branch """ base_head = Head(repo, f'refs/heads/{branch_name}') if repo.active_branch != base_head: print(f'Checking out {branch_name}.') base_head.checkout() if not no_pull and base_head.tracking_branch(): print(f'Pulling updates to {branch_name}...') remote_name = base_head.tracking_branch().remote_name remote = Remote(repo, remote_name) base_commit = base_head.commit for fetch_info in remote.pull(): if fetch_info.ref == base_head.tracking_branch(): if fetch_info.commit != base_commit: print( f'Updated {branch_name} to {fetch_info.commit.hexsha}') else: print(f'{branch_name} already up to date.') print('') return base_head
def __init__(self, git_object=None, change_ids=list(), upstream_branch=None, force=False, *args, **kwargs): # make sure to correctly initialize inherited objects before performing # any computation super(Supersede, self).__init__(*args, **kwargs) # test commit parameter if not git_object: raise SupersedeError("Commit should be provided") # test that we can use this git repo if self.is_detached(): raise SupersedeError("In 'detached HEAD' state") # To Do: check if it possible and useful. if self.repo.bare: raise SupersedeError("Cannot add notes in bare repositories") if not upstream_branch: raise SupersedeError("Missing upstream_branch parameter") try: # test commit "id" presence self._commit = self.repo.commit(git_object) except (BadName, BadObject): raise SupersedeError("Commit '%s' not found (or ambiguous)" % git_object) # test change_ids parameter if len(change_ids) == 0: raise SupersedeError("At least one change id should be provided") self._upstream_branch = upstream_branch self._change_ids = change_ids git_branch = Head(self.repo, 'refs/heads/%s' % upstream_branch) for change_id in change_ids: # Check change id format if not re.match(Supersede.CHANGE_ID_REGEX, change_id, re.IGNORECASE): raise SupersedeError("Invalid Change Id '%s'" % change_id) # Check if change id is actually present in some commit # reachable from <upstream_branch> try: change_commit = CommitMessageSearcher( repo=self.repo, branch=git_branch, pattern=Supersede.CHANGE_ID_HEADER_REGEX_FMT % change_id).find() self.log.debug("Change-id '%s' found in commit '%s'" % (change_id, change_commit)) except RuntimeError: if force: self.log.warn("Warning: change-id '%s' not found in '%s'" % (change_id, upstream_branch)) else: raise SupersedeError( "Change-Id '%s' not found in branch '%s'" % (change_id, upstream_branch))