Skip to content

Give a workspace the git identity of its repository - #28

Merged
renatoathaydes merged 3 commits into
mainfrom
claude/git-identity-repo-config-1bea25
Oct 22, 2026
Merged

renatoathaydes merged 3 commits into
mainfrom
claude/git-identity-repo-config-1bea25

Conversation

@renatoathaydes

@renatoathaydes renatoathaydes commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

DevPod gives the dev container a git identity by asking the host, which ran git config --global user.name / user.email. --global reads only ~/.gitconfig and doesn't follow include/includeIf, so a work identity kept in an includeIf "gitdir:~/work/" block was ignored and workspaces committed (and pushed) with the global, often personal, email, even when devpod up ran inside a work repository.

Fix

  • gitcredentials.GetRepositoryUser(dir) checks that dir is in a repository git can use (git -C <dir> rev-parse --git-dir), then runs git -C <dir> config user.name|user.email (no --global). That includes the repository's config and matching include/includeIf directives. GetGlobalUser() runs git config --global --includes.
  • devpod up looks the identity up in the repository the workspace comes from: its local folder, or the directory devpod up runs in, which cmd/up.go passes to workspace.Resolve. It saves it in workspace.json as gitUser, before the workspace client is created. The container asks for the identity on every devpod ssh/IDE connection, which can run from any directory, so it has to be saved. A saved identity isn't looked up again, so a later devpod up ws from elsewhere doesn't change it.
  • Outside a repository (e.g. the desktop app, for a git source), or in a repository git doesn't trust (safe.directory; logged at debug level), nothing is saved. The tunnel server then serves the current global identity (with includes) when the container connects, so it doesn't go stale.
  • New devpod up --git-user-name / --git-user-email set it explicitly (e.g. me+devpod@company.com) and are saved with the workspace. An empty value removes the saved one, so DevPod looks it up again.
  • The container side (configureGitUserLocally) still only fills in a name or email that the user's ~/.gitconfig doesn't have, so a devcontainer's own start command can still set the identity. Its check now follows includes.
  • SetUser used to run git config through sh -c/su -c with the value in double quotes. Since the new flag passes values straight to it, a name with ", $ or a backtick broke the credentials server. It now runs git directly on the same file GetUser reads (then chowns it, like ConfigureHelper).
  • devpod build doesn't connect to the container, so it doesn't look up an identity.

The protocol between CLI and agent is unchanged. The new gitUser field is omitempty, and older agents ignore it.

Docs: a new "Git identity" section in developing-in-workspaces/credentials.mdx, plus CHANGELOG entries.

Testing

  • Unit tests:
    • includeIf in a repository and a subdirectory of it
    • repository config
    • no directory, missing directory, not a repository, untrusted repository
    • global identity with includes
    • SetUser/GetUser round trip with quotes, $ and backticks
    • precedence between flags, saved and looked-up values, empty flags clearing, nothing saved outside a repository
    • the tunnel server's fallback
  • The tests set GIT_CEILING_DIRECTORIES, so they still pass with TMPDIR inside a repository.
  • go vet ./..., go test ./pkg/... ./cmd/... and golangci-lint v1.60.1 (CI flags) all pass.
  • Manually against Docker, with the bug report's setup (global me@personal.com, includeIf giving me@company.com):
    • image workspace started from inside the work repo → saved and container have me@company.com
    • started from outside a repository → nothing saved, container has me@personal.com
    • --git-user-name 'Ann "Nan $HOME' --recreate → container has the literal name
    • --git-user-name '' → saved name removed

Notes

  • A container that already has an identity keeps it, including those of workspaces created before this change. devpod up --recreate applies a new one.
  • Existing behaviour, not changed here: on the first connection to a brand-new container, a devpod ssh --command can run before the credentials server has set the identity.

🤖 Generated with Claude Code

renatoathaydes and others added 3 commits October 22, 2026 11:38
DevPod gave the dev container the identity from `git config --global`,
which only reads ~/.gitconfig and doesn't follow its include or
includeIf directives. A work identity kept in an includeIf block was
ignored, so workspaces committed with the global (often personal) email.

`devpod up` now resolves the identity where the workspace comes from
(its local folder, or the directory `devpod up` runs in) with
`git -C <dir> config`, which includes the repository's config and the
matching includes, falling back to `git config --global --includes`.
It saves the identity with the workspace when it has none yet, so later
connections from any directory use the same one, and the tunnel server
serves it to the container. The new `--git-user-name` and
`--git-user-email` options of `devpod up` set it explicitly.

The container still only fills in a name or email it doesn't have yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tests wrote Windows paths into git config files, where git takes a
backslash as an escape character and rejected the file. Write them with
forward slashes, which git accepts and gitdir patterns need, and match
the gitdir case insensitively, like Windows paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address review findings on the git identity change:

- `devpod up` saved whatever identity it found, also outside any
  repository (e.g. from the desktop app), and never looked it up again,
  so a workspace could keep a personal or outdated identity for good.
  It now only saves a value given with --git-user-name/--git-user-email
  or found in the workspace's repository. Otherwise the container gets
  the current global identity when it connects. An empty flag value
  removes the saved one.
- Check that the directory is in a repository git can use with
  `git rev-parse`, and log why not at debug level, so a repository git
  doesn't trust (safe.directory) no longer silently gives the global
  identity.
- SetUser ran `git config` through a shell with the value in double
  quotes, so a name with a quote or `$` broke the container's credentials
  server. It now runs git directly on the same config file GetUser reads.
- `devpod up` passes the directory it runs in to Resolve instead of
  Resolve reading the process's working directory.
- Keep the tests from finding a repository that the temporary directory
  is in, and cover the new cases.
- Docs and CHANGELOG: existing containers keep their identity until
  `devpod up --recreate`, the container only checks the user's
  ~/.gitconfig, and safe.directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@renatoathaydes
renatoathaydes merged commit b7de53a into main Oct 22, 2026
19 of 20 checks passed
@renatoathaydes
renatoathaydes deleted the claude/git-identity-repo-config-1bea25 branch October 22, 2026 11:29
Sign up for free to 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.

1 participant