All Git Basic Function

This commit is contained in:
Muhammad Tamir
2025-06-19 19:22:48 +07:00
parent 19676c31bb
commit baa3a41ba2
18 changed files with 777 additions and 6 deletions

View File

@@ -1,11 +1,13 @@
package org.yuemi;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.commands.CommandRegistrar;
public class App extends JavaPlugin {
@Override
public void onEnable() {
getLogger().info("Yuemi Git Plugin enabled.");
new CommandRegistrar(this).registerAll();
}
@Override

View File

@@ -0,0 +1,16 @@
package org.yuemi.commands;
import org.bukkit.plugin.java.JavaPlugin;
public class CommandRegistrar {
private final JavaPlugin plugin;
public CommandRegistrar(JavaPlugin plugin) {
this.plugin = plugin;
}
public void registerAll() {
plugin.getCommand("git").setExecutor(new GitRootCommand(plugin));
}
}

View File

@@ -0,0 +1,34 @@
package org.yuemi.commands;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.commands.SubcommandHandler;
import org.yuemi.commands.subcommands.GitHelpSubcommand;
public class GitRootCommand implements CommandExecutor {
private final JavaPlugin plugin;
private final SubcommandHandler handler;
public GitRootCommand(JavaPlugin plugin) {
this.plugin = plugin;
this.handler = new SubcommandHandler(plugin);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 0) {
new GitHelpSubcommand(plugin).execute(sender, args);
return true;
}
String subcommand = args[0].toLowerCase();
String[] subArgs = java.util.Arrays.copyOfRange(args, 1, args.length);
handler.handle(sender, subcommand, subArgs);
return true;
}
}

View File

@@ -0,0 +1,7 @@
package org.yuemi.commands;
import org.bukkit.command.CommandSender;
public interface SubcommandExecutor {
void execute(CommandSender sender, String[] args);
}

View File

@@ -0,0 +1,44 @@
package org.yuemi.commands;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.commands.subcommands.GitAddSubcommand;
import org.yuemi.commands.subcommands.GitCommitSubcommand;
import org.yuemi.commands.subcommands.GitPushSubcommand;
import org.yuemi.commands.subcommands.GitResetSubcommand;
import org.yuemi.commands.subcommands.GitInitSubcommand;
import org.yuemi.commands.subcommands.GitRemoteSubcommand;
import org.yuemi.commands.subcommands.GitFetchSubcommand;
import org.yuemi.commands.subcommands.GitPullSubcommand;
import org.yuemi.commands.subcommands.GitStatusSubcommand;
import org.yuemi.commands.subcommands.GitHelpSubcommand;
import java.util.HashMap;
import java.util.Map;
public class SubcommandHandler {
private final Map<String, SubcommandExecutor> commands = new HashMap<>();
public SubcommandHandler(JavaPlugin plugin) {
commands.put("add", new GitAddSubcommand(plugin));
commands.put("commit", new GitCommitSubcommand(plugin));
commands.put("push", new GitPushSubcommand(plugin));
commands.put("reset", new GitResetSubcommand(plugin));
commands.put("init", new GitInitSubcommand(plugin));
commands.put("remote", new GitRemoteSubcommand(plugin));
commands.put("fetch", new GitFetchSubcommand(plugin));
commands.put("pull", new GitPullSubcommand(plugin));
commands.put("status", new GitStatusSubcommand(plugin));
commands.put("help", new GitHelpSubcommand(plugin));
}
public void handle(CommandSender sender, String name, String[] args) {
SubcommandExecutor executor = commands.get(name.toLowerCase());
if (executor == null) {
sender.sendMessage("§cUnknown subcommand: " + name);
return;
}
executor.execute(sender, args);
}
}

View File

