Git workflow для Rust разработчиков: от первого commit до code review

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему Git workflow — это спасение для Rust-проекта (и вашей психики)

Вы когда-нибудь пытались откатить «сломанный» коммит в Rust-проекте, который случайно задевает src/main.rs, Cargo.toml и ещё три модуля? Знакомо? Если да — вы точно не одиноки. Rust славится своей строгостью, но без грамотного Git workflow даже самый идеальный код превращается в кашу.

В этой статье я покажу, как построить рабочий процесс с Git, который сэкономит вам часы дебага и нервов. Мы разберём реальные примеры на Rust, поговорим о CI/CD, code review и о том, как не потерять лицо перед коллегами. Поехали!

Основы Git workflow для Rust: с чего начать

Прежде чем нырять в детали, давайте определимся с базой. Для Rust-проектов я рекомендую использовать GitHub Flow или Git Flow — в зависимости от размера команды. Но есть одна особенность: Rust требует строгой типизации и тестов, поэтому ваш workflow должен быть заточен под cargo test и cargo clippy.

Структура веток для Rust

Вот минимальный набор веток, который я использую в своих проектах:

  • main — стабильная версия, всегда готова к релизу
  • develop — интеграционная ветка для фич
  • feature/название-фичи — каждая новая фича изолирована
  • hotfix/название — для срочных исправлений

Почему это важно для Rust? Потому что cargo check и cargo build могут выявить конфликты зависимостей, которые легче решить в изолированной ветке, чем в общей.

Пример: создаём feature-ветку для новой функции

Допустим, вы пишете парсер JSON на Rust. Вы начинаете с ветки main:

git checkout main
git pull origin main
git checkout -b feature/json-parser

Теперь вы пишете код. Вот простой пример функции, которую вы добавляете в src/parser.rs:

use serde_json::Value;

pub fn parse_json(input: &str) -> Result<Value, String> {
    serde_json::from_str(input).map_err(|e| e.to_string())
}

Перед коммитом обязательно запустите тесты:

cargo test
cargo clippy -- -D warnings

Если всё ок — делаем коммит:

git add src/parser.rs Cargo.toml
git commit -m "feat: добавить базовый парсер JSON с обработкой ошибок"

Обратите внимание на формат коммита: feat: — это часть Conventional Commits, которые отлично работают с Rust-проектами, особенно если вы используете автоматический changelog.

CI/CD для Rust: автоматическая проверка кода

Без CI ваш Git workflow — просто набор веток. Для Rust я настоятельно рекомендую добавить в корень проекта файл .github/workflows/ci.yml (если используете GitHub):

name: Rust CI

on:
  push:
    branches: [ "main", "develop" ]
  pull_request:
    branches: [ "main" ]

env:
  CARGO_TERM_COLOR: always

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - name: Build
      run: cargo build --verbose
    - name: Run tests
      run: cargo test --verbose
    - name: Lint
      run: cargo clippy -- -D warnings
    - name: Format check
      run: cargo fmt -- --check

Теперь каждый push в main или develop будет запускать сборку, тесты и линтинг. Если CI падает — коммит не мержится. Это железобетонная защита от «оно работает на моей машине».

Почему это спасёт ваш проект

Представьте: вы забыли обновить Cargo.lock после добавления зависимости. CI сразу это заметит — cargo build выдаст ошибку. Или вы случайно использовали unwrap() в продакшн-коде — clippy с флагом -D warnings заблокирует мерж.

Code review для Rust: на что обращать внимание

Code review — это не просто «посмотреть код». Для Rust есть специфические моменты, которые стоит проверять:

  • Безопасность памяти: нет ли лишних unsafe блоков?
  • Обработка ошибок: используете Result вместо паники?
  • Тесты: покрыты ли краевые случаи?
  • Документация: есть ли /// комментарии для публичных функций?

Пример плохого кода, который должен провалить review:

// Плохо: unwrap() без обработки
let data = parse_json(input).unwrap();

Хороший вариант:

// Хорошо: обработка ошибки
match parse_json(input) {
    Ok(data) => println!("Parsed: {}", data),
    Err(e) => eprintln!("Failed to parse: {}", e),
}

При мерже пулл-реквеста используйте squash merge или rebase, чтобы история оставалась чистой. Для Rust-проектов я предпочитаю rebase — так каждый коммит проходит CI.

Работа с Cargo.toml и Cargo.lock в Git

Это частая боль. Cargo.lock нужно коммитить для приложений (бинарников), но не для библиотек. В Git workflow это означает:

  • Если ваш проект — библиотека, добавьте Cargo.lock в .gitignore
  • Если приложение — всегда коммитьте Cargo.lock

Пример .gitignore для Rust-библиотеки:

/target
Cargo.lock

Для приложения:

/target

Когда вы меняете зависимости в Cargo.toml, обязательно обновляйте Cargo.lock через cargo update и коммитьте оба файла. Иначе коллеги получат разные версии зависимостей — и поехали баги.

Советы для новичков: как не наступить на грабли

Вот несколько лайфхаков, которые я вынес из своего опыта:

  • Не коммитьте бинарники: добавьте /target в .gitignore сразу при инициализации проекта
  • Используйте git hooks: например, pre-commit с cargo fmt и cargo clippy
  • Пи

Блоги

Рекомендаций по книгам