git/

Git operations for versioning workflows with secure command execution.

Overview

This module provides a complete interface for git operations needed in versioning workflows: querying commit history, managing tags, staging files, and inspecting repository status. All command arguments are validated character-by-character to prevent command injection attacks.

API

Models

Export Description Implementation
GitCommit Commit with hash, author, date, subject, body, refs commit.ts
GitTag Tag with name, commitHash, type (lightweight/annotated) tag.ts
GitTagType 'lightweight' | 'annotated' tag.ts
GitRef Reference with fullName, name, type, commitHash ref.ts
GitRefType 'branch' | 'tag' | 'remote' | 'head' | 'stash' ref.ts
RepositoryStatus Full repository status with branch, staged, modified status.ts
FileStatus 'added' | 'modified' | 'deleted' | 'renamed' ... status.ts
FileStatusEntry File path with its status status.ts
FileChange File change with path, status, and optional oldPath diff.ts
FileChangeStatus 'added' | 'modified' | 'deleted' | 'renamed' ... diff.ts
GitCommitWithFiles GitCommit extended with changed files diff.ts

Model Factories

Function Description Implementation
createGitCommit(opts) Create a GitCommit object commit.ts
createLightweightTag() Create a lightweight tag tag.ts
createAnnotatedTag() Create an annotated tag tag.ts
createGitRef(opts) Create a GitRef from full ref name ref.ts

Model Utilities

Function Description Implementation
getShortHash(hash) Get 7-character abbreviated hash commit.ts
isSameCommit(a, b) Compare commits by hash commit.ts
isMergeCommit(commit) Check if commit has multiple parents commit.ts
isRootCommit(commit) Check if commit has no parents commit.ts
extractScope(subject) Extract scope from conventional commit commit.ts
extractType(subject) Extract type from conventional commit commit.ts
isAnnotatedTag(tag) Check if tag is annotated tag.ts
isLightweightTag(tag) Check if tag is lightweight tag.ts
extractVersionFromTag Extract version string from tag name tag.ts
extractPackageFromTag Extract package name from tag name tag.ts
buildTagName(pkg, ver) Build tag name from package and version tag.ts
compareTagsByVersion Compare tags by extracted version (descending) tag.ts
isBranchRef(ref) Check if ref is a branch ref.ts
isTagRef(ref) Check if ref is a tag ref.ts
isRemoteRef(ref) Check if ref is a remote tracking branch ref.ts
isHeadRef(ref) Check if ref is HEAD ref.ts
buildRefName(type, n) Build full ref name from type and short name ref.ts
filterRefsByType() Filter refs by type ref.ts
filterRefsByRemote() Filter refs by remote name ref.ts

Git Client Factory

Export Description Implementation
createGitClient(config?) Create unified git client factory.ts
GitClient Interface with all git operations factory.ts
GitClientConfig Config: cwd, timeout, throwOnError factory.ts
DEFAULT_GIT_CLIENT_CONFIG Default configuration values factory.ts

Log Operations

Function Description Implementation
getCommitLog(opts) Get commit history log.ts
getCommitsBetween() Get commits between two refs log.ts
getCommitsSince() Get commits since a ref log.ts
getCommit(hash) Get single commit by hash log.ts
commitExists(hash) Check if commit exists log.ts

Diff Operations

Function Description Implementation
getChangedFilesBetween() Get file paths changed between refs diff.ts
getChangedFilesBetweenWithStatus() Get files with status (add/mod/del) diff.ts
getCommitWithFiles(hash) Get commit with its changed files diff.ts

Tag Operations

Function Description Implementation
getTags(opts) List all tags query-tags.ts
getTag(name) Get single tag by name query-tags.ts
createTag(name) Create a new tag manage-tags.ts
deleteTag(name) Delete a tag manage-tags.ts
tagExists(name) Check if tag exists query-tags.ts
getLatestTag() Get most recent tag by version query-tags.ts
getTagsForPackage() Get tags matching a package pattern query-tags.ts
pushTag(name) Push tag to remote manage-tags.ts

Commit Operations

Function Description Implementation
commit(message) Create a commit with message commit.ts
stage(files) Stage files for commit stage.ts
unstage(files) Unstage files stage.ts
stageAll() Stage all changes stage.ts
amendCommit() Amend the last commit commit.ts
createEmptyCommit() Create an empty commit commit.ts
getHead() Get HEAD commit hash head-info.ts
getCurrentBranch() Get current branch name head-info.ts
hasStagedChanges() Check for staged changes stage.ts
hasUnstagedChanges() Check for unstaged changes stage.ts
hasUntrackedFiles() Check for untracked files head-info.ts
discardChanges() Discard uncommitted changes stage.ts
discardAllChanges() Discard and unstage all stage.ts

Status Operations

Function Description Implementation
getStatus() Get full repository status status.ts
isClean() Check if working tree is clean status.ts
isGitRepository() Check if path is a git repository status.ts
getRepositoryRoot() Get repository root path status.ts
getHeadHash() Get HEAD commit hash head-info.ts
getHeadShortHash() Get abbreviated HEAD hash head-info.ts
hasConflicts() Check for merge conflicts status.ts
getAheadCount() Get commits ahead of upstream status.ts
getBehindCount() Get commits behind upstream status.ts
needsPush() Check if push is needed status.ts
needsPull() Check if pull is needed status.ts
getStagedFiles() Get list of staged files status.ts
getModifiedFiles() Get list of modified files status.ts
getUntrackedFiles() Get list of untracked files status.ts

Operation State