@@ -0,0 +1,63 @@
package org.yuemi.commands.subcommands;
import org.yuemi.commands.SubcommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.git.GitManager;
import java.util.List;
import java.util.ArrayList;
import java.io.File;
public class GitAddSubcommand implements SubcommandExecutor {
private final JavaPlugin plugin;
public GitAddSubcommand(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
File repoFolder = new File("."); // Default to root
List<String> filesToAdd = new ArrayList<>();
// Parse args
for (String arg : args) {
if (arg.equals(".")) {
// Use server root, add everything
repoFolder = new File(".");
} else if (arg.startsWith("--path=")) {
repoFolder = new File(arg.substring("--path=".length()));
} else if (arg.startsWith("--include=")) {
filesToAdd.add(arg.substring("--include=".length()));
} else if (!arg.startsWith("--")) {
// Add as literal file path
filesToAdd.add(arg);
}
}
try {
if (!repoFolder.exists() || !repoFolder.isDirectory()) {
sender.sendMessage("§cInvalid path: " + repoFolder.getAbsolutePath());
return;
}
GitManager git = new GitManager(repoFolder);
if (filesToAdd.isEmpty()) {
git.addAll();
sender.sendMessage("§aStaged ALL files in: §f" + repoFolder.getCanonicalPath());
} else {
git.addFiles(filesToAdd);
sender.sendMessage("§aStaged specific files in: §f" + repoFolder.getCanonicalPath());
filesToAdd.forEach(f -> sender.sendMessage("§7- §f" + f));
}
} catch (Exception e) {
sender.sendMessage("§cGit add failed: " + e.getMessage());
e.printStackTrace();
}
});
}
}

View File

@@ -0,0 +1,42 @@
package org.yuemi.commands.subcommands;
import org.yuemi.commands.SubcommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.git.GitManager;
import java.io.File;
public class GitCommitSubcommand implements SubcommandExecutor {
private final JavaPlugin plugin;
public GitCommitSubcommand(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
File repoFolder = new File(".");
String message = "Commit from Minecraft";
for (String arg : args) {
if (arg.startsWith("--path=")) {
repoFolder = new File(arg.substring("--path=".length()));
} else if (arg.startsWith("--message=") || arg.startsWith("-m=")) {
message = arg.contains("=") ? arg.split("=", 2)[1] : message;
}
}
try {
GitManager git = new GitManager(repoFolder);
git.commit(message);
sender.sendMessage("§aCommitted: §f" + message);
} catch (Exception e) {
sender.sendMessage("§cGit commit failed: " + e.getMessage());
e.printStackTrace();
}
});
}
}

View File

@@ -0,0 +1,50 @@
package org.yuemi.commands.subcommands;
import org.yuemi.commands.SubcommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.git.GitManager;
import java.io.File;
public class GitFetchSubcommand implements SubcommandExecutor {
private final JavaPlugin plugin;
public GitFetchSubcommand(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
File repoFolder = new File(".");
String username = null;
String token = null;
for (String arg : args) {
if (arg.startsWith("--path=")) {
repoFolder = new File(arg.substring("--path=".length()));
} else if (arg.startsWith("--username=")) {
username = arg.substring("--username=".length());
} else if (arg.startsWith("--token=")) {
token = arg.substring("--token=".length());
}
}
if (username == null || token == null) {
sender.sendMessage("§cUsage: /git fetch --username=... --token=...");
return;
}
try {
GitManager git = new GitManager(repoFolder);
git.fetch(username, token);
sender.sendMessage("§aFetched remote updates.");
} catch (Exception e) {
sender.sendMessage("§cGit fetch failed: " + e.getMessage());
e.printStackTrace();
}
});
}
}

View File

