feat(core): implement Git repository operations with gRPC services

- Add advertise_refs functionality for Git protocol communication
- Implement archive service with TAR/ZIP format support and streaming
- Create blame service for Git file annotation with line tracking
- Add branch management including create, delete, rename and compare operations
- Implement merge checking with conflict detection and fast-forward handling
- Add cherry-pick functionality for applying commits between branches
- Integrate gix library for Git repository operations and object handling
- Add comprehensive test suite covering all Git operations
- Implement proper error handling and repository validation
- Add pagination support for large result sets
- Create protobuf definitions for all Git operations and data structures
- Add build system for gRPC code generation and dependency management
This commit is contained in:
zhenyi
2026-06-04 13:05:38 +08:00
commit dcb0fb74c5
98 changed files with 20569 additions and 0 deletions
+167
View File
@@ -0,0 +1,167 @@
use gitks::bare::GitBare;
pub fn run_git(work_dir: &std::path::Path, args: &[&str]) -> duct::Expression {
duct::cmd("git", {
let work_str = work_dir.to_string_lossy().into_owned();
let mut v: Vec<String> = vec!["-C".into(), work_str];
v.extend(args.iter().map(|s| s.to_string()));
v
})
.env("GIT_AUTHOR_NAME", "Test")
.env("GIT_AUTHOR_EMAIL", "test@example.com")
.env("GIT_COMMITTER_NAME", "Test")
.env("GIT_COMMITTER_EMAIL", "test@example.com")
}
pub fn run(work_dir: &std::path::Path, args: &[&str]) {
let result = run_git(work_dir, args)
.stdout_capture()
.stderr_capture()
.unchecked()
.run()
.unwrap();
assert!(
result.status.success(),
"git {} failed: {}",
args.join(" "),
String::from_utf8_lossy(&result.stderr)
);
}
pub fn setup_bare_repo() -> (tempfile::TempDir, GitBare) {
let dir = tempfile::tempdir().expect("create temp dir");
let bare_dir = dir.path().join("test-repo");
duct::cmd(
"git",
["init", "--bare", bare_dir.to_string_lossy().as_ref()],
)
.run()
.expect("git init --bare");
let work_dir = dir.path().join("work");
duct::cmd(
"git",
[
"clone",
bare_dir.to_string_lossy().as_ref(),
work_dir.to_string_lossy().as_ref(),
],
)
.run()
.expect("clone");
run(&work_dir, &["checkout", "-b", "main"]);
std::fs::write(work_dir.join("README.md"), "# Test\n").unwrap();
run(&work_dir, &["add", "."]);
run(&work_dir, &["commit", "-m", "initial commit"]);
run(&work_dir, &["branch", "feature"]);
std::fs::write(work_dir.join("src.txt"), "source\n").unwrap();
run(&work_dir, &["add", "."]);
run(&work_dir, &["commit", "-m", "second commit"]);
std::fs::write(work_dir.join("README.md"), "# Test\n\nUpdated.\n").unwrap();
run(&work_dir, &["add", "."]);
run(&work_dir, &["commit", "-m", "third commit"]);
std::fs::create_dir_all(work_dir.join("src/lib")).unwrap();
std::fs::write(work_dir.join("src/lib/mod.rs"), "pub fn hello() {}\n").unwrap();
run(&work_dir, &["add", "."]);
run(&work_dir, &["commit", "-m", "add nested file"]);
run(&work_dir, &["tag", "v0.1.0"]);
run(
&work_dir,
&["push", "-f", "origin", "main:main", "feature:feature"],
);
run(
&work_dir,
&["push", "-f", "origin", "refs/tags/v0.1.0:refs/tags/v0.1.0"],
);
duct::cmd(
"git",
[
"--git-dir",
bare_dir.to_string_lossy().as_ref(),
"symbolic-ref",
"HEAD",
"refs/heads/main",
],
)
.run()
.expect("set HEAD to main");
(dir, GitBare { bare_dir })
}
pub fn setup_bare_repo_with_conflict() -> (tempfile::TempDir, GitBare) {
let dir = tempfile::tempdir().expect("create temp dir");
let bare_dir = dir.path().join("test-repo");
duct::cmd(
"git",
["init", "--bare", bare_dir.to_string_lossy().as_ref()],
)
.run()
.expect("git init --bare");
let work_dir = dir.path().join("work");
duct::cmd(
"git",
[
"clone",
bare_dir.to_string_lossy().as_ref(),
work_dir.to_string_lossy().as_ref(),
],
)
.run()
.expect("clone");
run(&work_dir, &["checkout", "-b", "main"]);
std::fs::write(work_dir.join("file.txt"), "base content\n").unwrap();
run(&work_dir, &["add", "."]);
run(&work_dir, &["commit", "-m", "base commit"]);
run(&work_dir, &["checkout", "-b", "branch-a"]);
std::fs::write(work_dir.join("file.txt"), "branch A content\n").unwrap();
run(&work_dir, &["add", "."]);
run(&work_dir, &["commit", "-m", "branch A change"]);
run(&work_dir, &["checkout", "main"]);
run(&work_dir, &["checkout", "-b", "branch-b"]);
std::fs::write(work_dir.join("file.txt"), "branch B content\n").unwrap();
run(&work_dir, &["add", "."]);
run(&work_dir, &["commit", "-m", "branch B change"]);
run(
&work_dir,
&[
"push",
"-f",
"origin",
"main:main",
"branch-a:branch-a",
"branch-b:branch-b",
],
);
duct::cmd(
"git",
[
"--git-dir",
bare_dir.to_string_lossy().as_ref(),
"symbolic-ref",
"HEAD",
"refs/heads/main",
],
)
.run()
.expect("set HEAD to main");
(dir, GitBare { bare_dir })
}