Function Description Implementation
getOperationState() Detect if git is mid-operation operation-state.ts
isOperationInProgress() Check if rebase/merge is in progress operation-state.ts
GitOperationState Result with inProgress, reason, details operation-state.ts
GitOperationStateReason 'rebase-interactive' | 'rebase-apply' | 'merge-in-progress' operation-state.ts

Security Utilities

Function Description Implementation
escapeGitRef(ref) Validate ref for safe shell use factory.ts
escapeGitPath(path) Validate path for safe shell use factory.ts
escapeGitArg(arg) Validate generic argument for safe shell use factory.ts
escapeFilePath(path) Validate file path for safe shell use factory.ts
escapeAuthor(author) Validate author string for safe shell use factory.ts
escapeGitTagPattern() Validate tag pattern for safe shell use factory.ts
escapeGitMessage() Escape message for safe shell use factory.ts

Usage Examples

Using GitClient (Recommended)

import { createGitClient } from '@hyperfrontend/versioning'

const git = createGitClient({ cwd: '/path/to/repo' })

// Get recent commits
const commits = git.getCommitLog({ maxCount: 10 })

// Get commits since last release
const newCommits = git.getCommitsSince('v1.0.0')

// Get files changed between refs
const changedFiles = git.getChangedFilesBetween('origin/main', 'HEAD')
const changesWithStatus = git.getChangedFilesBetweenWithStatus('v1.0.0', 'v2.0.0')

// Get commit with file details
const commitWithFiles = git.getCommitWithFiles('abc1234')
if (commitWithFiles) {
  for (const file of commitWithFiles.files) {
    console.log(`${file.status}: ${file.path}`)
  }
}

// Check repository state
if (git.isClean()) {
  // Create a tag
  git.createTag('v1.1.0', { message: 'Release v1.1.0' })
}

// Check for in-progress operations before committing
const state = git.getOperationState()
if (state.inProgress) {
  console.warn(`Cannot proceed: git ${state.reason} in progress`)
}

Standalone Operations

import {
  getCommitsBetween,
  getTags,
  getLatestTag,
  commit,
  stage,
  getChangedFilesBetween,
  getChangedFilesBetweenWithStatus,
  getCommitWithFiles,
} from '@hyperfrontend/versioning'

// Get commits between tags
const commits = getCommitsBetween('v1.0.0', 'v2.0.0')
console.log(`${commits.length} commits between releases`)

// Get files changed since main
const changedFiles = getChangedFilesBetween('origin/main', 'HEAD')
console.log(`${changedFiles.length} files changed`)

// Get files with status information
const changes = getChangedFilesBetweenWithStatus('v1.0.0', 'v2.0.0')
const added = changes.filter((c) => c.status === 'added')
const deleted = changes.filter((c) => c.status === 'deleted')
console.log(`${added.length} added, ${deleted.length} deleted`)

// Get commit with file details for changelog attribution
const commitWithFiles = getCommitWithFiles('abc1234')
if (commitWithFiles) {
  console.log(`${commitWithFiles.subject} touched ${commitWithFiles.files.length} files`)
}

// Find latest version tag
const latest = getLatestTag({ pattern: '@scope/pkg@' })
console.log(`Latest: ${latest?.name}`)

// Stage and commit
stage(['package.json', 'CHANGELOG.md'])
commit('chore: release v1.1.0')

Working with Tags

import { extractVersionFromTag, extractPackageFromTag, buildTagName, getTagsForPackage } from '@hyperfrontend/versioning'

// Parse tag names
extractVersionFromTag('v1.2.3') // '1.2.3'
extractVersionFromTag('@scope/pkg@1.0.0') // '1.0.0'

extractPackageFromTag('@scope/pkg@1.0.0') // '@scope/pkg'
extractPackageFromTag('utils@1.2.3') // 'utils'

// Build tag names
buildTagName('@scope/pkg', '2.0.0') // '@scope/pkg@2.0.0'
buildTagName('lib', '1.0.0', '${package}-v${version}') // 'lib-v1.0.0'

// Get all tags for a package
const tags = getTagsForPackage('@hyperfrontend/versioning')

Working with Commits

import { createGitCommit, isMergeCommit, extractType, extractScope } from '@hyperfrontend/versioning'

// Parse conventional commits
const subject = 'feat(lib-versioning): add git operations'
extractType(subject) // 'feat'
extractScope(subject) // 'lib-versioning'

// Check commit characteristics
if (isMergeCommit(commit)) {
  console.log('Skipping merge commit')
}

Security

All user input undergoes character-by-character validation before being used in shell commands:

  • No regex: Eliminates ReDoS vulnerabilities
  • Allowlist validation: Only permitted characters pass through
  • Length limits: Prevents memory exhaustion attacks
  • No shell interpolation: Arguments are validated, not escaped-and-quoted

Allowed Characters by Function

Function Allowed Characters
escapeGitRef a-z A-Z 0-9 / - _ . @ ~ ^ { }
escapeGitPath a-z A-Z 0-9 / - _ . (space)
escapeFilePath a-z A-Z 0-9 / - _ . (space)
escapeAuthor a-z A-Z 0-9 - _ . @ < > (space)
escapeGitTagPattern a-z A-Z 0-9 / - _ . @ *

Maximum Input Lengths

Input Type Max Length
Git reference 256
File path 4096
Author string 512
Commit message 10000
Tag pattern 256

See Also

  • workspace/ — Uses git for version coordination
  • flow/ — Orchestrates git commit/tag operations
  • commits/ — Parses conventional commit messages
  • Main README — Package overview and quick start
  • ARCHITECTURE.md — Design principles and data flow