Skip to content

Add a Clone tab that keeps local working copies of monitored repos - #1

Merged
aritchie merged 1 commit into
mainfrom
claude/clone-sync-repos-rv6e8q
Aug 10, 2026
Merged

Add a Clone tab that keeps local working copies of monitored repos#1
aritchie merged 1 commit into
mainfrom
claude/clone-sync-repos-rv6e8q

Conversation

@aritchie

Copy link
Copy Markdown
Owner

Pick a folder and every monitored repo gets a real git working copy in it.
A repo that isn't there yet is cloned; one that already is gets fetched and
fast-forwarded on whatever branch it happens to be checked out on.

The update path is deliberately conservative — it only ever moves a branch
pointer forward, and reports (rather than touches) anything else:

  • uncommitted changes to tracked files, so work in progress is never at risk
    (untracked files don't block: a fast-forward can't lose them, and treating
    build output as uncommitted work would wedge most real checkouts)
  • a detached HEAD, a purely local branch, or a branch that has diverged from
    its upstream, which is a merge/rebase decision only the user can make
  • a folder that isn't a working copy, has no remote, or whose origin points at
    some other repo entirely

It follows the branch's actual tracking remote rather than assuming origin, so
a fork whose main tracks a second remote is compared against the right ref.
Updating spends no API calls at all — the remote comes from the working copy —
and only a first clone needs the provider, for the clone URL.

Credentials go through GitCli the same way the sync engine does (env var plus
--config-env, never argv or .git/config), and are attached only when the remote
is on the account's own host, since http.extraHeader applies to every request
the invocation makes.

The chosen folder and layout (/owner/repo or a flat /repo) persist
in the document store, so they survive a restart on the desktop heads and ride
along in a database backup.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01M8jbrkMHb7wrUS5f2LH7UN

Pick a folder and every monitored repo gets a real git working copy in it.
A repo that isn't there yet is cloned; one that already is gets fetched and
fast-forwarded on whatever branch it happens to be checked out on.
The update path is deliberately conservative — it only ever moves a branch
pointer forward, and reports (rather than touches) anything else:
- uncommitted changes to tracked files, so work in progress is never at risk
(untracked files don't block: a fast-forward can't lose them, and treating
build output as uncommitted work would wedge most real checkouts)
- a detached HEAD, a purely local branch, or a branch that has diverged from
its upstream, which is a merge/rebase decision only the user can make
- a folder that isn't a working copy, has no remote, or whose origin points at
some other repo entirely
It follows the branch's actual tracking remote rather than assuming origin, so
a fork whose main tracks a second remote is compared against the right ref.
Updating spends no API calls at all — the remote comes from the working copy —
and only a first clone needs the provider, for the clone URL.
Credentials go through GitCli the same way the sync engine does (env var plus
--config-env, never argv or .git/config), and are attached only when the remote
is on the account's own host, since http.extraHeader applies to every request
the invocation makes.
The chosen folder and layout (<root>/owner/repo or a flat <root>/repo) persist
in the document store, so they survive a restart on the desktop heads and ride
along in a database backup.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8jbrkMHb7wrUS5f2LH7UN
CopilotAI lite review requested due to automatic review settings August 10, 2026 15:28
@aritchie
aritchie merged commit ff5c01b into mainAug 10, 2026

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Clone feature to GitHubShine that maintains real local git working copies for monitored repositories, persisting user-selected destination settings and surfacing per-repo outcomes in the UI.

Changes:

  • Introduces RepoCloneEngine and related models to clone missing repos and conservatively fast-forward existing working copies.
  • Adds Clone preferences persistence (ClonePrefs) via DocumentDb + JSON source-gen wiring.
  • Adds a new /clone page and sidebar entry, plus small UI/CSS tweaks and README updates.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/GitHubShine/wwwroot/css/app.cssAllows the clone row “path” column to shrink/ellipsis correctly in flex layout.
src/GitHubShine/MauiProgram.csRegisters ClonePrefs for DocumentDb table mapping.
src/GitHubShine/GlobalUsings.csAdds global GitHubShine.Clone namespace import for the new feature.
src/GitHubShine/GitHubShineJsonContext.csAdds ClonePrefs to source-generated JSON serialization context.
src/GitHubShine/Features/Clone/RepoCloneEngine.csImplements clone-or-fast-forward logic via git CLI with conservative safety checks.
src/GitHubShine/Features/Clone/CloneStore.csPersists clone settings and last-run timestamp in DocumentDb.
src/GitHubShine/Features/Clone/ClonePrefs.csDefines persisted clone settings (root directory, layout, last run time).
src/GitHubShine/Features/Clone/CloneOutcome.csDefines clone/update outcomes and UI-friendly summaries.
src/GitHubShine/Components/Pages/ClonePage.razorAdds the Clone tab UI, selection, run loop, and progress/log rendering.
src/GitHubShine/Components/Layout/MainLayout.razorAdds “Clone” navigation entry and icon in the sidebar.
README.mdDocuments the new Clone tab and feature folder structure.
Suppressed comments (1)

src/GitHubShine/Features/Clone/RepoCloneEngine.cs:193

  • The code currently only verifies that origin points at the monitored repo, but then it may fetch from a different tracking remote. That can both (1) wrongly skip valid checkouts (fork with origin=fork, tracking remote=upstream) and (2) fetch/fast-forward from an unrelated remote if the tracking remote is misconfigured. Validate the resolved remoteUrl (the remote you are about to fetch) against repo before fetching.
 var remoteUrl = origin;
if (!string.Equals(remote, "origin", StringComparison.Ordinal))
{
var other = await this.TryGitAsync(path, ["remote", "get-url", remote], null, ct).ConfigureAwait(false);
remoteUrl = other.Success ? other.StdOut.Trim() : "";

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +146 to +150
// A folder holding some other repo (easily done under the flat layout, where two owners'
// repos of the same name land in one place) must not be fetched into.
if (!RemoteMatches(origin, repo))
return Skip(CloneAction.SkippedDifferentRepo, path, null, progress,
$"Skipped — origin is {Redact(origin)}, not {repo.FullName}.");
Comment on lines +27 to +37
/// <summary>The checked-out branch has no matching branch on <c>origin</c>.</summary>
SkippedNoUpstream,

/// <summary>The folder exists but isn't a git working copy.</summary>
SkippedNotARepo,

/// <summary>It's a git working copy, but it has no <c>origin</c> remote to fetch from.</summary>
SkippedNoRemote,

/// <summary><c>origin</c> points at some other repo — almost certainly not the one asked for.</summary>
SkippedDifferentRepo
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@aritchie@claude