Files
hamann-ausgabe-core/HaWeb/FileHelpers/GitService.cs
Simon Martens bd563f6dae +Docker
2025-09-30 17:41:04 +02:00

172 lines
6.9 KiB
C#

namespace HaWeb.FileHelpers;
using LibGit2Sharp;
using HaWeb.Models;
public class GitService : IGitService {
private readonly string _repositoryPath;
private readonly string _remoteName;
private readonly string _branch;
private readonly string _url;
private readonly ILogger<GitService>? _logger;
public GitService(IConfiguration config, ILogger<GitService>? logger = null) {
_logger = logger;
_remoteName = "origin";
_branch = config.GetValue<string>("RepositoryBranch") ?? "main";
_url = config.GetValue<string>("RepositoryURL") ?? string.Empty;
var fileStoragePath = config.GetValue<string>("FileStoragePath") ?? throw new ArgumentException("FileStoragePath not configured");
_repositoryPath = Path.Combine(fileStoragePath, "GIT");
// Ensure repository exists
if (!Repository.IsValid(_repositoryPath)) {
_logger?.LogWarning("Repository not found at {Path}, attempting to initialize/clone", _repositoryPath);
InitializeRepository();
}
}
public GitState? GetGitState() {
try {
using var repo = new Repository(_repositoryPath);
var headCommit = repo.Head.Tip;
if (headCommit == null) {
_logger?.LogWarning("No commits found in repository");
return null;
}
return new GitState {
Commit = headCommit.Sha,
Branch = repo.Head.FriendlyName,
URL = _url,
PullTime = headCommit.Author.When.ToLocalTime().DateTime
};
}
catch (Exception ex) {
_logger?.LogError(ex, "Failed to get Git state");
return null;
}
}
public bool Pull() {
try {
using var repo = new Repository(_repositoryPath);
var oldCommitSha = repo.Head.Tip?.Sha;
// Configure pull options
var options = new PullOptions {
FetchOptions = new FetchOptions {
CredentialsProvider = (_url, _user, _cred) => GetCredentials()
}
};
// Create signature for merge commit (if needed)
var signature = new Signature(
new Identity("HaWeb Service", "hawebservice@localhost"),
DateTimeOffset.Now
);
// Perform pull
var result = Commands.Pull(repo, signature, options);
var newCommitSha = repo.Head.Tip?.Sha;
var hasChanges = oldCommitSha != newCommitSha;
if (hasChanges) {
_logger?.LogInformation("Pull successful: {OldCommit} -> {NewCommit}",
oldCommitSha?[..7], newCommitSha?[..7]);
} else {
_logger?.LogInformation("Pull completed but no new changes");
}
return hasChanges;
}
catch (Exception ex) {
_logger?.LogError(ex, "Failed to pull from remote repository");
return false;
}
}
public bool HasChanged(string? previousCommitSha) {
if (string.IsNullOrEmpty(previousCommitSha)) {
return true;
}
var currentState = GetGitState();
return currentState != null && currentState.Commit != previousCommitSha;
}
private void InitializeRepository() {
try {
if (!Directory.Exists(_repositoryPath)) {
Directory.CreateDirectory(_repositoryPath);
}
// If there's a .git directory but it's invalid, try to reinitialize
var gitDir = Path.Combine(_repositoryPath, ".git");
if (Directory.Exists(gitDir)) {
_logger?.LogWarning("Invalid .git directory found, attempting to use existing repository");
return;
}
// If URL is provided, clone; otherwise just initialize
if (!string.IsNullOrEmpty(_url)) {
_logger?.LogInformation("Cloning repository from {Url} to {Path}", _url, _repositoryPath);
var cloneOptions = new CloneOptions();
cloneOptions.FetchOptions.CredentialsProvider = (_url, _user, _cred) => GetCredentials();
// Clone with default branch, then checkout the specified branch if different
Repository.Clone(_url, _repositoryPath, cloneOptions);
_logger?.LogInformation("Repository cloned successfully");
// Checkout the specified branch if it's not the default
using var repo = new Repository(_repositoryPath);
// Log diagnostic information
_logger?.LogInformation("HEAD: {Head}, IsDetached: {IsDetached}, Tip: {Tip}",
repo.Head?.FriendlyName ?? "null",
repo.Head?.IsRemote.ToString() ?? "null",
repo.Head?.Tip?.Sha.Substring(0, 7) ?? "null");
_logger?.LogInformation("Available branches: {Branches}",
string.Join(", ", repo.Branches.Select(b => $"{b.FriendlyName} (Remote: {b.IsRemote})")));
var branch = repo.Branches[_branch] ?? repo.Branches[$"origin/{_branch}"];
if (branch == null) {
_logger?.LogWarning("Branch {Branch} not found. Attempting to create local tracking branch from origin/{Branch}", _branch, _branch);
var remoteBranch = repo.Branches[$"origin/{_branch}"];
if (remoteBranch != null) {
branch = repo.CreateBranch(_branch, remoteBranch.Tip);
repo.Branches.Update(branch, b => b.TrackedBranch = remoteBranch.CanonicalName);
_logger?.LogInformation("Created local tracking branch {Branch}", _branch);
}
}
if (branch != null && branch.FriendlyName != repo.Head.FriendlyName) {
Commands.Checkout(repo, branch);
_logger?.LogInformation("Checked out branch {Branch}", _branch);
} else if (branch != null) {
_logger?.LogInformation("Already on branch {Branch}", _branch);
} else {
_logger?.LogError("Could not find or create branch {Branch}", _branch);
}
}
else {
_logger?.LogInformation("Initializing empty repository at {Path}", _repositoryPath);
Repository.Init(_repositoryPath);
}
}
catch (Exception ex) {
_logger?.LogError(ex, "Failed to initialize repository");
throw;
}
}
private Credentials? GetCredentials() {
// For now, use default credentials (SSH agent, credential manager, etc.)
// Can be extended to support username/password or personal access tokens
// from configuration if needed
return new DefaultCredentials();
}
}