@@ -0,0 +1,30 @@
package org.yuemi.commands.subcommands;
import org.yuemi.commands.SubcommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.commands.SubcommandExecutor;
public class GitHelpSubcommand implements SubcommandExecutor {
private final JavaPlugin plugin;
public GitHelpSubcommand(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
sender.sendMessage("§2==== GitCraft Command Help ====");
sender.sendMessage("§a/git init §7--path=<dir> §8→ Initialize repo in folder");
sender.sendMessage("§a/git remote --add|--set-url|--remove --url=<url> §7[--path=dir]");
sender.sendMessage("§a/git add [file1 file2 ...] §7or §a/git add . §7[--path=dir]");
sender.sendMessage("§a/git reset [--hard|file] §7[--path=dir]");
sender.sendMessage("§a/git commit -m=<msg> §7[--path=dir]");
sender.sendMessage("§a/git status §7[--path=dir]");
sender.sendMessage("§a/git fetch --username=<u> --token=<t> §7[--path=dir]");
sender.sendMessage("§a/git pull --username=<u> --token=<t> §7[--path=dir]");
sender.sendMessage("§a/git push --username=<u> --token=<t> §7[--path=dir]");
sender.sendMessage("§a/git help §8→ Shows this message");
}
}

View File

@@ -0,0 +1,38 @@
package org.yuemi.commands.subcommands;
import org.yuemi.commands.SubcommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.git.GitManager;
import java.io.File;
public class GitInitSubcommand implements SubcommandExecutor {
private final JavaPlugin plugin;
public GitInitSubcommand(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
File repoFolder = new File(".");
for (String arg : args) {
if (arg.startsWith("--path=")) {
repoFolder = new File(arg.substring("--path=".length()));
}
}
try {
GitManager git = new GitManager(repoFolder);
git.initRepo(plugin);
sender.sendMessage("§aInitialized Git repository at: §f" + repoFolder.getAbsolutePath());
} catch (Exception e) {
sender.sendMessage("§cGit init failed: " + e.getMessage());
e.printStackTrace();
}
});
}
}

View File

@@ -0,0 +1,50 @@
package org.yuemi.commands.subcommands;
import org.yuemi.commands.SubcommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.git.GitManager;
import java.io.File;
public class GitPullSubcommand implements SubcommandExecutor {
private final JavaPlugin plugin;
public GitPullSubcommand(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
File repoFolder = new File(".");
String username = null;
String token = null;
for (String arg : args) {
if (arg.startsWith("--path=")) {
repoFolder = new File(arg.substring("--path=".length()));
} else if (arg.startsWith("--username=")) {
username = arg.substring("--username=".length());
} else if (arg.startsWith("--token=")) {
token = arg.substring("--token=".length());
}
}
if (username == null || token == null) {
sender.sendMessage("§cUsage: /git pull --username=... --token=...");
return;
}
try {
GitManager git = new GitManager(repoFolder);
git.pull(username, token);
sender.sendMessage("§aPulled and merged from origin.");
} catch (Exception e) {
sender.sendMessage("§cGit pull failed: " + e.getMessage());
e.printStackTrace();
}
});
}
}

View File

@@ -0,0 +1,50 @@
package org.yuemi.commands.subcommands;
import org.yuemi.commands.SubcommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.git.GitManager;
import java.io.File;
public class GitPushSubcommand implements SubcommandExecutor {
private final JavaPlugin plugin;
public GitPushSubcommand(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
File repoFolder = new File(".");
String username = null;
String token = null;
for (String arg : args) {
if (arg.startsWith("--path=")) {
repoFolder = new File(arg.substring("--path=".length()));
} else if (arg.startsWith("--username=")) {
username = arg.substring("--username=".length());
} else if (arg.startsWith("--token=")) {
token = arg.substring("--token=".length());
}
}
if (username == null || token == null) {
sender.sendMessage("§cUsage: /git push --username=... --token=...");
return;
}
try {
GitManager git = new GitManager(repoFolder);
git.push(username, token);
sender.sendMessage("§aPushed to remote.");
} catch (Exception e) {
sender.sendMessage("§cGit push failed: " + e.getMessage());
e.printStackTrace();
}
});
}
}

View File

