git

package
v1.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 12 Imported by: 0

README

Git App

Installs and configures Git with devgeta integration.

Recovering Lost Commits

Git rarely deletes commits immediately. A reset --hard, bad rebase, or force-push makes commits unreferenced — they survive ~90 days before GC.

1. Check reflog
git reflog --date=iso              # current branch
git reflog --date=iso --all        # all refs including remotes

Look for the line before the reset/rebase.

2. Scan dangling commits (if reflog isn't enough)
git fsck --no-reflogs --lost-found

List them readable, newest first:

for c in $(git fsck --no-reflogs 2>/dev/null | awk '/dangling commit/{print $3}'); do
  echo "$(git show -s --format='%ci %h %an | %s' $c)"
done | sort -r | head -30
3. Inspect before trusting
git show <hash>            # full diff
git show --stat <hash>     # files changed
git log --oneline <hash>   # chain behind it
4. Pin it (prevents GC)
git branch recovered <hash>
5. Restore
git reset --hard <hash>                      # move branch to commit
git push --force-with-lease origin <branch>  # restore remote

Special Cases

Staged but never committed (git add only):

git fsck --lost-found                # writes blobs to .git/lost-found/other/
git show <blob-hash>                 # inspect contents

You get file contents but not names.

Never staged: Unrecoverable from git. Check editor undo/local history.

Prevention

git push --force-with-lease    # refuses if remote changed
git branch backup              # before risky rebases

Common Workflows

Create a clean branch
git fetch origin
git checkout -b <branch> origin/main
git add . && git commit -m "feat: description"
git push -u origin <branch>
Re-sync with main (preserving uncommitted work)
git reset --soft <commit-before-your-work>
git stash
git merge main
# resolve conflicts if any, then:
git stash pop
git restore --staged .   # optional: unstage
Re-sync with main (preserving committed work)

When your work is already committed, replay it on top of the updated main.

Suppose your history is:

295769d  <-- main (old base)
   \
    ... your commits ...  <-- HEAD (feat/your-branch)

Primary: rebase. One command — linear history, no merge commit. Best for a personal branch with a few clean commits. Conflicts are resolved per commit, so they can recur across commits.

git switch feat/your-branch
git rebase main          # replays your commits on top of latest main

Alternative: wip branch + merge. Reach for this when conflicts are messy (e.g. package.json, package-lock.json) and you'd rather resolve them once, when you must keep the original commit SHAs (shared branch), or when you want an explicit backup branch instead of relying on the reflog.

git switch -c wip/your-branch       # pin your commits on a temp branch
git switch feat/your-branch         # back to your working branch
git reset --hard <old-base>         # e.g. 295769d — drop to main's old base
git merge main                      # fast-forward to latest main
git merge wip/your-branch           # replay your commits; resolve conflicts once

Either way, if the branch was already pushed, update the remote with git push --force-with-lease. Clean up the temp branch at the end:

git branch -d wip/your-branch
Squash merge into clean branch
git fetch origin
git checkout -b <branch> origin/main
git merge --squash origin/<source-branch>
git commit -m "feat: combined description"
git push -u origin <branch>

Diagnosing Branch Divergence

Symptom: git pull fails or says "Already up to date" but the expected files (e.g. a PR's content) aren't present. Usually the local branch and the remote branch share a name but have different histories — the local one is often just a copy of main under a different name.

1. Compare the two tips
git rev-parse HEAD                  # local tip
git rev-parse origin/<branch>       # remote tip (after fetch)

Different hashes → divergent histories.

2. Check for unique commits on each side
git fetch origin <branch>
git log --oneline origin/main..HEAD              # unique LOCAL commits
git log --oneline HEAD..origin/<branch>          # unique REMOTE commits

If "unique local commits" is empty, the local branch has no work of its own — it's safe to point it at the remote.

3. Check upstream tracking
git branch -vv                      # lists tracking branch per local branch
git rev-parse --abbrev-ref @{upstream}   # errors if no upstream is set

No upstream explains why a bare git pull fails. git pull origin HEAD resolves origin/HEAD (usually main), which is why it pulls the wrong ref and reports "Already up to date."

4. Align local branch to the real remote branch

Only when step 2 confirms no unique local work:

git fetch origin <branch>
git reset --hard origin/<branch>                 # adopt the remote history
git branch --set-upstream-to=origin/<branch>     # future `git pull` just works

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Git

type Git struct {
	Cmd  cmd.Command
	Base cmd.BaseCommandExecutor
	// Stream, when true, tees git command output (clone/pull/fetch/merge/…) to
	// the terminal in real time. Used by `dg task` utilities so humans and
	// agents see progress as it happens. Commands whose output is parsed
	// (e.g. ListBranches) intentionally stay non-streaming.
	Stream bool
}

func New

func New() *Git

func (*Git) BranchExists

func (g *Git) BranchExists(branch string) (bool, error)

BranchExists checks if a branch exists in the repository

func (*Git) BranchExistsIn

func (g *Git) BranchExistsIn(dir, branch string) (bool, error)

BranchExistsIn is BranchExists evaluated against the repository at dir ("" = current directory).

func (*Git) CheckHookCompatibility

func (g *Git) CheckHookCompatibility(repoRoot string) []string

CheckHookCompatibility scans the repo's effective hooks directory for scripts that use `[ -d .git ]` or `test -d .git`. In a git worktree the .git entry is a FILE, not a directory, so those checks always fail and block git commit.

Also detects Affiance hooks which have a known bug where the .git file regex fails to match due to trailing newlines, causing "no .git directory found". See: https://github.com/mariusbutuc/affiance/issues/XXX

Returns one warning string per offending hook file, or nil if all clear.

func (*Git) Clone

func (g *Git) Clone(url, dstPath string) error

func (*Git) CreateWorktree

func (g *Git) CreateWorktree(path, branch string) error

CreateWorktree creates a new worktree with a branch Handles three cases: 1. Local branch exists: checkout that branch 2. Remote branch exists: create tracking branch (after fetch) 3. Neither exists: create new branch from the freshly-fetched default branch

func (*Git) CreateWorktreeIn

func (g *Git) CreateWorktreeIn(repoDir, path, branch string) error

CreateWorktreeIn is CreateWorktree evaluated against the repository at repoDir ("" = current directory), so worktrees can be created for a repo the caller is not inside.

func (*Git) CurrentBranch

func (g *Git) CurrentBranch() (string, error)

CurrentBranch returns the checked-out branch name, or "" when HEAD is detached (mirrors `git branch --show-current`).

func (*Git) CurrentBranchIn

func (g *Git) CurrentBranchIn(dir string) (string, error)

CurrentBranchIn is CurrentBranch evaluated against the repository at dir ("" = current directory).

func (*Git) DeepClean

func (g *Git) DeepClean(url, dstPath string) error

func (*Git) DefaultBranch

func (g *Git) DefaultBranch() string

DefaultBranch returns the repository's default branch name (e.g. "main"). It resolves origin/HEAD when available; when unset it probes origin/main, origin/master, origin/develop in order via RemoteBranchExists and returns the first that exists, falling back to "main" as a last resort so callers always get a usable branch name.

func (*Git) DefaultBranchIn

func (g *Git) DefaultBranchIn(dir string) string

DefaultBranchIn is DefaultBranch evaluated against the repository at dir ("" = current directory).

func (*Git) DeleteBranch

func (g *Git) DeleteBranch(branch string, isForced bool) error

func (*Git) ExecuteCommand

func (g *Git) ExecuteCommand(args ...string) error

func (*Git) ExecuteCommandAt

func (g *Git) ExecuteCommandAt(dir string, args ...string) error

ExecuteCommandAt runs a git command with -C <dir> so it operates in the given directory regardless of the process's current working directory.

func (*Git) FetchOrigin

func (g *Git) FetchOrigin() error

func (*Git) FetchOriginTimeout

func (g *Git) FetchOriginTimeout(timeout time.Duration) error

FetchOriginTimeout runs `git fetch origin` bounded by timeout, so a hung network call can't block a caller expecting a fast response (e.g. TaskManager.ReviewScope). A zero timeout is unbounded, same as FetchOrigin.

func (*Git) ForceConfigure

func (g *Git) ForceConfigure() error

func (*Git) ForceInstall

func (g *Git) ForceInstall() error

func (*Git) GetMainWorktree

func (g *Git) GetMainWorktree(fromPath string) (string, error)

GetMainWorktree resolves the main worktree (repo root) path from any worktree in the repo, via `git worktree list --porcelain`'s first "worktree <path>" line (always the main worktree). Exported so callers outside this package (e.g. the worktree tooling's repo-candidate resolution) can reuse the same mechanism instead of duplicating it.

func (*Git) GetRepoRoot

func (g *Git) GetRepoRoot() (string, error)

GetRepoRoot returns the root directory of the current git repository

func (*Git) GetRepoRootIn

func (g *Git) GetRepoRootIn(dir string) (string, error)

GetRepoRootIn is GetRepoRoot evaluated against the repository at dir ("" = current directory). It also validates that dir is inside a git repo.

func (*Git) Install

func (g *Git) Install() error

func (*Git) IsWorktreeDirty

func (g *Git) IsWorktreeDirty(path string) (bool, error)

IsWorktreeDirty checks if a worktree has uncommitted changes

func (*Git) Kind

func (g *Git) Kind() apps.AppKind

func (*Git) ListBranches

func (g *Git) ListBranches() ([]string, error)

ListBranches returns all local branch names, stripping the current-branch marker (* ) and surrounding whitespace.

func (*Git) ListWorktrees

func (g *Git) ListWorktrees() ([]WorktreeInfo, error)

ListWorktrees returns parsed worktree information

func (*Git) ListWorktreesAt

func (g *Git) ListWorktreesAt(dir string) ([]WorktreeInfo, error)

ListWorktreesAt lists worktrees for the git repository at the given directory. This avoids depending on the current working directory.

func (*Git) Name

func (g *Git) Name() string

func (*Git) Pop

func (g *Git) Pop(branch string) error

func (*Git) PruneWorktrees

func (g *Git) PruneWorktrees() error

PruneWorktrees removes stale worktree entries

func (*Git) PruneWorktreesAt

func (g *Git) PruneWorktreesAt(dir string) error

PruneWorktreesAt removes stale worktree entries, running from the given directory.

func (*Git) Pull

func (g *Git) Pull(branch string) error

func (*Git) RemoteBranchExists

func (g *Git) RemoteBranchExists(branch string) (bool, error)

RemoteBranchExists checks if a remote branch exists (e.g., origin/feature-A)

func (*Git) RemoteBranchExistsIn

func (g *Git) RemoteBranchExistsIn(dir, branch string) (bool, error)

RemoteBranchExistsIn is RemoteBranchExists evaluated against the repository at dir ("" = current directory).

func (*Git) RemoveWorktree

func (g *Git) RemoveWorktree(path string, deleteBranch bool, branchName string) error

RemoveWorktree removes a worktree and optionally its associated branch. Resolves the main worktree first so the remove command doesn't run from within the worktree being deleted.

func (*Git) Restore

func (g *Git) Restore(branch, files string) error

func (*Git) RunCapture

func (g *Git) RunCapture(args ...string) (string, error)

RunCapture runs a git command and returns its stdout, for callers (e.g. `dg task`) that need to parse output rather than just check for an error.

func (*Git) ShortHead

func (g *Git) ShortHead() (string, error)

ShortHead returns HEAD's short commit SHA (mirrors `git rev-parse --short HEAD`).

func (*Git) SoftConfigure

func (g *Git) SoftConfigure() error

func (*Git) SoftInstall

func (g *Git) SoftInstall() error

func (*Git) SwitchBranch

func (g *Git) SwitchBranch(branch string) error

func (*Git) Uninstall

func (g *Git) Uninstall() error

func (*Git) Update

func (g *Git) Update() error

type WorktreeInfo

type WorktreeInfo struct {
	Path   string
	Branch string
	Commit string
}

WorktreeInfo contains information about a git worktree.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL