Java代码管理工具实现,包含pullpushbranch等基本功能。这个工具模拟了Git的核心概念,使用文件系统存储数据。

import java.io.*;
import java.nio.file.*;
import java.security.MessageDigest;
import java.util.*;
import java.util.stream.Collectors;

public class MiniGit {
    private static final String GIT_DIR = ".minigit";
    private static final String HEAD_FILE = GIT_DIR + "/HEAD";
    private static final String OBJECTS_DIR = GIT_DIR + "/objects";
    private static final String REFS_DIR = GIT_DIR + "/refs/heads";
    private static final String REMOTE_DIR = GIT_DIR + "/remote";

    public static void main(String[] args) throws Exception {
        if (args.length < 1) {
            System.err.println("Usage: java MiniGit <command> [args]");
            return;
        }

        switch (args[0]) {
            case "init" -> init();
            case "commit" -> {
                if (args.length < 2) commit(args[1]);
                else System.err.println("Commit message required");
            }
            case "branch" -> {
                if (args.length >= 2) branch(args[1]);
                else listBranches();
            }
            case "checkout" -> {
                if (args.length >= 2) checkout(args[1]);
                else System.err.println("Branch name required");
            }
            case "push" -> push();
            case "pull" -> pull();
            default -> System.err.println("Unknown command: " + args[0]);
        }
    }

    // 初始化仓库
    private static void init() throws IOException {
        Files.createDirectories(Paths.get(OBJECTS_DIR));
        Files.createDirectories(Paths.get(REFS_DIR));
        Files.createDirectories(Paths.get(REMOTE_DIR));
        setHead("main");
        branch("main"); // 创建默认分支
        System.out.println("Initialized empty MiniGit repository");
    }

    // 创建提交
    private static void commit(String message) throws Exception {
        String branch = getCurrentBranch();
        String parent = getBranchCommit(branch);
        
        // 创建树对象
        String treeHash = createTreeObject();
        
        // 创建提交对象
        String commit = createCommitObject(treeHash, parent, message);
        
        // 更新分支引用
        updateBranchRef(branch, commit);
        System.out.println("[" + branch + " " + commit.substring(0, 6) + "] " + message);
    }

    // 创建/列出分支
    private static void branch(String name) throws IOException {
        Path branchPath = Paths.get(REFS_DIR, name);
        if (!Files.exists(branchPath)) {
            String commit = getCurrentCommit();
            Files.write(branchPath, commit.getBytes());
            System.out.println("Created branch: " + name);
        } else {
            System.err.println("Branch already exists: " + name);
        }
    }

    private static void listBranches() throws IOException {
        String current = getCurrentBranch();
        try (var stream = Files.list(Paths.get(REFS_DIR))) {
            stream.map(Path::getFileName)
                  .forEach(p -> System.out.println((p.toString().equals(current) ? "* " : "  ") + p));
        }
    }

    // 切换分支
    private static void checkout(String branch) throws IOException {
        Path branchPath = Paths.get(REFS_DIR, branch);
        if (!Files.exists(branchPath)) {
            System.err.println("Branch not found: " + branch);
            return;
        }
        
        String commit = Files.readString(branchPath).trim();
        restoreCommit(commit);
        setHead(branch);
        System.out.println("Switched to branch: " + branch);
    }

    // 推送到远程
    private static void push() throws IOException {
        String branch = getCurrentBranch();
        String commit = getCurrentCommit();
        
        // 模拟远程存储
        Path remoteBranch = Paths.get(REMOTE_DIR, branch);
        Files.createDirectories(remoteBranch.getParent());
        Files.write(remoteBranch, commit.getBytes());
        
        System.out.println("Pushed " + branch + " to remote (" + commit.substring(0, 6) + ")");
    }

    // 从远程拉取
    private static void pull() throws IOException {
        String branch = getCurrentBranch();
        Path remoteBranch = Paths.get(REMOTE_DIR, branch);
        
        if (!Files.exists(remoteBranch)) {
            System.err.println("Remote branch not found: " + branch);
            return;
        }
        
        String remoteCommit = Files.readString(remoteBranch).trim();
        restoreCommit(remoteCommit);
        updateBranchRef(branch, remoteCommit);
        System.out.println("Updated " + branch + " to " + remoteCommit.substring(0, 6));
    }

    // ===== 核心工具方法 =====
    private static String createTreeObject() throws Exception {
        StringBuilder tree = new StringBuilder();
        try (var stream = Files.list(Paths.get(""))) {
            for (Path file : stream.filter(Files::isRegularFile).toList()) {
                if (file.toString().equals(GIT_DIR)) continue;
                byte[] content = Files.readAllBytes(file);
                String hash = hashObject(content);
                tree.append(file.getFileName()).append(" ").append(hash).append("\n");
            }
        }
        return writeObject("tree", tree.toString().getBytes());
    }

