git-protocol
gitoxideを活用し、Gutsリポジトリの操作においてGitプロトコルの実装パターンを適用することで、効率的なリポジトリ管理やデータ転送を実現するSkill。
📜 元の英語説明(参考)
Git protocol implementation patterns using gitoxide for Guts repository operations
🇯🇵 日本人クリエイター向け解説
gitoxideを活用し、Gutsリポジトリの操作においてGitプロトコルの実装パターンを適用することで、効率的なリポジトリ管理やデータ転送を実現するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o git-protocol.zip https://jpskill.com/download/16920.zip && unzip -o git-protocol.zip && rm git-protocol.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/16920.zip -OutFile "$d\git-protocol.zip"; Expand-Archive "$d\git-protocol.zip" -DestinationPath $d -Force; ri "$d\git-protocol.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
git-protocol.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
git-protocolフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
Guts 用 Git プロトコルスキル
あなたは gitoxide (gix) を使用して、Git 互換のリポジトリ操作を実装しています。
Gitoxide の概要
Gitoxide は、純粋な Rust による Git の実装です。主なクレートは以下の通りです。
gix: 高レベルな Git 操作gix-object: Git オブジェクト型gix-hash: オブジェクト ID の取り扱いgix-pack: パックファイルの操作gix-transport: Git プロトコルトランスポート
リポジトリ操作
リポジトリのオープン/作成
use gix::Repository;
use std::path::Path;
pub async fn open_or_create(path: &Path) -> Result<Repository> {
match gix::open(path) {
Ok(repo) => Ok(repo),
Err(_) => {
// Create new bare repository
gix::init_bare(path)?
}
}
}
オブジェクトの操作
use gix::ObjectId;
use gix::object::Kind;
pub struct ObjectStore {
repo: Repository,
}
impl ObjectStore {
pub fn get_object(&self, id: &ObjectId) -> Result<Object> {
let object = self.repo.find_object(id)?;
match object.kind {
Kind::Blob => self.decode_blob(object),
Kind::Tree => self.decode_tree(object),
Kind::Commit => self.decode_commit(object),
Kind::Tag => self.decode_tag(object),
}
}
pub fn write_blob(&self, data: &[u8]) -> Result<ObjectId> {
let id = self.repo.write_blob(data)?;
Ok(id)
}
}
コミット
use gix::actor::Signature;
pub struct CommitBuilder<'a> {
repo: &'a Repository,
tree: ObjectId,
parents: Vec<ObjectId>,
message: String,
author: Signature,
}
impl<'a> CommitBuilder<'a> {
pub fn new(repo: &'a Repository) -> Self {
let now = gix::date::Time::now_local_or_utc();
let default_sig = Signature {
name: "Guts User".into(),
email: "user@guts.local".into(),
time: now,
};
Self {
repo,
tree: ObjectId::null(),
parents: vec![],
message: String::new(),
author: default_sig,
}
}
pub fn tree(mut self, tree: ObjectId) -> Self {
self.tree = tree;
self
}
pub fn parent(mut self, parent: ObjectId) -> Self {
self.parents.push(parent);
self
}
pub fn message(mut self, msg: impl Into<String>) -> Self {
self.message = msg.into();
self
}
pub fn commit(self) -> Result<ObjectId> {
let commit = gix::objs::CommitRef {
tree: self.tree,
parents: self.parents.into(),
author: self.author.clone(),
committer: self.author,
encoding: None,
message: self.message.into(),
extra_headers: vec![],
};
let id = self.repo.write_object(&commit)?;
Ok(id)
}
}
Git プロトコルの実装
Smart HTTP プロトコル
use axum::{Router, routing::post, extract::Path};
pub fn git_http_router() -> Router {
Router::new()
.route("/:owner/:repo/git-upload-pack", post(upload_pack))
.route("/:owner/:repo/git-receive-pack", post(receive_pack))
.route("/:owner/:repo/info/refs", get(info_refs))
}
async fn upload_pack(
Path((owner, repo)): Path<(String, String)>,
body: Bytes,
) -> Result<impl IntoResponse> {
let repo = get_repository(&owner, &repo).await?;
// Parse want/have lines
let request = parse_upload_pack_request(&body)?;
// Generate packfile with requested objects
let packfile = generate_packfile(&repo, &request).await?;
Ok((
[(header::CONTENT_TYPE, "application/x-git-upload-pack-result")],
packfile,
))
}
async fn receive_pack(
Path((owner, repo)): Path<(String, String)>,
body: Bytes,
) -> Result<impl IntoResponse> {
let repo = get_repository(&owner, &repo).await?;
// Parse commands and packfile
let (commands, packfile) = parse_receive_pack(&body)?;
// Verify permissions
verify_push_permissions(&owner, &repo).await?;
// Apply packfile
apply_packfile(&repo, &packfile).await?;
// Update refs
for cmd in commands {
update_ref(&repo, &cmd).await?;
}
Ok((
[(header::CONTENT_TYPE, "application/x-git-receive-pack-result")],
"ok\n",
))
}
パックファイルの生成
use gix::pack;
pub async fn generate_packfile(
repo: &Repository,
wants: &[ObjectId],
haves: &[ObjectId],
) -> Result<Vec<u8>> {
// Find all objects to include
let objects = repo.rev_walk(wants)
.sorting(Sorting::ByCommitTimeNewestFirst)
.ancestors()
.filter(|id| !haves.contains(id))
.collect::<Vec<_>>();
// Create pack file
let mut pack_data = Vec::new();
let mut writer = pack::data::output::bytes::Writer::new(&mut pack_data);
for oid in objects {
let object = repo.find_object(oid)?;
writer.write_entry(object)?;
}
writer.finish()?;
Ok(pack_data)
}
リファレンスの管理
pub struct RefStore {
repo: Repository,
}
impl RefStore {
pub fn list_refs(&self) -> Result<Vec<(String, ObjectId)>> {
let refs = self.repo.references()?;
refs.all()?
.map(|r| {
let r = r?;
Ok((r.name().to_string(), r.target().id()))
})
.collect()
}
pub fn update_ref(&self, name: &str, new_id: ObjectId, old_id: Option<ObjectId>) -> Result<()> {
let ref_log_message = format!("guts: update {}", name);
if let Some(old) = old_id {
// Atomic compare-and-swap
self.repo
.reference(name, new_id, PreviousValue::MustExistAndMatch(old.into()))?;
} else {
// Create new ref
self.repo
.reference(name, new_id, PreviousValue::MustNotExist)?;
}
Ok(())
}
pub fn get_head(&self) -> Result<ObjectId> {
let head = self.repo.head_commit()?;
Ok(head.id) 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Git Protocol Skill for Guts
You are implementing Git-compatible repository operations using gitoxide (gix).
Gitoxide Overview
Gitoxide is a pure-Rust Git implementation. Key crates:
gix: High-level Git operationsgix-object: Git object typesgix-hash: Object ID handlinggix-pack: Pack file operationsgix-transport: Git protocol transport
Repository Operations
Opening/Creating Repositories
use gix::Repository;
use std::path::Path;
pub async fn open_or_create(path: &Path) -> Result<Repository> {
match gix::open(path) {
Ok(repo) => Ok(repo),
Err(_) => {
// Create new bare repository
gix::init_bare(path)?
}
}
}
Working with Objects
use gix::ObjectId;
use gix::object::Kind;
pub struct ObjectStore {
repo: Repository,
}
impl ObjectStore {
pub fn get_object(&self, id: &ObjectId) -> Result<Object> {
let object = self.repo.find_object(id)?;
match object.kind {
Kind::Blob => self.decode_blob(object),
Kind::Tree => self.decode_tree(object),
Kind::Commit => self.decode_commit(object),
Kind::Tag => self.decode_tag(object),
}
}
pub fn write_blob(&self, data: &[u8]) -> Result<ObjectId> {
let id = self.repo.write_blob(data)?;
Ok(id)
}
}
Commits
use gix::actor::Signature;
pub struct CommitBuilder<'a> {
repo: &'a Repository,
tree: ObjectId,
parents: Vec<ObjectId>,
message: String,
author: Signature,
}
impl<'a> CommitBuilder<'a> {
pub fn new(repo: &'a Repository) -> Self {
let now = gix::date::Time::now_local_or_utc();
let default_sig = Signature {
name: "Guts User".into(),
email: "user@guts.local".into(),
time: now,
};
Self {
repo,
tree: ObjectId::null(),
parents: vec![],
message: String::new(),
author: default_sig,
}
}
pub fn tree(mut self, tree: ObjectId) -> Self {
self.tree = tree;
self
}
pub fn parent(mut self, parent: ObjectId) -> Self {
self.parents.push(parent);
self
}
pub fn message(mut self, msg: impl Into<String>) -> Self {
self.message = msg.into();
self
}
pub fn commit(self) -> Result<ObjectId> {
let commit = gix::objs::CommitRef {
tree: self.tree,
parents: self.parents.into(),
author: self.author.clone(),
committer: self.author,
encoding: None,
message: self.message.into(),
extra_headers: vec![],
};
let id = self.repo.write_object(&commit)?;
Ok(id)
}
}
Git Protocol Implementation
Smart HTTP Protocol
use axum::{Router, routing::post, extract::Path};
pub fn git_http_router() -> Router {
Router::new()
.route("/:owner/:repo/git-upload-pack", post(upload_pack))
.route("/:owner/:repo/git-receive-pack", post(receive_pack))
.route("/:owner/:repo/info/refs", get(info_refs))
}
async fn upload_pack(
Path((owner, repo)): Path<(String, String)>,
body: Bytes,
) -> Result<impl IntoResponse> {
let repo = get_repository(&owner, &repo).await?;
// Parse want/have lines
let request = parse_upload_pack_request(&body)?;
// Generate packfile with requested objects
let packfile = generate_packfile(&repo, &request).await?;
Ok((
[(header::CONTENT_TYPE, "application/x-git-upload-pack-result")],
packfile,
))
}
async fn receive_pack(
Path((owner, repo)): Path<(String, String)>,
body: Bytes,
) -> Result<impl IntoResponse> {
let repo = get_repository(&owner, &repo).await?;
// Parse commands and packfile
let (commands, packfile) = parse_receive_pack(&body)?;
// Verify permissions
verify_push_permissions(&owner, &repo).await?;
// Apply packfile
apply_packfile(&repo, &packfile).await?;
// Update refs
for cmd in commands {
update_ref(&repo, &cmd).await?;
}
Ok((
[(header::CONTENT_TYPE, "application/x-git-receive-pack-result")],
"ok\n",
))
}
Pack File Generation
use gix::pack;
pub async fn generate_packfile(
repo: &Repository,
wants: &[ObjectId],
haves: &[ObjectId],
) -> Result<Vec<u8>> {
// Find all objects to include
let objects = repo.rev_walk(wants)
.sorting(Sorting::ByCommitTimeNewestFirst)
.ancestors()
.filter(|id| !haves.contains(id))
.collect::<Vec<_>>();
// Create pack file
let mut pack_data = Vec::new();
let mut writer = pack::data::output::bytes::Writer::new(&mut pack_data);
for oid in objects {
let object = repo.find_object(oid)?;
writer.write_entry(object)?;
}
writer.finish()?;
Ok(pack_data)
}
Reference Management
pub struct RefStore {
repo: Repository,
}
impl RefStore {
pub fn list_refs(&self) -> Result<Vec<(String, ObjectId)>> {
let refs = self.repo.references()?;
refs.all()?
.map(|r| {
let r = r?;
Ok((r.name().to_string(), r.target().id()))
})
.collect()
}
pub fn update_ref(&self, name: &str, new_id: ObjectId, old_id: Option<ObjectId>) -> Result<()> {
let ref_log_message = format!("guts: update {}", name);
if let Some(old) = old_id {
// Atomic compare-and-swap
self.repo
.reference(name, new_id, PreviousValue::MustExistAndMatch(old.into()))?;
} else {
// Create new ref
self.repo
.reference(name, new_id, PreviousValue::MustNotExist)?;
}
Ok(())
}
pub fn get_head(&self) -> Result<ObjectId> {
let head = self.repo.head_commit()?;
Ok(head.id)
}
}
Guts Extensions to Git
/// Extended commit with Guts-specific metadata
#[derive(Debug, Clone)]
pub struct GutsCommit {
/// Standard Git commit
pub git_commit: gix::Commit,
/// Ed25519 signature of commit hash
pub signature: Signature,
/// Signer's public key
pub signer: PublicKey,
/// Consensus round when commit was accepted
pub consensus_round: Option<u64>,
}
impl GutsCommit {
pub fn verify(&self) -> Result<bool> {
let commit_hash = self.git_commit.id.as_bytes();
self.signer.verify(commit_hash, &self.signature)
}
}