@@ -0,0 +1,65 @@
package org.yuemi.commands.subcommands;
import org.yuemi.commands.SubcommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.git.GitManager;
import java.io.File;
public class GitRemoteSubcommand implements SubcommandExecutor {
private final JavaPlugin plugin;
public GitRemoteSubcommand(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
File repoFolder = new File(".");
String url = null;
String mode = "add";
for (String arg : args) {
if (arg.startsWith("--path=")) {
repoFolder = new File(arg.substring("--path=".length()));
} else if (arg.startsWith("--url=")) {
url = arg.substring("--url=".length());
} else if (arg.equalsIgnoreCase("--add")) {
mode = "add";
} else if (arg.equalsIgnoreCase("--set-url")) {
mode = "set";
} else if (arg.equalsIgnoreCase("--remove")) {
mode = "remove";
}
}
try {
GitManager git = new GitManager(repoFolder);
switch (mode) {
case "add":
if (url == null) throw new IllegalArgumentException("Missing --url");
git.addRemote(url);
sender.sendMessage("§aAdded remote origin: " + url);
break;
case "set":
if (url == null) throw new IllegalArgumentException("Missing --url");
git.setRemoteUrl(url);
sender.sendMessage("§aUpdated remote origin URL to: " + url);
break;
case "remove":
git.removeRemote();
sender.sendMessage("§aRemoved remote origin.");
break;
}
} catch (Exception e) {
sender.sendMessage("§cGit remote failed: " + e.getMessage());
e.printStackTrace();
}
});
}
}

View File

@@ -0,0 +1,55 @@
package org.yuemi.commands.subcommands;
import org.yuemi.commands.SubcommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.git.GitManager;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import java.io.File;
public class GitResetSubcommand implements SubcommandExecutor {
private final JavaPlugin plugin;
public GitResetSubcommand(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
File repoFolder = new File("."); // Default path
ResetType resetType = ResetType.MIXED; // default is soft reset (unstage)
if (args.length >= 1 && args[0].startsWith("--path=")) {
String path = args[0].substring("--path=".length());
repoFolder = new File(path);
}
try {
GitManager git = new GitManager(repoFolder);
if (args.length >= 1 && args[0].equalsIgnoreCase("--hard")) {
resetType = ResetType.HARD;
git.reset(resetType);
sender.sendMessage("§cHard reset performed.");
return;
}
if (args.length >= 1 && !args[0].startsWith("--")) {
String file = args[0];
git.resetFile(file);
sender.sendMessage("§aUnstaged file: §f" + file);
return;
}
git.reset(resetType);
sender.sendMessage("§aReset staged changes.");
} catch (Exception e) {
sender.sendMessage("§cGit reset failed: " + e.getMessage());
e.printStackTrace();
}
});
}
}

View File

@@ -0,0 +1,68 @@
package org.yuemi.commands.subcommands;
import org.yuemi.commands.SubcommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.yuemi.git.GitManager;
import org.eclipse.jgit.api.Status;
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class GitStatusSubcommand implements SubcommandExecutor {
private final JavaPlugin plugin;
public GitStatusSubcommand(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
File repoFolder = new File(".");
for (String arg : args) {
if (arg.startsWith("--path=")) {
repoFolder = new File(arg.substring("--path=".length()));
}
}
try {
GitManager git = new GitManager(repoFolder);
Status status = git.getStatus();
Stream<String> changes = Stream.concat(
status.getUncommittedChanges().stream().map(f -> "Uncommitted: " + f),
Stream.concat(
status.getUntracked().stream().map(f -> "Untracked: " + f),
status.getModified().stream().map(f -> "Modified: " + f)
)
);
List<String> messages = changes.collect(Collectors.toList());
if (messages.isEmpty()) {
sender.sendMessage("§aClean working directory.");
return;
}
if (sender instanceof Player) {
messages.stream().limit(10).forEach(msg -> sender.sendMessage("§e" + msg));
if (messages.size() > 10) {
sender.sendMessage("§7... and " + (messages.size() - 10) + " more.");
}
} else {
messages.forEach(msg -> sender.sendMessage("[GitStatus] " + msg));
}
} catch (Exception e) {
sender.sendMessage("§cGit status failed: " + e.getMessage());
e.printStackTrace();
}
});
}
}

