LogoFreestyle

The Git Objects API

A comprehensive guide to working with Git objects (blobs, commits, trees, tags, and refs) in Freestyle

Git Objects API

The Git Objects API allows you to access and explore Git objects directly from your repositories. This API is useful for building tools that need to understand Git repository structure, inspect files, visualize commit history, and more.

Overview

Git stores all data as objects of four fundamental types:

  1. Blobs - Raw file content
  2. Trees - Directory listings mapping names to blobs or other trees
  3. Commits - Snapshots of the repository at a specific point in time
  4. Tags - References to specific commits with additional metadata

The Git Objects API in Freestyle provides access to all these object types through a consistent REST API.

Usage

Blobs

Blobs represent the content of files in Git. When you retrieve a blob, you get the raw file content (always base64 encoded for binary safety).

Get a Blob

// Using fetch directly with the API
fetch(`https://api.freestyle.sh/git/v1/repo/${repoId}/git/blobs/${blobHash}`, {
  headers: {
    "Authorization": "Bearer your-api-key"
  }
})
.then(response => response.json())
.then(blob => {
  // blob.content is base64 encoded
  const decodedContent = atob(blob.content);
  console.log(decodedContent);
});

Response structure:

interface BlobObject {
  // The blob content (base64 encoded)
  content: string;
  // Always "base64"
  encoding: "base64";
  // The blob's hash
  sha: string;
}

Commits

Commits represent snapshots of your repository at specific points in time.

Get a Commit

fetch(`https://api.freestyle.sh/git/v1/repo/${repoId}/git/commits/${commitHash}`, {
  headers: {
    "Authorization": "Bearer your-api-key"
  }
})
.then(response => response.json())
.then(commit => {
  console.log(commit.message);
  console.log(commit.author);
  console.log(commit.tree.sha);
});

Response structure:

interface CommitObject {
  // The commit author
  author: {
    date: string;
    name: string;
    email: string;
  };
  // The committer (may be different from author)
  committer: {
    date: string;
    name: string;
    email: string;
  };
  // The commit message
  message: string;
  // The tree this commit points to
  tree: {
    sha: string;
  };
  // Parent commits (usually one, multiple for merge commits)
  parents: Array<{
    sha: string;
  }>;
  // The commit's hash
  sha: string;
}

Trees

Trees represent directories in Git. A tree object contains a list of entries, each with a name, type (blob or tree), and hash.

Get a Tree

fetch(`https://api.freestyle.sh/git/v1/repo/${repoId}/git/trees/${treeHash}`, {
  headers: {
    "Authorization": "Bearer your-api-key"
  }
})
.then(response => response.json())
.then(tree => {
  // Inspect files and subdirectories
  tree.tree.forEach(entry => {
    if (entry.type === "blob") {
      console.log(`File: ${entry.path}`);
    } else if (entry.type === "tree") {
      console.log(`Directory: ${entry.path}`);
    }
  });
});

Response structure:

interface TreeObject {
  // The tree's entries (files and subdirectories)
  tree: Array<{
    // The entry's type: "blob" (file) or "tree" (directory)
    type: "blob" | "tree";
    // The entry's path (filename or directory name)
    path: string;
    // The entry's hash
    sha: string;
  }>;
  // The tree's hash
  sha: string;
}

Tags

Tags are references to specific objects (usually commits) with additional metadata like tagger information and a message.

Get a Tag

fetch(`https://api.freestyle.sh/git/v1/repo/${repoId}/git/tags/${tagHash}`, {
  headers: {
    "Authorization": "Bearer your-api-key"
  }
})
.then(response => response.json())
.then(tag => {
  console.log(tag.name);
  console.log(tag.message);
  console.log(tag.target.sha);
});

Response structure:

interface TagObject {
  // The tag name
  name: string;
  // The tagger (may be null for lightweight tags)
  tagger?: {
    date: string;
    name: string;
    email: string;
  };
  // The tag message (may be null for lightweight tags)
  message?: string;
  // The object this tag points to (usually a commit)
  target: {
    sha: string;
  };
  // The tag's hash
  sha: string;
}

Common Use Cases

Processing Files from a Git Trigger

When a Git trigger is invoked by a push to your repository, Freestyle sends a payload containing information about the event, including the commit hash. You can use this to inspect files that were changed in the commit:

