From 5da7861c2a3b06d92950b4e7d12878c1fa32035f Mon Sep 17 00:00:00 2001 From: Adam Majer Date: Mon, 8 Apr 2024 14:55:37 +0200 Subject: [PATCH] Switch to sha-256 git repo and use git tools again --- lib/git.py | 192 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 126 insertions(+), 66 deletions(-) diff --git a/lib/git.py b/lib/git.py index bd2fd11..411d734 100644 --- a/lib/git.py +++ b/lib/git.py @@ -4,7 +4,6 @@ import os import pathlib import subprocess -import pygit2 import requests from lib.binary import BINARY @@ -20,11 +19,6 @@ class Git: self.committer = committer self.committer_email = committer_email - self.repo = None - - def is_open(self): - return self.repo is not None - def exists(self): """Check if the path is a valid git repository""" return (self.path / ".git").exists() @@ -35,35 +29,60 @@ class Git: self.open() def open(self): - # Convert the path to string, to avoid some limitations in - # older pygit2 - self.repo = pygit2.init_repository(str(self.path)) + subprocess.run( + ['git', 'init', '--object-format=sha256', '-b', 'factory'], + cwd=self.path, + check=True, + ) def is_dirty(self): """Check if there is something to commit""" - assert self.is_open() - - return self.repo.status() + status_str = subprocess.run( + ['git', 'status', '--porcelain=2'], + cwd=self.path, + stdout=subprocess.PIPE, + check=True + ).stdout.decode('utf-8') + return len(list(filter(None, status_str.split('\n')))) > 0 def branches(self): - return list(self.repo.branches) + br=subprocess.run( + ['git', 'for-each-ref', '--format=%(refname:short)', 'refs/heads/'], + cwd=self.path, + check=True, + stdout=subprocess.PIPE + ).stdout.decode('utf-8').split() + if len(br) == 0: + br.append('factory') # unborn branch? + return br - def branch(self, branch, commit=None): - if not commit: - commit = self.repo.head - else: - commit = self.repo.get(commit) - self.repo.branches.local.create(branch, commit) + def branch(self, branch, commit='HEAD'): + commit = subprocess.run( + ['git', 'rev-parse', '--verify', '--end-of-options', commit + '^{commit}'], + cwd=self.path, + check=True, + stdout=subprocess.PIPE + ).stdout.decode('utf-8').strip() + return subprocess.run(['git', 'branch', branch, commit], check=True) def checkout(self, branch): """Checkout into the branch HEAD""" new_branch = False - ref = f"refs/heads/{branch}" if branch not in self.branches(): - self.repo.references["HEAD"].set_target(ref) + subprocess.run( + ['git', 'branch', '-q', branch, 'HEAD'], + cwd=self.path, + check=True + ) new_branch = True else: - self.repo.checkout(ref) + ref = f"refs/heads/{branch}" + if (self.path/'.git'/ref).exists(): + subprocess.run( + ['git', 'checkout', '-q', branch], + cwd=self.path, + check=True + ) return new_branch def commit( @@ -87,30 +106,62 @@ class Git: committer_time = committer_time if committer_time else user_time if self.is_dirty(): - self.repo.index.add_all() + subprocess.run( + ["git", "add", "--all", "."], + cwd=self.path, + check=True, + ) - self.repo.index.write() - author = pygit2.Signature(user, user_email, int(user_time.timestamp())) - committer = pygit2.Signature( - committer, committer_email, int(committer_time.timestamp()) + tree_id = subprocess.run( + ['git', 'write-tree'], + cwd=self.path, + check=True, + stdout=subprocess.PIPE + ).stdout.decode('utf-8').strip() + + parent_array = [] + if isinstance(parents, list): + for parent in filter(None, parents): + parent_array = parent_array + ['-p', parent] + elif isinstance(parents, str): + parents_array = ['-p', parents] + + commit_id = subprocess.run( + ['git', 'commit-tree'] + parent_array + [tree_id], + cwd=self.path, + env={ + "GIT_AUTHOR_NAME": user, + "GIT_AUTHOR_EMAIL": user_email, + "GIT_AUTHOR_DATE": f"{int(user_time.timestamp())} +0000", + "GIT_COMMITTER_NAME": committer, + "GIT_COMMITTER_EMAIL": committer_email, + "GIT_COMMITTER_DATE": f"{int(committer_time.timestamp())} +0000", + }, + input=message.encode('utf-8'), + check=True, + stdout=subprocess.PIPE + ).stdout.decode('utf-8').rstrip() + subprocess.run( + ['git', 'reset', '--soft', commit_id], + cwd=self.path, + check=True, ) + return commit_id - tree = self.repo.index.write_tree() - return self.repo.create_commit( - "HEAD", author, committer, message, tree, parents - ) - - def last_commit(self): - try: - return self.repo.head.target - except: - return None - - def branch_head(self, branch): - return self.repo.references["refs/heads/" + branch].target + def branch_head(self, branch='HEAD'): + return subprocess.run( + ['git', 'rev-parse', '--verify', '--end-of-options', branch], + cwd=self.path, + check=True, + stdout=subprocess.PIPE + ).stdout.decode('utf-8').strip() def set_branch_head(self, branch, commit): - self.repo.references["refs/heads/" + branch].set_target(commit) + return subprocess.run( + ['git', 'branch', '-f', branch, commit], + cwd=self.path, + check=True, + ) def gc(self): logging.debug(f"Garbage recollect and repackage {self.path}") @@ -121,17 +172,21 @@ class Git: stderr=subprocess.STDOUT, ) - def clean(self): - for path, _ in self.repo.status().items(): - logging.debug(f"Cleaning {path}") - try: - (self.path / path).unlink() - self.repo.index.remove(path) - except Exception as e: - logging.warning(f"Error removing file {path}: {e}") +# def clean(self): +# for path, _ in self.repo.status().items(): +# logging.debug(f"Cleaning {path}") +# try: +# (self.path / path).unlink() +# self.repo.index.remove(path) +# except Exception as e: +# logging.warning(f"Error removing file {path}: {e}") def add(self, filename): - self.repo.index.add(filename) + subprocess.run( + ['git', 'add', filename], + cwd=self.path, + check=True, + ) def add_default_lfs_gitattributes(self, force=False): if not (self.path / ".gitattributes").exists() or force: @@ -185,9 +240,11 @@ class Git: return any(fnmatch.fnmatch(filename, line) for line in patterns) def remove(self, file: pathlib.Path): - self.repo.index.remove(file.name) - (self.path / file).unlink() - + subprocess.run( + ['git', 'rm', '-q', '--ignore-unmatch', file.name], + cwd=self.path, + check=True, + ) patterns = self.get_specific_lfs_gitattributes() if file.name in patterns: patterns.remove(file.name) @@ -213,19 +270,22 @@ class Git: if response.status_code not in (201, 409): print(response.data) url = f"gitea@src.opensuse.org:{org_name}/{repo_name}.git" - self.repo.remotes.create("origin", url) + subprocess.run( + ['git', 'remote', 'add', 'origin', url], + cwd=self.path, + check=True, + ) def push(self, force=False): - remo = self.repo.remotes["origin"] + cmd = ['git', 'push']; + if force: + cmd.append('-f') + cmd.append('origin') + cmd.append('refs/heads/factory'); + cmd.append('refs/heads/devel'); + subprocess.run( + cmd, + cwd=self.path, + check=True, + ) - keypair = pygit2.KeypairFromAgent("gitea") - callbacks = pygit2.RemoteCallbacks(credentials=keypair) - - refspecs = ["refs/heads/factory"] - develspec = "refs/heads/devel" - if develspec in self.repo.references: - if force: - refspecs.append(f"+{develspec}:{develspec}") - else: - refspecs.append("{develspec}:{develspec}") - remo.push(refspecs, callbacks=callbacks)