Machine Coding Problem

Git Version Control

maco30maco60macoAllinfrastructurecomposite-patternhashingdirected-acyclic-graph-(dag)
Commonly Asked By:GitHubGitLabAtlassianMicrosoft

The Git Version Control system must support:

  • Initializing a repository (init)
  • Adding files to a staging area (add)
  • Committing staged files with metadata (commit)
  • Viewing the commit history (log)

Below is a clean, multi-language codebase blueprint showcasing our design patterns and thread-safety strategies in Java and Python:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.security.MessageDigest;
import java.util.*;
import java.util.concurrent.*;

// 1. Core Git Objects (Composite Pattern)
abstract class GitObject {
    protected String hash;
    public String getHash() { return hash; }
    public abstract String type();
    public abstract String serialize();
    
    protected void computeHash() {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            byte[] hashBytes = digest.digest(serialize().getBytes());
            StringBuilder sb = new StringBuilder();
            for (byte b : hashBytes) {
                sb.append(String.format("%02x", b));
            }
            this.hash = sb.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

class Blob extends GitObject {
    private final String content;

    public Blob(String content) {
        this.content = content;
        computeHash();
    }

    @Override public String type() { return "blob"; }
    @Override public String serialize() { return "blob " + content.length() + "\0" + content; }
    public String getContent() { return content; }
}

class TreeEntry {
    public final String mode;
    public final String name;
    public final String hash;
    public TreeEntry(String mode, String name, String hash) {
        this.mode = mode; this.name = name; this.hash = hash;
    }
}

class Tree extends GitObject {
    private final List<TreeEntry> entries = new ArrayList<>();

    public Tree(List<TreeEntry> entries) {
        this.entries.addAll(entries);
        this.entries.sort(Comparator.comparing(e -> e.name)); // Canonical sort
        computeHash();
    }

    @Override public String type() { return "tree"; }
    @Override public String serialize() {
        StringBuilder sb = new StringBuilder();
        for (TreeEntry e : entries) sb.append(e.mode).append(" ").append(e.name).append("\0").append(e.hash);
        return "tree " + sb.length() + "\0" + sb.toString();
    }
}

class Commit extends GitObject {
    private final String treeHash;
    private final String parentHash;
    private final String author;
    private final String message;
    private final long timestamp;

    public Commit(String treeHash, String parentHash, String author, String message) {
        this.treeHash = treeHash;
        this.parentHash = parentHash;
        this.author = author;
        this.message = message;
        this.timestamp = System.currentTimeMillis();
        computeHash();
    }

    @Override public String type() { return "commit"; }
    @Override public String serialize() {
        String content = "tree " + treeHash + "\n" +
                (parentHash != null ? "parent " + parentHash + "\n" : "") +
                "author " + author + " " + timestamp + "\n\n" +
                message + "\n";
        return "commit " + content.length() + "\0" + content;
    }
    public String getParentHash() { return parentHash; }
}

// 2. Object Database
class ObjectDatabase {
    private final Map<String, GitObject> store = new ConcurrentHashMap<>();

    public void store(GitObject obj) { store.put(obj.getHash(), obj); }
    public GitObject get(String hash) { return store.get(hash); }
}

// 3. The Repository Facade
class Repository {
    private final ObjectDatabase odb = new ObjectDatabase();
    private final Map<String, String> index = new ConcurrentHashMap<>(); // path -> blob hash
    private final Map<String, String> branches = new ConcurrentHashMap<>(); // branch -> commit hash
    private volatile String head = "main"; // current branch

    public Repository() {
        branches.put("main", null);
    }

    public synchronized void add(String path, String content) {
        Blob blob = new Blob(content);
        odb.store(blob);
        index.put(path, blob.getHash());
    }

    public synchronized String commit(String author, String message) {
        List<TreeEntry> entries = new ArrayList<>();
        for (Map.Entry<String, String> entry : index.entrySet()) {
            entries.add(new TreeEntry("100644", entry.getKey(), entry.getValue()));
        }
        Tree tree = new Tree(entries);
        odb.store(tree);

        String parentCommitHash = branches.get(head);
        Commit commit = new Commit(tree.getHash(), parentCommitHash, author, message);
        odb.store(commit);

        branches.put(head, commit.getHash());
        return commit.getHash();
    }

    public void log() {
        String curr = branches.get(head);
        while (curr != null) {
            Commit c = (Commit) odb.get(curr);
            System.out.println("commit " + c.getHash());
            System.out.println("Author: " + c.serialize().split("\n")[2]);
            System.out.println("\n    " + c.serialize().split("\n\n")[1]);
            curr = c.getParentHash();
        }
    }
}

// 4. Main Driver Class
public class GitRunner {
    public static void main(String[] args) {
        Repository repo = new Repository();
        
        System.out.println("--- Adding and Committing First File ---");
        repo.add("hello.txt", "Hello World!");
        repo.commit("Alice <alice@example.com>", "Initial commit: added hello.txt");
        
        System.out.println("\n--- Adding and Committing Second File ---");
        repo.add("main.java", "public class Main {}");
        repo.commit("Bob <bob@example.com>", "Added main.java class");
        
        System.out.println("\n--- Git Log ---");
        repo.log();
    }
}