Всем привет! Меня звать Казильский, мне 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. Добавлю что это делал я, критика принимается но в рамках разумного, если мой мусорокод вас не устроил, ограничтесь конструктивной критикой! =)