Мой велосипед = Git: как 17-летний решил сделать свой сервер Git с нуля
Всем привет! Меня звать Казильский, мне 17, и я действительно люблю git.
Но иногда хочется… Что‑то эдакое. Захотелось свой маленький «всеподконтрольный» велосипед — чтобы и как на GitHub, и чтобы всё можно было сделать по‑своему, и чтобы понять все внутренности.
Зачем вообще писать свой Git HTTP сервер?
Потому что могу и хочется! Хочу сам решать, что там должно быть, как и когда пушить, какие фичи включать, где ставить костыли и где делать красиво.
Полнейший контроль: Люблю git, но хочу свой сервер без лишней магии и чтобы можно было учиться на нем, ломать и снова чинить.
Интересно разобраться как работает git на самом “низком уровне”. Оказывается, там всё не так просто =(
Как реально работает git clone/pull/push по HTTP? (человеческим языком, с примерами и болью)
Представьте: вы пишете
git clone http://localhost:8000/git/test-repoИ думаете, что просто качаете файлы.
На самом деле под капотом происходит целая “переписка” между git-клиентом и сервером.
1. Запрос инфы — info/refs
Клиент:
Хочу узнать, какие ветки и коммиты есть!
Шлёт:
GET /git/test-repo/info/refs?service=git-upload-pack(именно такой URL, не просто /git/test-repo)
Сервер:
Отвечаю тебе специальным бинарным форматом (это не JSON, не XML, а магия git).
Пишу в ответ:
Заголовок:
Content-Type: application/x-git-upload-pack-advertisementТело — список веток, capabilities, хэши, всё как любит git. (Иначе будете как и я мучатся дня 4 чисто перебирая формат тела)
2. Клиент хочет получить объекты — git-upload-pack
Клиент:
Дай мне вот эти коммиты, вот эти объекты!
POST-запрос:
POST /git/test-repo/git-upload-packВ теле — бинарщина от git (железно не человекочитаемо, для меня как минимум).
Как сервер это обрабатывает?
Запускаю, как есть:
git upload-pack --stateless-rpc ./repositories/test-repo.gitВесь бинарный запрос, который пришёл от клиента, я в Rust просто передаю в stdin этого процесса.
Всё, что git выдаёт в stdout, я отправляю обратно клиенту.
Rust-код примерно такой:
let mut child = Command::new("git") .args(["upload-pack", "--stateless-rpc", repo_path]) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn()?; child.stdin.write_all(&body)?; let mut response = Vec::new(); child.stdout.read_to_end(&mut response)?;Получается, что мой сервер — это «мостик», который просто прокидывает байтики туда‑сюда, но и контролирует, кто когда может это делать.
3. Push: как отправить изменения на сервер
Тут всё похоже, только протокол другой.
Клиент:
Хочу узнать, что я могу пушить.
GET /git/test-repo/info/refs?service=git-receive-packСервер:
Отвечаю “рекламой” своих возможностей для push.
Клиент:
Держи мои объекты и обновления веток!
POST /git/test-repo/git-receive-packВсё опять в бинарном виде.
Сервер:
Запускаю:
git receive-pack --stateless-rpc ./repositories/test-repo.gitВсё тело запроса — в stdin, всё, что выдаёт git — назад клиенту.
4. Где тут моя “фишка” и что мне НЕ нравится
Я добавил ограничение по времени: пушить можно только с 10:00 до 12:00 по UTC. Фича? Да, просто захотелось так! (вообще я просто тестил)
Если где-то ошибся, клиент просто пишет “fatal: protocol error”, и ищи потом, где напортачил.
Дебажить тяжело, потому что бинарные форматы, логи не человекочитаемые (опять же, для меня), и если что — git просто молчит.
Документация по протоколу — квест, половину пришлось вытаскивать по исходникам git.
Но зато теперь я понимаю, КАК реально работает git на сервере, и почему там всё так сложно (хотя я сомневаюсь что смогу заспидранить перепись сервера с нуля).
Итог
Мне 17, я люблю git, и теперь у меня есть свой маленький сервер, который реально работает с обычным git‑клиентом.
Захотелось сделать «велосипед» — сделал! Управляю всем сам, учусь на ошибках и кайфую от процесса.
Если вам интересно, как это работает, или хотите заценить код, или просто поугарать над чужой болью — милости прошу: Kazilsky/Git‑HTTP‑Server
Если будет хоть немного фидбека, мемов или багрепортов — обещаю развивать сервер дальше, добавить новые фичи и не бросать этот движ.
P.S. Не бойтесь делать свои велосипеды. Даже если вам 17, даже если у вас уже есть git. Это реально весело!
P.S.S. Добавлю что это делал я, критика принимается но в рамках разумного, если мой мусорокод вас не устроил, ограничтесь конструктивной критикой! =)