feat(git): fetch/push/ff primitives with auth env, no task trailer in company repos

This commit is contained in:
mika kuns
2026-10-01 11:51:28 +02:00
parent bc0aa28afb
commit 76f260a9a5
4 changed files with 63 additions and 6 deletions
+43 -1
View File
@@ -450,6 +450,45 @@ public sealed class GitService
return (exitCode, stderr);
}
/// <summary>
/// <c>git fetch origin</c> for one branch into <c>refs/remotes/origin/&lt;branch&gt;</c>. The explicit
/// refspec works without a configured fetch refspec. <paramref name="env"/> carries the auth
/// header (never the command line) and must disable the terminal prompt.
/// </summary>
public async Task<(int ExitCode, string Stderr)> FetchBranchAsync(
string repoDir, string branch, IReadOnlyDictionary<string, string>? env, CancellationToken ct = default)
{
var (exitCode, _, stderr) = await RunGitAsync(repoDir,
["fetch", "origin", $"+refs/heads/{branch}:refs/remotes/origin/{branch}"], ct, env: env);
return (exitCode, stderr);
}
/// <summary>Moves the local, not checked-out <paramref name="branch"/> to <c>origin/&lt;branch&gt;</c>; git itself refuses a non-fast-forward. No network.</summary>
public async Task<(int ExitCode, string Stderr)> FastForwardLocalBranchAsync(
string repoDir, string branch, CancellationToken ct = default)
{
var (exitCode, _, stderr) = await RunGitAsync(repoDir,
["fetch", ".", $"refs/remotes/origin/{branch}:refs/heads/{branch}"], ct);
return (exitCode, stderr);
}
/// <summary><c>git push -u origin &lt;branch&gt;</c> from <paramref name="worktreePath"/>; auth via <paramref name="env"/>.</summary>
public async Task<(int ExitCode, string Stderr)> PushBranchAsync(
string worktreePath, string branch, IReadOnlyDictionary<string, string>? env, CancellationToken ct = default)
{
var (exitCode, _, stderr) = await RunGitAsync(worktreePath, ["push", "-u", "origin", branch], ct, env: env);
return (exitCode, stderr);
}
/// <summary>Branch that <c>refs/remotes/origin/HEAD</c> points to (without <c>origin/</c>), or null.</summary>
public async Task<string?> GetRemoteHeadBranchAsync(string repoDir, CancellationToken ct = default)
{
var (exitCode, stdout, _) = await RunGitAsync(repoDir, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], ct);
if (exitCode != 0 || string.IsNullOrWhiteSpace(stdout)) return null;
var head = stdout.Trim();
return head.StartsWith("origin/", StringComparison.Ordinal) ? head["origin/".Length..] : head;
}
/// <summary><c>git merge --ff-only</c>: moves the checked-out branch to <paramref name="sourceRef"/> or fails without touching it.</summary>
public async Task<(int ExitCode, string Stderr)> MergeFastForwardOnlyAsync(
string worktreePath, string sourceRef, CancellationToken ct = default)
@@ -721,7 +760,8 @@ public sealed class GitService
private static async Task<(int ExitCode, string Stdout, string Stderr)> RunGitAsync(
string workDir, IEnumerable<string> args, CancellationToken ct, string? stdinData = null, bool trimOutput = true)
string workDir, IEnumerable<string> args, CancellationToken ct, string? stdinData = null, bool trimOutput = true,
IReadOnlyDictionary<string, string>? env = null)
{
var psi = new ProcessStartInfo
{
@@ -742,6 +782,8 @@ public sealed class GitService
// of the OS locale git for Windows picks up.
psi.Environment["LC_ALL"] = "C";
psi.Environment["LANGUAGE"] = "C";
if (env is not null)
foreach (var (key, value) in env) psi.Environment[key] = value;
psi.ArgumentList.Add("-C");
psi.ArgumentList.Add(workDir);
foreach (var a in args) psi.ArgumentList.Add(a);