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 явно.
.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
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 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. Головне - щоб угоду дотримувалась уся команда, а не окремі люди.
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 на сховище: записи без опису через тиждень неможливо зрозуміти. Довгу незавершену роботу краще закомітити в окрему гілку.