// This function would be called by your webhook handler
async function processGitTriggerWebhook(webhookPayload, apiKey) {
  const repoId = webhookPayload.repoId;
  const commitHash = webhookPayload.commit;
 
  const headers = {
    "Authorization": `Bearer ${apiKey}`
  };
 
  // Get the commit to find what was changed
  const commitResponse = await fetch(
    `https://api.freestyle.sh/git/v1/repo/${repoId}/git/commits/${commitHash}`,
    { headers }
  );
  const commit = await commitResponse.json();
 
  console.log(`Processing commit: ${commit.message}`);
  console.log(`Author: ${commit.author.name} <${commit.author.email}>`);
 
  // Get the tree pointed to by the commit
  const treeResponse = await fetch(
    `https://api.freestyle.sh/git/v1/repo/${repoId}/git/trees/${commit.tree.sha}`,
    { headers }
  );
  const rootTree = await treeResponse.json();
 
  // Example: Find package.json in the repository
  const packageJsonEntry = treeEntries.find(entry => entry.type === "blob" && entry.path === "package.json")
  if (packageJsonEntry) {
    // Get the content of package.json
    const blobResponse = await fetch(
      `https://api.freestyle.sh/git/v1/repo/${repoId}/git/blobs/${packageJsonEntry.sha}`,
      { headers }
    );
    const blob = await blobResponse.json();
 
    // Parse the package.json content
    const packageJson = JSON.parse(atob(blob.content));
    console.log(`Project name: ${packageJson.name}`);
    console.log(`Dependencies: ${Object.keys(packageJson.dependencies || {}).length}`);
  }
}

Building a File Browser

You can build a recursive file browser:

async function exploreDirectory(repoId, treeSha, apiKey, currentPath = "") {
  const headers = {
    "Authorization": `Bearer ${apiKey}`
  };
 
  const treeResponse = await fetch(
    `https://api.freestyle.sh/git/v1/repo/${repoId}/git/trees/${treeSha}`,
    { headers }
  );
  const tree = await treeResponse.json();
 
  for (const entry of tree.tree) {
    const entryPath = currentPath ? `${currentPath}/${entry.path}` : entry.path;
 
    if (entry.type === "tree") {
      // Recursively explore subdirectories
      await exploreDirectory(repoId, entry.sha, apiKey, entryPath);
    } else if (entry.type === "blob") {
      // Process files
      console.log(`File: ${entryPath}`);
      // You could fetch the blob content here if needed
    }
  }
}

Viewing File Contents from a Specific Commit

async function viewFileAtCommit(repoId, commitHash, filePath, apiKey) {
  const headers = {
    "Authorization": `Bearer ${apiKey}`
  };
 
  // Get the commit
  const commitResponse = await fetch(
    `https://api.freestyle.sh/git/v1/repo/${repoId}/git/commits/${commitHash}`,
    { headers }
  );
  const commit = await commitResponse.json();
 
  // Get the root tree
  let treeResponse = await fetch(
    `https://api.freestyle.sh/git/v1/repo/${repoId}/git/trees/${commit.tree.sha}`,
    { headers }
  );
  let currentTree = await treeResponse.json();
 
  // Navigate the directory structure
  const pathParts = filePath.split('/');
  const fileName = pathParts.pop();
 
  for (const directory of pathParts) {
    const dirEntry = currentTree.tree.find(
      entry => entry.type === "tree" && entry.path === directory
    );
 
    if (!dirEntry) {
      throw new Error(`Directory not found: ${directory}`);
    }
 
    treeResponse = await fetch(
      `https://api.freestyle.sh/git/v1/repo/${repoId}/git/trees/${dirEntry.sha}`,
      { headers }
    );
    currentTree = await treeResponse.json();
  }
 
  // Find the file in the current directory
  const fileEntry = currentTree.tree.find(
    entry => entry.type === "blob" && entry.path === fileName
  );
 
  if (!fileEntry) {
    throw new Error(`File not found: ${fileName}`);
  }
 
  // Get the file content
  const blobResponse = await fetch(
    `https://api.freestyle.sh/git/v1/repo/${repoId}/git/blobs/${fileEntry.sha}`,
    { headers }
  );
  const blob = await blobResponse.json();
 
  // Decode and return the content
  return atob(blob.content);
}

Best Practices

  1. Cache results when possible - Git objects are immutable, so you can safely cache them
  2. Handle binary data correctly - Remember that blob content is base64 encoded

Conclusion

The Git Objects API provides low-level access to Git repository data, enabling you to build powerful tools for Git repository management, visualization, and integration with other systems.

For detailed API reference documentation, see the API Reference section.

Refs API coming soon.

On this page