Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Junior: питання на співбесіді з теми «Основи й щоденна робота»

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

5 питань

У Git файл проходить три місця:

Місце Що там Команда, що переносить далі
робочий каталог файли, які ви редагуєте git add
індекс (staging area) знімок, яким буде наступний коміт git commit
репозиторій збережені коміти -

Індекс - це чернетка наступного коміту. git commit записує не «всі змінені файли», а саме те, що лежить в індексі.

git add app/Models/User.php      # додати файл в індекс
git add .                        # усі зміни в поточному каталозі
git status                       # що в індексі, що змінено, що не відстежується
git commit -m "Add email verification"

Навіщо цей проміжний крок:

  • логічні коміти: за день змінено десять файлів, але це дві різні задачі. В індекс додаються файли першої задачі - коміт, потім другої - ще один коміт;
  • контроль: перед комітом видно, що саме потрапить в історію (git diff --staged), і випадковий dd() чи налагоджувальний файл не проскочать;
  • частини файлу: git add -p додає в індекс лише вибрані фрагменти змін.

Важлива деталь: індекс зберігає вміст файлу на момент git add. Якщо після add файл змінити ще раз, у коміт піде стара версія, а нові зміни залишаться в робочому каталозі - git status покаже файл одночасно в двох секціях.

Прибрати з індексу, не втрачаючи змін у файлі:

git restore --staged app/Models/User.php

git commit -a додає в індекс усі зміни вже відстежуваних файлів і комітить їх одним кроком. Нові файли він не підхоплює - їх треба додати через git add явно.

Докладніше в документації: git add

.gitignore перелічує шаблони файлів, які Git не повинен відстежувати: залежності, кеші, логи, локальні налаштування.

Синтаксис шаблонів:

