* feat(backend): add pure-Go go-git storage backend Add an opt-in storage/RCS backend (gogit) that implements backend.Storage using github.com/go-git/go-git/v5. It removes the runtime dependency on the git binary for cross-compiled and single-binary distributions and for Windows users without Git for Windows on PATH. - New package internal/backend/storage/gogit implementing the existing backend.Storage interface (file ops delegate to fs.Store, RCS ops use go-git). The interface contract is unchanged. - Registered as gogit with priority 12 (after gitfs) so the CLI backend remains the auto-detected default when git is present. - Selectable via storage.backend=gogit, GOPASS_GIT_BACKEND=gogit or --storage=gogit. - Automatic fallback to gogit when the git binary is absent: the gitfs loader now fails Handles/New/Init/Clone with ErrGitBinaryNotFound and backend.ResolveStorageBackend downgrades a requested GitFS backend. - File-level merge with a two-parent merge commit for non-fast-forward pulls. A true conflict never discards a secret: the local version is kept and the remote version is written to <name>.conflict-<shortsha>. - All transport/auth logic isolated in auth.go (the go-git v6 seam). SSH via x/crypto/ssh + ssh-agent; HTTPS via git credential fill when git is available, otherwise URL userinfo. - Signed commits shell out to git commit -S when configured and git is present; otherwise an unsigned commit is created with a warning. gpg.format=ssh signing is unsupported. - doctor is aware of the pure-Go backend. - Docs: docs/backends/gogit.md, docs/backends.md, docs/config.md, docs/components.dot, docs/commands/git.md, ADR A-17 marked implemented. - Tests: unit + merge-matrix tests, a cross-backend object-graph parity test, and an opt-in integration test. Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * fix(backend): address review feedback on the go-git backend Harden and correct the pure-Go go-git storage backend following review of the initial implementation. Security and correctness: - Validate every path from a remote tree before materialising it: reject absolute paths, paths escaping the store root, and anything inside .git. Also reject .git/.git/... in fs.Store.safePath as defence in depth, so a crafted remote commit can no longer plant a hook or rewrite the config. - Pull before pushing so a remote that is ahead no longer makes every push fail with a non-fast-forward error. - Replace HardReset with MergeReset when fast-forwarding: untracked files are preserved and uncommitted edits abort the reset instead of being silently discarded. Materialise an unborn HEAD first, which is what `gopass setup --remote` needs. - Stage explicit paths with AddOptions{Path} only. go-git checks All before Path, so the previous {All:true, Path:rel} staged the whole worktree and swept unrelated files into every "Save secret" commit. - Never resolve a concurrent .gpg-id (recipient) change automatically: abort the pull with a clear error and leave the worktree untouched. - Make the merge transactional: compute the whole resolution before writing anything and restore the index and worktree to HEAD on failure. - Recreate symlinks from the remote tree instead of writing the target as a regular file, refuse submodules, and include the file mode in the identical-on-both-sides check. - Route the merge commit through the same signing path as Commit. - Pick a collision-free conflict-file name by growing the hash prefix. - Fix Revisions: go-git's PathFilter compares each commit with the next in iteration order, so walk the log and diff against the first parent. - Honour commit.gpgsign from the global scope and pass --no-verify to the signing shell-out so git hooks are never run. - Fix urlUserInfo: only short-circuit on a URL that carries a password and percent-decode the userinfo. Run `git credential fill` in the store directory. Redact the password from clone errors. Backend selection: - Centralise the flag > env > config > default precedence in ResolveGitBackend and GitBackendFromEnvOrConfig, and use it from init, rcs and clone so GOPASS_GIT_BACKEND is honoured everywhere. - Only honour GOPASS_GIT_BACKEND when the configured/detected backend is already a git-family backend, so a cryptfs or jjfs store is not reopened as plain gogit/gitfs. - Resolve the cryptfs substorage so cryptfs stores open on git-less systems. - Move fossilfs to priority 20 to resolve the collision with gogit (12). Also stop reporting the expected "nothing to commit" result of the .gitattributes commit as a failure. Add unit tests for the new behaviour (add isolation, push-pulls-first, untracked preservation, symlink merge, .git rejection, .gpg-id conflict, conflict-name collision, revisions across merges, path validation, auth, no-git path), a cross-backend merge test, and a `gopass sync` integration test. Reconcile the ADR and docs/backends/gogit.md with the implementation. Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * test(gogit): make cross-backend git CLI checks work on Windows The cross-backend tests invoked the git CLI in ways the MSYS2 git used by the Windows CI job cannot handle: - `git -C <dir>` is implemented with a chdir() that does not accept a Windows-style backslash path (e.g. `C:\msys64\tmp\...\001`), so it failed with exit status 128 even though the repository is valid. Set the process working directory via cmd.Dir instead, matching the gitfs backend. - `rev-parse HEAD^{tree}` is mangled by the MSYS2 runtime's brace expansion (`{tree}` -> `tree`), yielding "ambiguous argument 'HEAD^tree'". Use `show -s --format=%T HEAD` instead. Also capture stderr so future failures are diagnosable. Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * fix(backend): address review feedback on the go-git backend Second review round on the pure-Go go-git storage backend: - fs: refuse to write through symlinked path components and replace a symlink with a regular file instead of following it, so a remote tree cannot plant a link into .git and then write through it. SetSymlink now only accepts relative targets that stay inside the store and outside .git. The .git guard also covers Windows aliases (GIT~1, trailing dots/spaces, alternate data streams) and nested components. - gogit: skip local-only deletions in the merge plan (staging them failed with "entry not found"), refuse a non-fast-forward merge over uncommitted changes, and restore only the affected paths on failure. - gogit: preserve staged files across an unborn-HEAD pull (gopass setup --remote), return early when the remote is an ancestor of local instead of creating an empty merge commit, and report a missing remote branch instead of silently doing nothing. - gogit: make the unsigned-commit fallback opt-in via gogit.allow-unsigned-commits, accept yes/on/1 for commit.gpgsign, sign merge commits via .git/MERGE_HEAD (git commit -p is --patch), and honour the commit timestamp for signed commits. - gogit: strip userinfo from remote URLs before handing them to go-git so credentials cannot leak through go-git's error messages; fix splitHTTPURL for username-only URLs and disable the terminal prompt for git credential fill. - gogit: resolve user.name/user.email local-over-global, compare revisions against all parents, exclude untracked files from staged changes, delete loose objects on prune, and treat .age-recipients/.age-ids/ .gopass-mapping as recipient files. - backend: only honour storage.backend for git-family backends and apply GOPASS_GIT_BACKEND after auto-detection, so a cryptfs/jjfs store is not reopened as plain gogit/gitfs. - docs: reconcile the ADR, docs/backends/gogit.md and docs/config.md with the implementation; drop the duplicate A-17 index row. Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * fix(backend): address remaining review feedback on the go-git backend - fs: reject .jj (and .git) metadata paths in safePath; rename the guard to rejectVCSDir/IsVCSDirName and cover the .jj aliases in the tests. - gogit: preserve the executable bit when materialising remote tree entries so a merge does not record a spurious mode change. - gogit: add tests for executable-mode preservation, retry after a partial merge failure, symlink-then-regular-file against .git/config, and the opt-in signed-commit requirement. - fossilfs: rephrase the priority comment (highest among VCS backends). - docs: reconcile the ADR and gogit backend docs with the implementation (all-parents TREESAME history, prune handler, opt-in unsigned fallback, .jj guard, executable mode). Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> * test(gogit): make executable-mode merge assertion Windows-aware TestGoGitMergePreservesExecutableMode asserted Unix semantics on all platforms. Windows has no executable bit: os.Chmod cannot set it and go-git derives the index mode from the OS file mode (filemode.NewFromOSFileMode), so both the worktree bit and the recorded index mode are non-executable there. ADR A-17 already accepts this ("gopass secrets are never executable"), so gate the Unix-only assertions on GOOS and assert the regular-file outcome on Windows. Also document that the executable bit is preserved except on Windows in docs/backends/gogit.md and in the applyFileMode comment, and reword the test comment to describe the resolved mode rather than a Unix mode. Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org> --------- Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>
1.4 KiB
git command
The git command runs an arbitrary git command inside a password store,
without the need to cd into the store directory first.
Note: This command is provided by the gitfs storage backend and is only
available when the selected store uses that backend. It requires the git
binary at runtime. Stores using the pure-Go gogit
backend still have a .git directory, so this command works for them too as
long as git is installed.
Synopsis
$ gopass git status
$ gopass git --store foo log --oneline
$ gopass git remote add origin git@example.com/store.git
Modes of operation
- Run any
gitsubcommand in the directory of the selected store
Flags
| Flag | Aliases | Description |
|---|---|---|
--store |
Store to operate on. |
Details
The command is hidden from the regular help output because it is intended as an
escape hatch. Prefer the dedicated subcommands (e.g. gopass sync) for common
operations.
The arguments after gopass git are passed through to git unchanged. STDIN,
STDOUT and STDERR are connected to the terminal.
Exit codes
The exit code is that of the invoked git process, with 0 indicating success.
See docs/exit-codes.md for the git entry and the full
table.