Ты прошёл уроки 0-11. По отдельности всё понятно: коммит, индекс, ветка, удалёнка. Но в голове это лежит как куча запчастей, а не как собранный мотоцикл. Самая частая боль новичка звучит так: "вроде каждую команду знаю, а сесть и сделать реальный проект от пустой папки до push на GitHub не могу - теряюсь, в каком порядке что нажимать". Этот урок ровно про это. Никакой новой теории. Только руки. Мы пройдём один сквозной мини-проект end-to-end: создадим папку, проинициализируем репозиторий, настроим себя, наделаем осмысленных коммитов, заведём ветку фичи, сольём её обратно, выложим всё на GitHub и подтянем правку, сделанную через веб. Это точка сборки между фундаментом и продвинутыми темами - после неё git с нуля перестанет быть страшным.
Держи в голове одну модель из прошлых уроков, она склеивает всё остальное. У Git три состояния файла: рабочая директория (где ты редактируешь), индекс/staging (что попадёт в следующий коммит) и история (зафиксированные коммиты). Команда
Код: Выделить всё
git statusКод: Выделить всё
git log --oneline --graph
Шаг 1-3: git init, identity и первый коммит README
Создаём папку проекта и инициализируем репозиторий. Это база любого туториала git и любого первого проекта git.
Код: Выделить всё
mkdir todo-cli && cd todo-cli
git init -b main
Код: Выделить всё
-b mainКод: Выделить всё
git initКод: Выделить всё
-b mainКод: Выделить всё
git config --global init.defaultBranch mainКод: Выделить всё
$ git status
On branch main
No commits yet
nothing to commit (create README to get started)
Код: Выделить всё
.gitТеперь identity. Git подписывает каждый коммит автором, и без настройки он либо угадает кривое имя из системы, либо откажется коммитить. Настраиваем один раз глобально (для всех репозиториев):
Код: Выделить всё
git config --global user.name "Ivan Petrov"
git config --global user.email "ivan@example.com"
Код: Выделить всё
$ printf '# todo-cli\n\nПростой список дел в терминале.\n' > README.md
$ git add README.md
$ git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README.md
$ git commit -m "Первый коммит: README с описанием проекта"
[main (root-commit) a1b2c3d] Первый коммит: README с описанием проекта
1 file changed, 1 insertion(+)
create mode 100644 README.md
Код: Выделить всё
git addКод: Выделить всё
git commitКод: Выделить всё
a1b2c3dШаг 4-6: .gitignore и осмысленные коммиты через git add -p
Прежде чем плодить файлы - .gitignore. Это список шаблонов, которые Git будет игнорировать: мусор сборки, секреты, локальные настройки. Правило простое: лучше завести его сразу, чем потом вычищать случайно закоммиченный пароль.
Код: Выделить всё
$ printf 'node_modules/\n*.log\n.env\n.DS_Store\n' > .gitignore
$ git add .gitignore
$ git commit -m "Добавил .gitignore для мусора и секретов"
Код: Выделить всё
$ touch debug.log
$ git status
On branch main
nothing to commit, working tree clean
Код: Выделить всё
*.logТеперь напишем код и познакомимся с приёмом, который отличает аккуратного человека от того, кто валит всё в один коммит
Код: Выделить всё
git add . && git commit -m "stuff"Код: Выделить всё
$ cat > todo.py <<'EOF'
def add_task(tasks, text):
tasks.append({"text": text, "done": False})
def list_tasks(tasks):
for i, t in enumerate(tasks):
mark = "x" if t["done"] else " "
print(f"[{mark}] {i}: {t['text']}")
EOF
Код: Выделить всё
git add -pКод: Выделить всё
$ git add -p todo.py
@@ -0,0 +1,8 @@
+def add_task(tasks, text):
+ tasks.append({"text": text, "done": False})
+
+def list_tasks(tasks):
...
(1/1) Stage this hunk [y,n,q,a,d,s,e,?]?
Код: Выделить всё
$ git commit -m "Реализовал add_task: добавление задачи в список"
$ git add todo.py
$ git commit -m "Реализовал list_tasks: вывод задач с отметкой"
$ git log --oneline --graph
* e5f6a7b (HEAD -> main) Реализовал list_tasks: вывод задач с отметкой
* d4e5f6a Реализовал add_task: добавление задачи в список
* c3d4e5f Добавил .gitignore для мусора и секретов
* a1b2c3d Первый коммит: README с описанием проекта
Код: Выделить всё
HEAD -> mainШаг 7-9: ветка фичи через git switch -c и слияние git merge
Новую функциональность делают в отдельной ветке, чтобы main всегда оставался рабочим. Это сердце того, как работать с git в команде. Создаём ветку и сразу переключаемся на неё одной командой:
Код: Выделить всё
$ git switch -c feature/done
Switched to a new branch 'feature/done'
Код: Выделить всё
git switchКод: Выделить всё
git restoreКод: Выделить всё
git checkout -b feature/doneКод: Выделить всё
-cДобавим функцию отметки задачи выполненной и закоммитим:
Код: Выделить всё
$ cat >> todo.py <<'EOF'
def mark_done(tasks, index):
tasks[index]["done"] = True
EOF
$ git add todo.py
$ git commit -m "Добавил mark_done: отметка задачи выполненной"
$ git log --oneline --graph --all
* 1a2b3c4 (HEAD -> feature/done) Добавил mark_done: отметка задачи выполненной
* e5f6a7b (main) Реализовал list_tasks: вывод задач с отметкой
* d4e5f6a Реализовал add_task: добавление задачи в список
...
Код: Выделить всё
--allКод: Выделить всё
$ git switch main
Switched to branch 'main'
$ git merge feature/done
Updating e5f6a7b..1a2b3c4
Fast-forward
todo.py | 3 +++
1 file changed, 3 insertions(+)
Код: Выделить всё
$ git branch -d feature/done
Deleted branch feature/done (was 1a2b3c4).
Локально всё готово - выкладываем на форж. Это финальная цель: git github пример end-to-end. На github.com создай новый репозиторий с именем todo-cli, без галок "Add README" и "Add .gitignore" - они создадут лишние коммиты, и push упрётся в конфликт историй. Нужен пустой репозиторий. GitHub покажет URL; берём HTTPS-вариант. Привязываем удалёнку и пушим:
Код: Выделить всё
$ git remote add origin https://github.com/ivanpetrov/todo-cli.git
$ git remote -v
origin https://github.com/ivanpetrov/todo-cli.git (fetch)
origin https://github.com/ivanpetrov/todo-cli.git (push)
$ git push -u origin main
Enumerating objects: 12, done.
...
To https://github.com/ivanpetrov/todo-cli.git
* [new branch] main -> main
branch 'main' set up to track 'origin/main'.
Код: Выделить всё
originКод: Выделить всё
-uКод: Выделить всё
--set-upstreamКод: Выделить всё
origin/mainКод: Выделить всё
git pushКод: Выделить всё
git pullКод: Выделить всё
-uКод: Выделить всё
git config --global push.autoSetupRemote trueКод: Выделить всё
$ git log --oneline --graph
* 1a2b3c4 (HEAD -> main, origin/main) Добавил mark_done: отметка задачи выполненной
...
Код: Выделить всё
origin/mainТеперь обратное направление - правка через веб и
Код: Выделить всё
git pullКод: Выделить всё
$ git pull
remote: Enumerating objects: 5, done.
...
Updating 1a2b3c4..9f8e7d6
Fast-forward
README.md | 2 ++
1 file changed, 2 insertions(+)
Код: Выделить всё
git pullКод: Выделить всё
git fetchКод: Выделить всё
git mergeТипичные грабли первого дня
- Забыл настроить user.email или указал не тот. Коммиты на GitHub не приклеятся к профилю, будут висеть "ничьи". Лечится: поставь правильный email и для будущих коммитов это сработает само; старые при необходимости переписывают отдельно.
- Создал репозиторий на GitHub с README. Тогда упрётся в "Updates were rejected" - на сервере уже есть коммит, которого нет у тебя. Для самого первого раза проще пересоздать пустым; общий путь - сделать
Код: Выделить всё
git pushи потом push.Код: Выделить всё
git pull --rebase origin main - Свалил всё в до .gitignore.[/b] В индекс уедут node_modules, .env, .DS_Store. Убрать из индекса, не трогая файл на диске:
Код: Выделить всё
git add ., потом дописать .gitignore и закоммитить.Код: Выделить всё
git rm --cached -r node_modules - Боишься switch/checkout с незакоммиченными правками. Если правки конфликтуют с целевой веткой, Git сам откажется переключаться и предупредит - данные он не теряет. Перед сменой ветки доводи дерево до "working tree clean" или прячь в stash.
- Запутался master или main. Если без
Код: Выделить всё
git initдал тебе master, а на GitHub ждёшь main - переименуй локально:Код: Выделить всё
-b, и пушьКод: Выделить всё
git branch -m main.Код: Выделить всё
git push -u origin main
Сделай это, не подглядывая в текст выше - именно повтор без подсказки закрепляет.
- Создай папку и
Код: Выделить всё
first-git. ПроверьКод: Выделить всё
git init -b main- убедись, что видишь "No commits yet".Код: Выделить всё
git status - Настрой user.name и user.email (если ещё не глобально).
- Сделай README.md, ,
Код: Выделить всё
git add. ГляньКод: Выделить всё
git commit- один узел.Код: Выделить всё
git log --oneline --graph - Заведи .gitignore с , закоммить. Создай
Код: Выделить всё
*.logи убедись по status, что он невидим.Код: Выделить всё
test.log - Создай файл с двумя функциями, разнеси их в два коммита через (попробуй s и e).
Код: Выделить всё
git add -p - , добавь третью функцию, закоммить. Посмотри
Код: Выделить всё
git switch -c feature/x- где разошлись ветки.Код: Выделить всё
git log --graph --all - Вернись ,
Код: Выделить всё
git switch main- поймай слово Fast-forward. Удали веткуКод: Выделить всё
git merge feature/x.Код: Выделить всё
-d - Заведи пустой репозиторий на GitHub, ,
Код: Выделить всё
git remote add origin URL.Код: Выделить всё
git push -u origin main - Поправь README через веб, сделай . Сверь
Код: Выделить всё
git pull- серверный коммит на месте.Код: Выделить всё
git log
- Чем отличается от
Код: Выделить всё
git addв терминах трёх деревьев - что куда перемещается?Код: Выделить всё
git commit - Почему не копирует коммиты, и что на самом деле создаёт новую ветку?
Код: Выделить всё
git switch -c feature - Что значит Fast-forward при merge и при каком условии вместо него появился бы merge-коммит с двумя родителями?
- Что конкретно делает флаг в
Код: Выделить всё
-uи почему его достаточно сделать один раз?Код: Выделить всё
git push -u origin main
Ты собрал весь фундамент в один рабочий навык: пустая папка - init - identity - коммиты - ветка - merge - GitHub - push - pull. Три состояния и граф ты теперь не зубришь, а видишь после каждой команды через status и log --graph. Это и есть тот самый первый день с git для начинающих, после которого инструмент перестаёт пугать. Дальше курс идёт вглубь - слияния с конфликтами, rebase, объектная модель - но точка опоры у тебя уже есть. Если шаг не сошёлся, не гадай: запусти
Код: Выделить всё
git status