---
title: ref
description: HEAD と ref の中身、付けてよい名前、作るときと動かすときの条件。
---

## 中身

ref には、バージョン ID と改行を UTF-8 で書き、ほかの空白は入れません。`HEAD` には `ref: ` とブランチのパスを書きます。

```
refs/heads/main      019a3c4e-7b21-7f3a-9c55-2f0d8e1a4b6c\n
HEAD                 ref: refs/heads/main\n
```

| パス | 動くか | 用途 |
| --- | --- | --- |
| `HEAD` | めったに動かない | 既定のブランチ。ref を指定しない読み手は `HEAD` をたどる |
| `refs/heads/main` | 動く | 読み手が普段見るバージョン |
| `refs/heads/<name>` | 動く | 共有する作業中のもの |
| `refs/tags/<name>` | 動かない | 固定したいバージョン。たとえば `2025-final`。消してよい |
| `refs/requests/<number>` | ホストが決める | 承認待ちなど |

## 名前

`refs/heads/`、`refs/tags/`、`refs/requests/` のあとの名前は 128 文字までです。`/` で階層に分けてよく（たとえば `refs/heads/alice/fix-2025`）、各階層は `[a-z0-9][a-z0-9._-]*` に合わせます。名前は ASCII だけです。そうすれば、ref のパスがどの置き場でも URL でも同じになります。ほかの文字の名前は、ホストのデータベースなど別の場所に置き、ASCII の名前と対応させます。

## ref を動かす

- ref は条件付きで書かなければならない。作るときは `If-None-Match: *`、動かすときは `If-Match: <読んだ ETag>`。条件が合わなければ何も書かれず、書き手にそう伝わる
- ref が指すバージョンは、`version.json` まで書き終えていなければならない
- ブランチは既定では前へだけ動く。いまブランチが指すバージョンを `parents` に含まないバージョンは断る。後ろへ戻すのは、明示的に指示したときだけ
- タグは作るだけで、動かさない
- 断られた書き手は、チェックアウトし直して、いまあるものの上に作り直す。FDL 0.1 はマージしない。親が 2 つのバージョンを作る方法はない
- ホストは、書き手に ref を書かせず、自分で確認してから動かしてよい

条件付き書き込みのない置き場に push してはいけません。最後に書いた人が、ほかの人のバージョンを黙って置き換えてしまいます。読むのはかまいません。

## refs.json

素の HTTP の読み手は置き場の一覧を取れないので、名前を知らないブランチやタグを見つけられません。ルートの `refs.json` がそれを並べます。Git の dumb HTTP プロトコルの `info/refs` にあたります。

```json refs.json
{
  "head": "refs/heads/main",
  "refs": {
    "refs/heads/main": "019a3c4e-7b21-7f3a-9c55-2f0d8e1a4b6c",
    "refs/tags/2025-final": "019a2f10-0c4d-7e88-a1b2-55c0d9e3f7aa"
  }
}
```

- ref を動かした人、作った人、消した人が、そのあとで `HEAD` とすべての ref を読み直して `refs.json` を書き直す。ref を自分で動かすホストも、自分の ref を動かす書き手も同じ
- `refs/requests/` は入れない
- `refs.json` は名前を探すための一覧で、正ではない。ref より遅れていることがある。ref のいまのバージョンが要る読み手は、ref そのものを読む
- ref と同じく、キャッシュさせない

## 送り直した書き込み

最初の返事を受け取れずに ref の書き込みを送り直した書き手は、自分の最初の書き込みのせいで条件が合わず断られることがあります。そのとき ref が、動かそうとしていたバージョンを指していれば、動かすのは成功しています。