# коментар
.env              # файл .env у будь-якому каталозі
/.env             # лише в корені репозиторію
storage/logs/     # каталог (слеш у кінці - лише каталоги)
*.log             # усі файли з розширенням .log
**/cache/*.php    # cache/*.php на будь-якій глибині
!.env.example     # заперечення: цей файл НЕ ігнорувати

Правила, що часто дивують:

  • слеш на початку чи всередині прив'язує шаблон до каталогу, де лежить .gitignore; без слеша шаблон спрацьовує на будь-якому рівні;
  • останнє правило перемагає: порядок має значення, ! скасовує попередній збіг;
  • ! не працює, якщо проігноровано батьківський каталог. Git не заходить у проігнорований каталог, тож файл усередині повернути не вийде:
storage/*          # ігнорувати вміст, але не сам каталог
!storage/.gitkeep  # тепер працює
  • .gitignore може бути в будь-якому підкаталозі - його правила діють відносно цього каталогу (так зроблено в Laravel: storage/framework/cache/.gitignore).

Де ще задаються виключення:

Файл Для чого
.gitignore у репозиторії спільне для команди: vendor/, node_modules/, .env
.git/info/exclude лише для вас у цьому репозиторії, не комітиться
глобальний файл (core.excludesFile) ваші персональні файли для всіх проєктів: .DS_Store, .idea/
git config --global core.excludesFile ~/.gitignore_global

Файли редактора й ОС краще тримати в глобальному списку, а не в .gitignore кожного проєкту.

Перевірити, яке правило спрацювало:

git check-ignore -v storage/logs/laravel.log
# storage/logs/.gitignore:1:*   storage/logs/laravel.log

Докладніше в документації: gitignore

git status показує стан файлів відносно індексу й останнього коміту:

On branch main
Changes to be committed:            ← в індексі, піде в коміт
  modified:   app/Models/User.php

Changes not staged for commit:      ← змінено, але не додано
  modified:   routes/web.php

Untracked files:                    ← нові, Git їх ще не відстежує
  app/Actions/SendInvite.php

Компактний формат зручніший для щоденної роботи:

git status -sb
# ## main...origin/main [ahead 1]
# M  app/Models/User.php      ← ліва колонка: індекс
#  M routes/web.php           ← права колонка: робочий каталог
# MM config/app.php           ← змінено і в індексі, і після нього
# ?? app/Actions/SendInvite.php

Три порівняння git diff:

Команда Що з чим порівнює
git diff робочий каталог з індексом - те, що ще не додано
git diff --staged (--cached) індекс з останнім комітом - що піде в коміт
git diff HEAD робочий каталог з останнім комітом - усі зміни разом

Типова плутанина: після git add команда git diff «нічого не показує». Зміни нікуди не зникли - вони в індексі, і їх видно через git diff --staged.

Корисні варіанти:

git diff --stat                 # лише список файлів і кількість рядків
git diff --word-diff            # зміни по словах - зручно для тексту й документації
git diff main..feature -- app/  # між гілками, лише каталог app
git diff -w                     # ігнорувати зміни пробілів

Перед кожним комітом корисна звичка - переглянути git diff --staged: саме цей вивід побачать рецензенти.

Докладніше в документації: git diff

Повідомлення коміту читають через місяці: у git log, git blame, при пошуку помилки. «fix», «wip», «правки» нічого не пояснюють.

Структура доброго повідомлення:

Limit login attempts per email and IP          ← заголовок до ~50-72 символів

Brute force attempts were spread across IPs,   ← порожній рядок, потім тіло
so the per-IP limiter alone did not stop them.
Key the limiter by email and IP together.

Fixes #142                                     ← посилання на задачу

Правила:

  • заголовок - коротко, у наказовому способі («Add», «Fix», а не «Added»), без крапки в кінці;
  • тіло пояснює чому і що саме, а не «як» - як видно з diff;
  • один коміт - одна логічна зміна: якщо в повідомлення проситься «і ще», комітів має бути два.

Conventional Commits - угода про формат заголовка:

<тип>(<область>): <опис>

feat(auth): add two-factor authentication
fix(billing): round VAT before summing invoice lines
refactor(orders): extract shipping calculator
docs: describe queue configuration
feat(api)!: remove v1 endpoints

Основні типи: feat (нова можливість), fix (виправлення), docs, refactor, test, chore, perf, ci, build. ! після типу чи рядок BREAKING CHANGE: у футері позначають несумісну зміну.

Що дає формат:

  • автоматичний changelog і примітки до релізу (release-please, semantic-release);
  • семантичне версіювання: fix - patch, feat - minor, breaking change - major;
  • історію легко фільтрувати: git log --oneline --grep '^feat'.

Перевірка формату - commitlint у хуку commit-msg чи в CI. Головне - щоб угоду дотримувалась уся команда, а не окремі люди.

Докладніше в документації: Conventional Commits 1.0.0

git stash відкладає незакомічені зміни й повертає робочий каталог до стану останнього коміту. Типова ситуація: ви посеред задачі, а треба терміново перемкнутися на іншу гілку й виправити помилку.

git stash push -m "half-done invoice export"   # відкласти
git switch hotfix                              # працювати деінде
git switch feature/export
git stash pop                                  # повернути зміни

Що потрапляє в stash за замовчуванням:

  • зміни у відстежуваних файлах (і в індексі, і в робочому каталозі);
  • нові невідстежувані файли - ні. Для них -u (--include-untracked), для проігнорованих теж - -a (--all).
git stash -u

Робота зі списком:

git stash list
# stash@{0}: On feature/export: half-done invoice export
# stash@{1}: WIP on main: 4f2a1c3 Fix typo

git stash show -p stash@{1}   # що всередині
git stash drop stash@{1}      # видалити

apply проти pop:

git stash apply git stash pop
застосовує зміни так так
видаляє запис зі списку ні так, якщо застосування пройшло без конфліктів
  • apply зручний, коли ті самі зміни треба застосувати в кількох гілках;
  • pop - звичайний випадок «відклав і повернув»;
  • при конфлікті pop запис не видаляє - після розв'язання конфлікту його треба прибрати вручну через git stash drop.

Корисне:

  • git stash push -- app/Models/User.php - відкласти лише вказані файли;
  • --staged - відкласти лише те, що в індексі;
  • git stash branch new-branch - створити гілку від коміту, на якому зроблено stash, і застосувати зміни там. Рятує, коли stash не накладається на поточний код.

Не перетворюйте stash на сховище: записи без опису через тиждень неможливо зрозуміти. Довгу незавершену роботу краще закомітити в окрему гілку.

Докладніше в документації: git stash