    private static String createCommitObject(String tree, String parent, String message) throws Exception {
        String commit = "tree " + tree + "\n" +
                       (parent != null ? "parent " + parent + "\n" : "") +
                       "message " + message + "\n";
        return writeObject("commit", commit.getBytes());
    }

    private static void restoreCommit(String commitHash) throws Exception {
        byte[] data = readObject(commitHash);
        String content = new String(data);
        
        // 恢复文件
        for (String line : content.split("\n")) {
            if (line.startsWith("tree")) {
                String treeHash = line.split(" ")[1];
                restoreTree(treeHash);
            }
        }
    }

    private static void restoreTree(String treeHash) throws Exception {
        byte[] data = readObject(treeHash);
        for (String line : new String(data).split("\n")) {
            String[] parts = line.split(" ");
            if (parts.length == 2) {
                Path file = Paths.get(parts[0]);
                byte[] content = readObject(parts[1]);
                Files.write(file, content);
            }
        }
    }

    private static String writeObject(String type, byte[] data) throws Exception {
        String hash = hashObject(data);
        Path objectPath = Paths.get(OBJECTS_DIR, hash);
        Files.write(objectPath, (type + " " + data.length + "\n").getBytes());
        Files.write(objectPath, data, StandardOpenOption.APPEND);
        return hash;
    }

    private static byte[] readObject(String hash) throws IOException {
        Path objectPath = Paths.get(OBJECTS_DIR, hash);
        byte[] allBytes = Files.readAllBytes(objectPath);
        int headerEnd = new String(allBytes).indexOf('\n');
        return Arrays.copyOfRange(allBytes, headerEnd + 1, allBytes.length);
    }

    private static String hashObject(byte[] data) throws Exception {
        MessageDigest digest = MessageDigest.getInstance("SHA-1");
        byte[] hash = digest.digest(data);
        StringBuilder hexString = new StringBuilder();
        for (byte b : hash) {
            hexString.append(String.format("%02x", b));
        }
        return hexString.toString();
    }

    private static void setHead(String branch) throws IOException {
        Files.write(Paths.get(HEAD_FILE), branch.getBytes());
    }

    private static String getCurrentBranch() throws IOException {
        return Files.readString(Paths.get(HEAD_FILE)).trim();
    }

    private static String getCurrentCommit() throws IOException {
        return getBranchCommit(getCurrentBranch());
    }

    private static String getBranchCommit(String branch) throws IOException {
        Path branchPath = Paths.get(REFS_DIR, branch);
        return Files.exists(branchPath) ? Files.readString(branchPath).trim() : null;
    }

    private static void updateBranchRef(String branch, String commit) throws IOException {
        Files.write(Paths.get(REFS_DIR, branch), commit.getBytes());
    }
}

功能说明:

  1. 初始化仓库 init

    • 创建.minigit目录和子目录

    • 初始化默认分支main

  2. 提交更改 commit "message"

    • 创建树对象(记录当前文件状态)

    • 创建提交对象(包含树对象、父提交和消息)

    • 更新分支引用

  3. 分支管理 branch

    • branch:列出所有分支(当前分支前加*

    • branch new-branch:创建新分支

  4. 切换分支 checkout branch-name

    • 恢复分支指向的提交状态

    • 更新HEAD引用

  5. 推送代码 push

    • 将当前分支状态复制到"远程"(.minigit/remote

  6. 拉取代码 pull

    • 从"远程"恢复分支状态到本地

存储结构:

text

复制

下载

.minigit
├── HEAD              # 当前分支
├── objects           # 存储所有对象
├── refs/heads        # 分支引用(指向提交)
└── remote            # 模拟远程仓库

使用示例:

bash

复制

下载

# 初始化仓库
java MiniGit init

# 创建文件
echo "Hello" > test.txt

# 提交更改
java MiniGit commit "Initial commit"

# 创建分支
java MiniGit branch feature

# 切换分支
java MiniGit checkout feature

# 修改文件并提交
echo "World" >> test.txt
java MiniGit commit "Add feature"

# 推送到远程
java MiniGit push

# 切回主分支并拉取
java MiniGit checkout main
java MiniGit pull

注意事项:

  1. 这是一个简化版实现,不包含:

    • 冲突解决

    • 差异比较

    • 网络通信(远程使用本地目录模拟)

    • 完整Git对象模型

  2. 实际文件存储使用SHA-1哈希作为文件名

  3. 仅支持文本文件,未处理二进制文件

  4. 每次提交会存储所有文件的完整副本(非增量存储)

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