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:
@@ -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 })
|
||||
}
|
||||
Reference in New Issue
Block a user