0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

git-aiでコードの作成者が人間かAIかを見える化する

0
Posted at

概要

git-aiはAIによるコード生成をフックし、対象行をGitのNotesへ記録することで、AIコーディング率を導出する仕組み。

既存のコードについて、AIが生成したものかどうかを推測で判定するのではなく、AIがコードを生成したときに「この差分はAIが書いた」と明示的に記録する。

gitの拡張なので、言語にとらわれず幅広いプロジェクトに導入できる。

Claude Code、Codex、Cursor、GitHub Copilot、GeminiなどメジャーなAIには対応している。

公式

仕組み

大まかな流れ。

  1. Cursor、Claude Code、GitHub CopilotなどのAIエージェントがファイルを編集する
  2. git-aiの checkpoint が呼ばれる
  3. 編集前後の差分を見て、AI由来・人間由来を記録する
  4. コミット時に、その情報を Authorship Log としてまとめる
  5. そのログを Git Notes に保存する
  6. git ai statsgit ai blame で割合や行単位の由来を見る

導入

Next.jsでプロジェクトを立ち上げ、これにgit-aiを導入してみる。

npx create-next-app@latest learn-git-ai --yes

cd learn-git-ai

Macに導入するな以下のコマンド。

curl -sSL https://usegitai.com/install.sh | bash

以下のコマンドを実行し、versionが表示されたらOK。

git ai --version

自分はパスが通っておらず、バージョンが表示されなかったので、追加で以下を実行したところ、バージョンが表示されるようになった。

echo 'export PATH="$HOME/.git-ai/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc

また、拡張機能のgit-aiが入っていない場合は手動でインストールする。

image.png

試してみる

差分確認

src/app/page.tsxについて、適当にgithub copilotに変更してもらう。

// これ以降全てgithub copilotが書いたコード

const menuItems = [
	{ label: "ホーム", href: "#" },
	{ label: "プロフィール", href: "#" },
	{ label: "設定", href: "#" },
	{ label: "ヘルプ", href: "#" },
	{ label: "ログアウト", href: "#" },
];

export default function Home() {
	return (
		<div className="min-h-screen bg-zinc-50 dark:bg-zinc-900 flex items-center justify-center font-sans">
			<div className="w-full max-w-sm bg-white dark:bg-zinc-800 rounded-2xl shadow-lg overflow-hidden">
				<div className="px-6 py-8 border-b border-zinc-100 dark:border-zinc-700">
					<h1 className="text-2xl font-bold text-zinc-800 dark:text-zinc-100">
						メニュー
					</h1>
					<p className="text-sm text-zinc-500 dark:text-zinc-400 mt-1">
						項目を選択してください
					</p>
				</div>
				<nav>
					<ul>
						{menuItems.map((item, index) => (
							<li key={index}>
								<a
									href={item.href}
									className="flex items-center justify-between px-6 py-4 text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors border-b border-zinc-100 dark:border-zinc-700 last:border-none"
								>
									<span className="font-medium">{item.label}</span>
									<span className="text-zinc-400"></span>
								</a>
							</li>
						))}
					</ul>
				</nav>
			</div>
		</div>
	);
}

ターミナルで以下を実行する。

git ai status

結果は以下の通り。

you  ········░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ai
     0%           untracked  21%           79%

52 secs ago      +32     -6  Github-copilot gpt-5-mini
52 secs ago       +5    -61  username

このように、どれくらいのコードをAIが実装したのかがわかる。

コミット後にAI生成割合を確認する

コミットをした後にgit ai statusを実行しても、先ほどのようなAI生成割合等を確認することはできない。

$ git ai status
No checkpoints recorded since last commit (80d6f22)

If you've made AI edits recently and don't see them here, you might need to install hooks:

  git-ai install-hooks

確認するには以下のコマンドを使う。

git ai stats

結果

you  ··░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ai
     0%           untracked   5%           95%

これは、

  • 人間が書いたコード:0%
  • AIが書いたコード:95%
  • Git未追跡ファイル:5%

ということを表している。

注意すべき点は、この割合はプロジェクト全体についての説明ではないということ。git ai statsでは、直近のコミット(HEAD)のみが対象になっている。

複数のコミットや、ブランチ全体をみるには範囲の指定をする必要がある。

範囲の指定方法は以下の通り。

git ai stats <start>..<end>

startとendにはそれぞれcommitのIDを記入する。

現在のブランチの最初のコミットから最新のコミットまでを対象にしたい場合、以下のように書く。

git ai stats 4b825dc642cb6eb9a060e54bf8d69288fbee4904..HEAD

4b825dc642cb6eb9a060e54bf8d69288fbee4904は空のツリーを表す。

つまり上記のコマンドは、空の状態〜HEADまでを対象とする、ということ。

現在のブランチでmainブランチから増えた分を対象にしたいなら、以下のように書くこともできる。

git ai stats main..HEAD

ファイル単位で確認

以下のコマンドを実行することで、gitのblameでもAIが生成したコードかどうかを確認することができる。

git ai blame <ファイルパス>

src/app/page.tsxについて確認したい場合は、以下のコマンドを実行する。

git ai blame src/app/page.tsx

結果

80d6f224 (username         2026-05-28 19:55:32 +0900  1) // これ以降全てgithub copilotが書いたコード
80d6f224 (username         2026-05-28 19:55:32 +0900  2) 
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900  3) const menuItems = [
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900  4)  { label: "ホーム", href: "#" },
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900  5)  { label: "プロフィール", href: "#" },
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900  6)  { label: "設定", href: "#" },
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900  7)  { label: "ヘルプ", href: "#" },
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900  8)  { label: "ログアウト", href: "#" },
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900  9) ];
^135f51b (username         2026-05-28 15:47:52 +0900 10) 
^135f51b (username         2026-05-28 15:47:52 +0900 11) export default function Home() {
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 12)  return (
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 13)          <div className="min-h-screen bg-zinc-50 dark:bg-zinc-900 flex items-center justify-center font-sans">
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 14)                  <div className="w-full max-w-sm bg-white dark:bg-zinc-800 rounded-2xl shadow-lg overflow-hidden">
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 15)                          <div className="px-6 py-8 border-b border-zinc-100 dark:border-zinc-700">
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 16)                                  <h1 className="text-2xl font-bold text-zinc-800 dark:text-zinc-100">
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 17)                                          メニュー
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 18)                                  </h1>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 19)                                  <p className="text-sm text-zinc-500 dark:text-zinc-400 mt-1">
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 20)                                          項目を選択してください
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 21)                                  </p>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 22)                          </div>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 23)                          <nav>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 24)                                  <ul>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 25)                                          {menuItems.map((item, index) => (
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 26)                                                  <li key={index}>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 27)                                                          <a
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 28)                                                                  href={item.href}
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 29)                                                                  className="flex items-center justify-between px-6 py-4 text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-zinc-700 transition-colors border-b border-zinc-100 dark:border-zinc-700 last:border-none"
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 30)                                                          >
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 31)                                                                  <span className="font-medium">{item.label}</span>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 32)                                                                  <span className="text-zinc-400">›</span>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 33)                                                          </a>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 34)                                                  </li>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 35)                                          ))}
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 36)                                  </ul>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 37)                          </nav>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 38)                  </div>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 39)          </div>
80d6f224 (github-copilot 2026-05-28 19:55:32 +0900 40)  );
^135f51b (username       2026-05-28 15:47:52 +0900 41) }

config設定

生成物やテスト出力については対象外にすることができる。

.git-ai-ignoreファイルをルートに作成し、以下のように記述する。

.next/**
out/**
coverage/**
playwright-report/**
test-results/**
*.generated.*
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?