В папке Документы\Arduino\polivalka у меня жили пять файлов: polivalka.ino, polivalka_2.ino, polivalka_final.ino, polivalka_final_okt.ino и polivalka_TOCHNO.ino. Однажды поливалка на балконе перестала открывать кран, и мне понадобилась версия, которая работала в июле. Какая из пяти — не знал ни я, ни флешка с «копией от пятницы»: она хранила то, что было в пятницу, но не то, что было в июле, и уж точно не то, что я сломал во вторник. В тот день я поставил git — двадцать минут освоения, и папка с копиями больше не понадобилась.
Git — не «для программистов с корпоративными серверами». Это программа, которая делает снимки папки по вашей команде и умеет показывать, что изменилось между снимками. Ниже: четыре команды, которых хватит навсегда, правило имени папки, от которого зависит, откроется ли клонированный проект в IDE, и .gitignore, спасающий от мусора и утечки паролей.
Отдельно про «у меня и так всё сохраняется». Облачный диск синхронизирует текущее состояние: испортили файл вечером — утром испорченный файл на всех машинах. Флешка хранит то, что вы вспомнили скопировать. Кнопки «восстановить предыдущую версию» в облаках существуют, но ищут по датам без подписей и без сравнения строк. Git отличается именно историей с комментариями: каждое состояние подписано вашими словами, и найти «версию с рабочим насосом» — поиск по логу, а не археология по датам файлов.
Git в четырёх командах
git init в папке скетча — «начни следить за этой папкой». git add . — «вот файлы для снимка». git commit -m «починил debounce кнопки» — «сделай снимок с подписью». git log — «покажи историю». Каждый коммит — фотография всего проекта на момент, когда он работал. Вернуться к любому снимку — git checkout, сравнить два состояния построчно — git diff. Никакие копии так не умеют: они хранят момент, но не умеют ни сравнивать, ни возвращаться, ни отвечать на вопрос «что я менял в августе».
Второй этаж — GitHub: git push заливает историю в облако, приватный репозиторий никто не видит. Ноутбук встретил чай — код и вся история живы на сервере. Кто не дружит с консолью: GitHub Desktop делает те же add, commit и push кнопками, суть та же. А вот Arduino Web Editor бэкапом считать не стоит: скетчи там живут в чужом облаке, без истории версий и без связи с локальной папкой проекта.
Первый коммит делайте ещё до первой правки: git add . на только что сохранённом проекте — и у вас есть эталон, с которым сравнивается всё дальнейшее. Дальше — по коммиту на осмысленный шаг: «добавил датчик», «перевёл на millis», «чиню debounce». Подписи пишите для себя будущего: через полгода лог с сообщениями «правки» и «исправления» бесполезен, а «перевёл опрос датчика на millis, delay убрал» экономит вечер. Двадцать-сорок коммитов за сезон — нормальный темп хобби-проекта, репозиторий при этом весит меньше сотни килобайт.
Одна деталь, на которой спотыкаются все: IDE открывает скетч только из папки с тем же именем, что у .ino файла — polivalka.ino обязана лежать в polivalka/, мы разбирали это в уроке про структуру скетчбука. Git clone создаёт папку с именем репозитория, значит репозиторий должен называться polivalka: тогда клонированный проект открывается сразу, без переименований. Той же логикой репозиторий — на папку проекта, а не на весь скетчбук: в Документах\Arduino живут libraries, служебные папки и двадцать чужих проектов, история такого монолита превратится в кашу. Один проект — один репозиторий, и правило имени соблюдается само. Заодно решается вопрос двух машин: закоммитил дома, вытянул на ноутбуке, и правки не расходятся — истина одна, в репозитории.
# артефакты сборки — пересоздаются из кода.pio/build/Debug/ # резервные копии — их работа теперь у коммитов*.bak # секреты — никогда не попадают в историюsecrets.h # а шаблон остаётся видимым для соседей по проекту!secrets.h.exampleСборочным артефактам в git делать нечего: папку build из экспорта бинарников, .pio у PlatformIO-проектов, Debug от отладчика любой коммит пересоздаёт заново — хранить их всё равно что хранить выжимки апельсина рядом с апельсинами. Файл secrets.h с паролем Wi-Fi не коммитим даже в приватный репозиторий: привычка важнее текущей приватности, репозиторий однажды уедет публичным. Вместо секрета хранится шаблон secrets.h.example с пустыми строками — скачавший проект видит, какие переменные завести. Для версий библиотек в чистом IDE-проекте я держу в корне LIBRARIES.txt с названиями и номерами версий: примитивно, зато новая машина собирается за вечер.
Глобальную папку libraries из скетчбука не коммитят — чужие сотни мегабайт в вашей истории никому не нужны, мы и ставили их кликами в менеджере библиотек без всякого git. Если проект на PlatformIO — версии и так зафиксированы в ini, это его суперспособность. О том, как живут библиотеки вне менеджера и почему zip-архив бывает спасением, я писал в уроке про zip-библиотеку — при восстановлениях на новой машине это знание пригождается чаще, чем кажется.
Про пароль в коде отдельная ремарка: даже если секрет уже засветился в истории, репозиторий лечится — историю можно переписать фильтром и сменить пароль. Но проще с первого дня держать секреты в отдельном файле, который git игнорирует: привычка стоит пяти минут, а спасает от пересоздания репозитория с нуля. Для проектов с Wi-Fi это не паранойя: платы с беспроводным модулем почти всегда носят ключ в коде, и в чужих открытых репозиториях он лежит ровно в открытом виде.
Что коммитим, а что игнорим
| Файл или папка | В git? | Почему |
|---|---|---|
| sketch.ino, .cpp, .h | да | это код — вся история должна быть про него |
| platformio.ini, LIBRARIES.txt | да | версии библиотек и плат — часть проекта |
| build/, .pio/, Debug/ | нет | артефакты сборки пересоздаются из кода |
| *.bak и копии «финал_2» | нет | их работу теперь делает история коммитов |
| secrets.h с паролями | никогда | утечка репозитория не должна стоить аккаунта |
| secrets.h.example | да | шаблон: видно, какие переменные завести |
Правило, после которого git перестал быть чуждым: заставил работать — коммит. Настроил автомат полива — коммит с подписью, добавил датчик — коммит. Через полгода, когда поливалка ведёт себя странно, git diff между рабочим коммитом и текущим показывает ровно изменившиеся строки: половина «странных глюков» оказывается недавней правкой, и читать логи компиляции приходится в разы реже. Версии, которые уезжают жить в устройство, помечаю тегом: git tag v1.0 — и git checkout v1.0 возвращает код, который стоит на балконе прямо сейчас. Рискованные эксперименты делаю в отдельной ветке: попробовал, не пошло — вернулся в main, рабочая версия не пострадала. Один нюанс: редакторы оставляют временные файлы и резервные копии — не добавляйте их вручную «на всякий случай», один раз настройте .gitignore под свою систему и забудьте. И помните: настройки, сохранённые в EEPROM на плате, бэкапом кода не являются — чип перепрошивается, а код без истории не возвращается.
- 1. Поливалка заработала как надо. Что делаете дальше по правилу урока?
- 2. Что обязательно попадает в .gitignore ардуинного проекта?
- 3. Почему репозиторий делают на папку проекта, а не на весь скетчбук?
- 4. Что окажется под присмотром git после git add . в папке скетча?
- 5. Как зафиксировать версию прошивки, которая уехала жить в поливалку на балкон?
- git init в папке проекта, add и commit после каждого работающего состояния — история заменяет polivalka_final_2.ino навсегда
- Имя папки = имя .ino: репозиторий называйте как скетч, иначе клонированная копия не откроется в IDE
- .gitignore: .pio и build, *.bak, secrets.h; шаблон secrets.h.example храните, сам секрет — никогда
- Репозиторий на папку проекта, не на весь скетчбук; бэкап git-проекта — git push, а не копия папки с .git
- Тег v1.0 на коммите работающей версии: git checkout вернёт код, который стоит в устройстве прямо сейчас
Частые вопросы по теме урока
- Поливалка заработала как надо. Что делаете дальше по правилу урока?
- Рабочее состояние — лучшая точка снимка: коммит запоминает точный код, который лежит в устройстве. Когда через месяц что-то сломается, будет с чем сравнивать через git diff. Делаете коммит с подписью о том, что именно заработало
- Что обязательно попадает в .gitignore ардуинного проекта?
- Артефакты сборки пересоздаются из кода, хранить их в истории незачем — они раздувают репозиторий. А шаблон secrets.h.example наоборот нужен: без него сосед по проекту не знает, какие переменные заводить. Папки сборки: .pio у PlatformIO, build у экспорта бинарников
- Почему репозиторий делают на папку проекта, а не на весь скетчбук?
- Документы\Arduino — общее место для всех проектов. Каждому проекту — свой репозиторий со своей историей; правило «папка = имя скетча» при этом соблюдается автоматически. В скетчбуке живут libraries и служебные папки — история станет кашей
- Что окажется под присмотром git после git add . в папке скетча?
- Git хранит файлы целиком: .ino, .cpp, .h, ini-конфиги, readme. Исключения перечисляете вы в .gitignore — сборки, резервные копии и секреты. Все текстовые файлы проекта, кроме перечисленных в .gitignore
- Как зафиксировать версию прошивки, которая уехала жить в поливалку на балкон?
- Тег — постоянная метка на снимке: git checkout v1.0 вернёт ровно тот код, что работает в устройстве. Флешки, маркеры и скриншоты с историей не связаны и теряются первыми. Поставить тег v1.0 на коммит