-
Notifications
You must be signed in to change notification settings - Fork 75
feat: use git remote URL for cross-machine project memory sync #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3d87d79
c42b07e
a88b0bd
ff06340
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,43 @@ export function getGitEmail(): string | null { | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Normalize a git remote URL to a canonical form so that SSH, HTTPS, | ||
| * and with/without `.git` suffix all produce the same identifier. | ||
| * | ||
| * Examples: | ||
| * git@github.com:user/repo.git → github.com/user/repo | ||
| * https://github.com/user/repo → github.com/user/repo | ||
| * git@gitlab.com:org/sub/repo.git → gitlab.com/org/sub/repo | ||
| */ | ||
| export function normalizeGitUrl(url: string): string { | ||
| return url | ||
| .replace(/^[a-z+]+:\/\//, "") // strip protocol (https://, git://, ssh://) | ||
| .replace(/^[^@]+@/, "") // strip user@ prefix (git@, user@) | ||
| .replace(/:(\d+)\//, "/$1/") // preserve port numbers (e.g. :8080/) | ||
| .replace(":", "/") // SSH colon to slash (github.com:user → github.com/user) | ||
| .replace(/\.git$/, "") // strip trailing .git | ||
| .replace(/\/+$/, ""); // strip trailing slashes | ||
| } | ||
|
|
||
| /** | ||
| * Get the git remote URL for the given directory. | ||
| * This provides a stable, cross-machine identifier for projects. | ||
| * Returns null if not in a git repo or no remote configured. | ||
| */ | ||
| export function getGitRemoteUrl(directory: string): string | null { | ||
| try { | ||
| const remoteUrl = execSync("git config --get remote.origin.url", { | ||
| cwd: directory, | ||
| encoding: "utf-8", | ||
| stdio: ["pipe", "pipe", "pipe"], | ||
| }).trim(); | ||
| return remoteUrl || null; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| export function getUserTag(): string { | ||
| // If userContainerTag is explicitly set, use it | ||
| if (CONFIG.userContainerTag) { | ||
|
|
@@ -36,13 +73,37 @@ export function getProjectTag(directory: string): string { | |
| return CONFIG.projectContainerTag; | ||
| } | ||
|
|
||
| // Otherwise, auto-generate based on containerTagPrefix | ||
| // Try to use git remote URL as a stable cross-machine project identifier | ||
| // This allows the same project on different machines to share memories | ||
| const remoteUrl = getGitRemoteUrl(directory); | ||
| if (remoteUrl) { | ||
| return `${CONFIG.containerTagPrefix}_project_${sha256(normalizeGitUrl(remoteUrl))}`; | ||
| } | ||
|
Comment on lines
+78
to
+81
This comment was marked as outdated.
Sorry, something went wrong.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Valid finding. Existing users have memories stored under Fixed in a88b0bd: all read operations (search, list, compaction context fetch) now query both the canonical remote-based tag and the legacy directory-based tag, deduplicating by memory ID. Writes go only to the new tag, so memories gradually migrate forward. |
||
|
|
||
| // Fall back to directory path hash (machine-specific) | ||
| return `${CONFIG.containerTagPrefix}_project_${sha256(directory)}`; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the legacy directory-hash project tag if it differs from the | ||
| * current (remote-based) tag. Used to query old memories created before | ||
| * the git-remote-based tagging was introduced. | ||
| */ | ||
| export function getLegacyProjectTag(directory: string): string | undefined { | ||
| if (CONFIG.projectContainerTag) return undefined; | ||
|
|
||
| const remoteUrl = getGitRemoteUrl(directory); | ||
| if (!remoteUrl) return undefined; | ||
|
|
||
| // A remote exists, so the canonical tag is remote-based. | ||
| // Return the old directory-based tag for migration reads. | ||
| return `${CONFIG.containerTagPrefix}_project_${sha256(directory)}`; | ||
| } | ||
|
|
||
| export function getTags(directory: string): { user: string; project: string } { | ||
| export function getTags(directory: string): { user: string; project: string; legacyProject?: string } { | ||
| return { | ||
| user: getUserTag(), | ||
| project: getProjectTag(directory), | ||
| legacyProject: getLegacyProjectTag(directory), | ||
| }; | ||
| } | ||
This comment was marked as outdated.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Evaluated and this is not a real issue. The port number is part of the server identity — if a server runs on port 2222, all clones will include
:2222and they normalize consistently togitlab.local/2222/user/repo.The only failure case would be one machine using
ssh://git@gitlab.local:22/user/repo.git(explicitly specifying the default port) while another usesssh://git@gitlab.local/user/repo.git. This is vanishingly rare — no standard git tooling generates URLs with the default port.Stripping port numbers would actually be worse: it would incorrectly unify repos from different servers on the same host but different ports (e.g., a staging and production GitLab on the same machine).