Skip to main content

Overview

The working directory (cwd) determines where Git commands are executed. useGit manages this through a stateful system that tracks the current working directory for each Git instance.

How Working Directory Works

When you create a Git instance, the working directory is set and used for all subsequent Git operations:
import { createGit } from "use-git"

const git = createGit({ cwd: "/path/to/repo" })

// All commands execute in /path/to/repo
await git.status()      // runs: git status (in /path/to/repo)
await git.commit("msg") // runs: git commit -m "msg" (in /path/to/repo)

Default Working Directory

When no cwd is specified, the current directory (".") is used:
import git from "use-git"

// Operates in the current directory
await git.status()
This is equivalent to:
import { createGit } from "use-git"

const git = createGit({ cwd: "." })
await git.status()

Changing Working Directory

useGit provides a setCwd() function to update the working directory after instance creation:
import { createGit, setCwd } from "use-git"

const git = createGit({ cwd: "./repo-1" })
await git.status() // operates in ./repo-1

// Change working directory
setCwd("./repo-2")
await git.status() // operates in ./repo-2
setCwd() changes the working directory globally for all instances that share the same state. For independent repositories, create separate instances instead.

Multiple Repositories

Create separate instances when working with multiple repositories:
import { createGit } from "use-git"

const frontend = createGit({ cwd: "./apps/frontend" })
const backend = createGit({ cwd: "./apps/backend" })

// Each operates in its own directory
await frontend.status() // runs in ./apps/frontend
await backend.status()  // runs in ./apps/backend

Sequential Repository Access

If you need to work with repositories sequentially, use setCwd():
import { createGit, setCwd } from "use-git"

const git = createGit()

// Work with first repository
setCwd("./repo-1")
await git.add()
await git.commit("Update repo 1")

// Switch to second repository
setCwd("./repo-2")
await git.add()
await git.commit("Update repo 2")

Absolute vs Relative Paths

Both absolute and relative paths are supported:
import { createGit } from "use-git"

// Relative path (relative to process.cwd())
const git1 = createGit({ cwd: "./my-repo" })

// Absolute path
const git2 = createGit({ cwd: "/home/user/projects/my-repo" })

// Using process utilities
const git3 = createGit({ cwd: process.cwd() })
Relative paths are resolved relative to the Node.js process’s current working directory (process.cwd()), not relative to your source file location.

Common Patterns

Monorepo Package Manager

import { createGit } from "use-git"
import { join } from "node:path"

const packages = ["frontend", "backend", "shared"]

for (const pkg of packages) {
  const git = createGit({ cwd: join("./packages", pkg) })
  const status = await git.status(["--short"])
  
  if (status) {
    console.log(`${pkg} has changes`)
  }
}

Repository Validation Tool

import { createGit } from "use-git"

async function validateRepo(repoPath: string) {
  const git = createGit({ cwd: repoPath })
  
  try {
    // Check if it's a valid Git repository
    const isRepo = await git.isRepo()
    
    if (!isRepo) {
      throw new Error(`${repoPath} is not a Git repository`)
    }
    
    // Check for uncommitted changes
    const isDirty = await git.isDirty()
    
    return { valid: true, dirty: isDirty }
  } catch (error) {
    return { valid: false, error }
  }
}

await validateRepo("/path/to/repo-1")
await validateRepo("/path/to/repo-2")

Temporary Working Directory

import { createGit, setCwd } from "use-git"
import { mkdtemp } from "node:fs/promises"
import { tmpdir } from "node:os"
import { join } from "node:path"

const originalCwd = process.cwd()
const tempDir = await mkdtemp(join(tmpdir(), "git-test-"))

try {
  const git = createGit({ cwd: tempDir })
  
  await git.init()
  await git.commit("Initial commit", { flags: ["--allow-empty"] })
  
  // Use temporary repository
  const log = await git.log()
  console.log(log)
} finally {
  // Restore original directory if needed
  setCwd(originalCwd)
}

Internal Implementation

The working directory is managed through a state module:
// Simplified internal implementation
export let cwd = "."

export function setCwd(next: string) {
  cwd = next
}
When Git commands execute, they use Node.js’s child_process.spawn() with the cwd option:
const child = spawn("git", ["status"], {
  cwd: cwd, // Current working directory from state
})
This stateful approach means the working directory is shared across all operations using the same instance. Create separate instances to isolate working directories.

Troubleshooting

Directory Does Not Exist

If the specified directory doesn’t exist, Git operations will fail:
const git = createGit({ cwd: "/nonexistent/path" })

try {
  await git.status()
} catch (error) {
  // Error: ENOENT: no such file or directory
  console.error(error.message)
}
Solution: Ensure the directory exists before creating the instance:
import { access } from "node:fs/promises"
import { constants } from "node:fs"

const repoPath = "/path/to/repo"

try {
  await access(repoPath, constants.R_OK)
  const git = createGit({ cwd: repoPath })
} catch {
  console.error(`Directory ${repoPath} does not exist or is not accessible`)
}

Not a Git Repository

Operations will fail if the working directory is not a Git repository:
const git = createGit({ cwd: "/tmp" })

try {
  await git.status()
} catch (error) {
  // Error: fatal: not a git repository
  console.error(error.message)
}
Solution: Check if the directory is a Git repository first:
const git = createGit({ cwd: "/tmp" })

if (await git.isRepo()) {
  await git.status()
} else {
  console.log("Not a Git repository")
}

Next Steps

Creating Git Instance

Learn about instance creation and configuration

Error Handling

Handle directory and repository errors

Build docs developers (and LLMs) love