View File

@@ -0,0 +1,146 @@
package org.yuemi.git;
import java.util.List;
import java.util.ArrayList;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.AddCommand;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
public class GitManager {
private final File repoDir;
private Git git;
public GitManager(File repoDir) {
this.repoDir = repoDir;
}
private Git getOrOpenGit() throws Exception {
if (git == null) {
git = Git.open(repoDir);
}
return git;
}
public boolean isCloned() {
return new File(repoDir, ".git").exists();
}
public void cloneRepo(String url) throws Exception {
this.git = Git.cloneRepository()
.setURI(url)
.setDirectory(repoDir)
.call();
}
public void addAll() throws Exception {
getOrOpenGit().add().addFilepattern(".").call();
}
public void addFiles(List<String> files) throws Exception {
AddCommand add = getOrOpenGit().add();
for (String file : files) {
add.addFilepattern(file);
}
add.call();
}
public void commit(String message) throws Exception {
getOrOpenGit()
.commit()
.setMessage(message)
.setAuthor("Minecraft", "mc@localhost")
.call();
}
public void push(String username, String token) throws Exception {
getOrOpenGit()
.push()
.setCredentialsProvider(new UsernamePasswordCredentialsProvider(username, token))
.call();
}
public void checkout(String branch) throws Exception {
getOrOpenGit()
.checkout()
.setName(branch)
.call();
}
public void close() {
if (git != null) {
git.close();
}
}
public void reset(ResetType type) throws Exception {
getOrOpenGit().reset().setMode(type).call();
}
public void resetFile(String filePath) throws Exception {
getOrOpenGit().reset().addPath(filePath).call();
}
public void initRepo(JavaPlugin plugin) throws Exception {
if (git == null) {
git = Git.init().setDirectory(repoDir).call();
}
File gitignore = new File(repoDir, ".gitignore");
if (!gitignore.exists()) {
try (InputStream in = plugin.getResource("gitignore.txt")) {
if (in != null) {
Files.copy(in, gitignore.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
}
}
public void addRemote(String url) throws Exception {
getOrOpenGit().remoteAdd()
.setName("origin")
.setUri(new org.eclipse.jgit.transport.URIish(url))
.call();
}
public void setRemoteUrl(String url) throws Exception {
getOrOpenGit().remoteSetUrl()
.setRemoteName("origin")
.setRemoteUri(new org.eclipse.jgit.transport.URIish(url))
.call();
}
public void removeRemote() throws Exception {
getOrOpenGit().remoteRemove()
.setRemoteName("origin")
.call();
}
public void fetch(String username, String token) throws Exception {
getOrOpenGit()
.fetch()
.setCredentialsProvider(new UsernamePasswordCredentialsProvider(username, token))
.call();
}
public void pull(String username, String token) throws Exception {
getOrOpenGit()
.pull()
.setCredentialsProvider(new UsernamePasswordCredentialsProvider(username, token))
.call();
}
public Status getStatus() throws Exception {
return getOrOpenGit().status().call();
}
}

View File

@@ -0,0 +1,14 @@
# Minecraft plugin defaults
logs/
*.log
*.tmp
*.lock
*.bak
*.swp
cache/
debug/
.idea/
*.iml
*.class
target/
build/

View File

@@ -5,9 +5,6 @@ authors:
version: 1.0
api-version: 1.21
commands:
gitclone:
description: Clones a Git repository
gitcommit:
description: Commits all changes
gitpush:
description: Pushes to remote
git:
description: Git subcommand dispatcher
usage: /git <add|commit|push|clone>