Базовый цикл: add → commit → push
Базовый цикл: add → commit → push
Ежедневная работа с Git сводится к простому циклу: изменил файлы, выбрал что включить в коммит, зафиксировал с осмысленным сообщением, отправил на remote. Этот цикл повторяется десятки раз в день, и чем лучше ты его понимаешь, тем меньше сюрпризов.
В первом уроке мы говорили о трёх зонах Git (working directory, staging area, repository). Сейчас разберём, как именно данные перемещаются между ними, и какие инструменты помогают контролировать этот процесс.
git status - твой компас
Перед любым действием смотри git status. Он показывает, в каком состоянии файлы:
git status
# On branch main
# Changes not staged for commit:
# modified: internal/handler/user.go
#
# Untracked files:
# internal/handler/auth.go
Файлы бывают в четырёх состояниях:
- Untracked - Git не знает об этом файле (новый файл, не добавлен через
git add) - Modified - файл изменён, но изменения не в staging area
- Staged - файл добавлен в staging area, готов к коммиту
- Committed - файл зафиксирован в репозитории, совпадает с последним коммитом
# Краткий формат (удобнее для повседневной работы)
git status -s
# M internal/handler/user.go # modified, staged
# M internal/service/auth.go # modified, not staged
# ?? internal/handler/auth.go # untracked
# A internal/model/token.go # new file, staged
git diff - что именно изменилось
git status показывает какие файлы изменились, а git diff показывает что именно изменилось:
# Разница между working directory и staging area
# (что изменилось, но ещё не добавлено в staging)
git diff
# Разница между staging area и последним коммитом
# (что попадёт в следующий коммит)
git diff --staged
# Разница между working directory и последним коммитом
git diff HEAD
# Разница для конкретного файла
git diff internal/handler/user.go
# Только имена изменённых файлов
git diff --name-only
git add - выбираем что коммитить
git add перемещает изменения из working directory в staging area:
# Добавить конкретный файл
git add internal/handler/user.go
# Добавить всю директорию
git add internal/handler/
# Добавить все изменённые и новые файлы
git add .
# Добавить все отслеживаемые файлы (без untracked)
git add -u
Частичное добавление с git add -p
Самая мощная опция - интерактивное добавление по частям. Допустим, ты изменил файл в двух местах, но хочешь разбить изменения на два логических коммита:
git add -p internal/handler/user.go
Git покажет каждый изменённый фрагмент (hunk) и спросит, что делать:
@@ -15,6 +15,8 @@ func (h *UserHandler) GetProfile(w http.ResponseWriter, r *http.Request) {
+ // validate token
+ token := r.Header.Get("Authorization")
Stage this hunk [y,n,q,a,d,s,e,?]?
y- добавить этот фрагмент в stagingn- пропуститьs- разделить на более мелкие фрагментыq- выйти
Это позволяет делать атомарные коммиты: один коммит - одно логическое изменение.
git commit - фиксируем снимок
# Коммит с однострочным сообщением
git commit -m "feat: add token validation to GetProfile"
# Открыть редактор для многострочного сообщения
git commit
# Добавить все отслеживаемые файлы и сразу закоммитить (без git add)
git commit -am "fix: handle nil pointer in auth middleware"
Conventional Commits
В командной работе принято придерживаться формата сообщений. Один из самых популярных - Conventional Commits:
<тип>(<область>): <описание>
<тело - опционально>
<футер - опционально>
Типы:
feat- новая функциональностьfix- исправление багаrefactor- рефакторинг без изменения поведенияdocs- документацияtest- тестыchore- инфраструктура, зависимости, CI
Единый формат сообщений нужен не только для красоты: из него потом генерируется changelog между тегами релизов.
Примеры:
git commit -m "feat(auth): add JWT refresh token endpoint"
git commit -m "fix(handler): return 404 instead of 500 for missing user"
git commit -m "refactor(service): extract validation into separate method"
git commit -m "test(progress): add unit tests for lesson completion"
git commit --amend
Если ты только что сделал коммит и понял, что забыл добавить файл или допустил опечатку в сообщении:
# Добавить забытый файл в последний коммит
git add internal/handler/auth.go
git commit --amend --no-edit
# Изменить сообщение последнего коммита
git commit --amend -m "feat(auth): add JWT validation middleware"
git push - отправляем на remote
# Первый push ветки (--set-upstream связывает локальную ветку с remote)
git push -u origin main
# Последующие push-и (upstream уже установлен)
git push
git pull vs git fetch
Обе команды получают изменения с remote, но работают по-разному:
git fetch - скачивает новые коммиты с remote, но не трогает твои локальные файлы. Безопасная операция - ты можешь посмотреть, что изменилось, перед тем как применять.
git fetch origin
# Посмотреть, что пришло
git log main..origin/main --oneline
# Применить, когда готов
git merge origin/main
git pull - это git fetch + git merge (или git rebase, если настроен pull.rebase). Удобно, но может неожиданно создать merge-коммит.
# Обычный pull (fetch + merge)
git pull
# Pull с rebase (линейная история)
git pull --rebase
.gitignore и .gitkeep
.gitignore
Файл .gitignore в корне репозитория указывает, какие файлы Git должен игнорировать (полный разбор синтаксиса, глобального gitignore и защиты от утечки секретов - в отдельном уроке):
# Скомпилированные бинарники Go
/bin/
*.exe
# Зависимости
/vendor/
# IDE
.idea/
.vscode/
# Переменные окружения
.env
.env.local
# Системные файлы
.DS_Store
Thumbs.db
# Проверить, игнорируется ли файл
git check-ignore -v .env
# .gitignore:12:.env .env
.gitkeep
Git не отслеживает пустые директории. Если тебе нужна пустая папка в репозитории (например, logs/ или uploads/), положи в неё файл .gitkeep:
mkdir -p uploads
touch uploads/.gitkeep
git add uploads/.gitkeep
git commit -m "chore: add empty uploads directory"
.gitkeep - это конвенция, а не фича Git. Файл может называться как угодно, но .gitkeep - общепринятое название.
Remote tracking branches
Когда ты делаешь git push -u origin main, Git создаёт связь между локальной веткой main и удалённой origin/main. Это remote tracking branch. То же самое работает и для feature-веток, с которыми ты будешь работать каждый день.
# Посмотреть связи
git branch -vv
# * main 3a7f1b2 [origin/main] feat: add auth handler
# develop 1c2d3e4 [origin/develop: ahead 2] refactor: extract service
# «ahead 2» значит: у тебя есть 2 локальных коммита, которых нет на remote
# «behind 3» значит: на remote есть 3 коммита, которых нет у тебя
Мини-задание
- Создай репозиторий с двумя файлами, измени оба, но закоммить только один с помощью
git add <файл> - Попробуй
git add -p- добавь часть изменений из одного файла - Сделай коммит с сообщением в формате Conventional Commits
- Используй
git commit --amendчтобы исправить сообщение последнего коммита - Настрой
.gitignoreдля Go-проекта (бинарники, .env, .idea/)