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

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

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

5 питань

Git читає конфігурацію з кількох файлів. Пізніший рівень перекриває раніший:

Рівень Файл Прапорець
system /etc/gitconfig (чи в каталозі встановлення) --system
global ~/.gitconfig чи ~/.config/git/config --global
local .git/config репозиторію --local (за замовчуванням)
worktree .git/config.worktree --worktree
команда git -c key=value ... -
git config --global user.name "Olena Kovalenko"
git config user.email olena@company.com          # лише цей репозиторій
git config --list --show-origin                  # усі значення і звідки вони
git config --show-origin --get user.email

--show-origin - перший інструмент, коли «налаштування не працює»: видно, який файл перекрив значення.

Умовні включення - різні налаштування для роботи й особистих проєктів:

# ~/.gitconfig
[user]
    name = Olena Kovalenko
    email = olena@personal.dev

[includeIf "gitdir:~/work/"]
    path = ~/.gitconfig-work
# ~/.gitconfig-work
[user]
    email = olena@company.com
    signingkey = ~/.ssh/work_ed25519.pub

Усі репозиторії в ~/work/ автоматично отримують робочу пошту - більше ніяких комітів з особистою адресою в корпоративному репозиторії.

Умови includeIf:

  • gitdir: - шлях до репозиторію (gitdir/i: - без урахування регістру);
  • onbranch: - поточна гілка;
  • hasconfig:remote.*.url: - адреса віддаленого репозиторію, наприклад усі репозиторії з github.com/company/**.

Що важливо:

  • слеш у кінці gitdir:~/work/ означає «цей каталог і все всередині»;
  • .git/config не комітиться і не поширюється через клон - спільні налаштування команди передаються інакше (документація, скрипт налаштування, .gitattributes і .editorconfig у репозиторії);
  • у CI змінні оточення GIT_AUTHOR_NAME, GIT_COMMITTER_EMAIL перекривають конфігурацію.

Докладніше в документації: git config: умовні включення

Буває, що в одному файлі змішалися дві задачі: виправлення помилки й рефакторинг. git add -p (--patch) дозволяє додати в індекс лише окремі фрагменти (hunks) змін і зробити з них два чисті коміти.

git add -p app/Services/InvoiceService.php

Git показує кожен фрагмент і питає, що з ним робити:

@@ -42,7 +42,7 @@ public function total(): int
-        return $this->lines->sum('price');
+        return $this->lines->sum(fn ($line) => $line->price * $line->quantity);
(1/3) Stage this hunk [y,n,q,a,d,s,e,?]?

Основні відповіді:

Клавіша Дія
y / n додати / пропустити фрагмент
s розбити фрагмент на менші (якщо між змінами є незмінені рядки)
e відредагувати фрагмент вручну - для змін у сусідніх рядках
a / d додати / пропустити всі решта фрагменти файлу
q вийти

Типовий процес:

git add -p                         # обрати фрагменти виправлення
git diff --staged                  # перевірити, що в індексі
git commit -m "Fix invoice total for multiple quantities"
git add -p                         # тепер рефакторинг
git commit -m "Extract line total calculation"

Той самий режим є в інших командах:

  • git restore -p - відкинути частину змін у робочому каталозі;
  • git restore --staged -p - прибрати частину з індексу;
  • git stash push -p - відкласти лише вибрані фрагменти;
  • git commit -p - обрати фрагменти й одразу закомітити.

Про що пам'ятати:

  • нові файли add -p не показує, бо Git їх ще не відстежує. Спершу git add -N файл (intent-to-add) - тоді вміст з'явиться як фрагменти;
  • проміжний стан може не працювати: закомічена половина змін не перевірялася окремо. Перед пушем корисно прогнати тести на кожному коміті чи хоча б на останньому;
  • IDE (PhpStorm, VS Code) дають той самий механізм через галочки біля рядків - під капотом те саме часткове додавання в індекс.

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

git checkout історично робить дві принципово різні речі:

git checkout feature        # перемкнути гілку
git checkout -- config.php  # відкинути зміни у файлі (безповоротно!)

Одна команда і для безпечної операції, і для руйнівної. git checkout name перемкне гілку, якщо гілка name існує, а якщо ні, але є такий файл - мовчки перезапише файл. Саме через це в Git 2.23 з'явилися дві вузькі команди.

git switch - лише гілки:

git switch main
git switch -c feature/export        # створити й перемкнутися
git switch -c fix origin/fix        # нова локальна гілка від віддаленої
git switch -                        # повернутися на попередню гілку
git switch --detach v2.4.0          # явно перейти в detached HEAD

git restore - лише файли:

git restore config.php                    # відкинути зміни в робочому каталозі
git restore --staged config.php           # прибрати з індексу, зміни лишаються
git restore --source=HEAD~3 config.php    # взяти версію файлу з іншого коміту
git restore --staged --worktree .         # повністю повернути все до HEAD

Відповідність старих і нових команд:

Було Стало
git checkout branch git switch branch
git checkout -b new git switch -c new
git checkout -- file git restore file
git reset HEAD file git restore --staged file
git checkout abc123 -- file git restore --source=abc123 file

Чому це важливо:

  • намір явний: switch ніколи не чіпає незакомічені зміни у файлах без потреби, а restore ніколи не перемикає гілку;
  • detached HEAD не трапляється випадково: git switch v2.4.0 відмовиться працювати з тегом без --detach, тоді як checkout мовчки перейде в detached HEAD;
  • git status сам підказує нові команди («use git restore...»).

git checkout нікуди не дівся і працює, як раніше, - у скриптах і старих інструкціях він зустрічатиметься ще довго. Але для щоденної роботи switch і restore безпечніші.

Обережно: git restore файл так само безповоротно відкидає незакомічені зміни, як і checkout --. Їх немає ні в reflog, ні в stash.

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

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

Типова ситуація: .env чи .idea/ випадково потрапили в репозиторій, потім їх додали в .gitignore - а git status все одно показує зміни.

Рішення - прибрати файл з індексу, не видаляючи з диска:

git rm --cached .env
git rm -r --cached .idea/
git commit -m "Stop tracking local environment files"
  • --cached видаляє файл лише з індексу - на диску він лишається;
  • після коміту файл стає невідстежуваним, і тепер .gitignore на нього діє.

Пастка для команди: у колег після git pull цей файл буде видалено з диска, бо для Git коміт означає «файл видалено». Їхній локальний .env зникне. Попередьте команду або попросіть зберегти копію перед оновленням.

Чим відрізняються варіанти:

Команда Індекс Диск
git rm file видаляє видаляє
git rm --cached file видаляє залишає
rm file залишає (до git add) видаляє

Масово привести репозиторій у відповідність з .gitignore:

git rm -r --cached .
git add .
git commit -m "Apply .gitignore"

Перед комітом обов'язково перегляньте git status - так можна випадково прибрати потрібні файли.

Перейменування - git mv:

git mv app/Helpers.php app/Support/Helpers.php

Це те саме, що mv + git rm старого + git add нового. Git не зберігає факт перейменування - він визначає його при порівнянні за схожістю вмісту.

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

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

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

Головне - ці файли не можна відновити засобами Git. Їх немає в жодному коміті, reflog чи stash. Тому git clean за замовчуванням навіть відмовляється працювати без явного прапорця (clean.requireForce=true).

Безпечний порядок - спершу подивитися:

git clean -n          # dry run: що буде видалено
git clean -nd         # разом з невідстежуваними каталогами
git clean -f          # видалити файли
git clean -fd         # файли й каталоги

Прапорці:

Прапорець Що робить
-n лише показати (dry run)
-f справді видалити
-d включно з каталогами
-x також проігноровані файли (.env, vendor/, node_modules/)
-X лише проігноровані файли
-i інтерактивний вибір
-e <шаблон> додатково виключити шаблон

-x - найнебезпечніший прапорець. git clean -fdx у Laravel-проєкті видалить .env, vendor/, node_modules/, storage/*.key, локальну базу SQLite - усе, що ігнорується. Корисно для «чистого» збирання в CI, але вдома варто запускати лише після -n.

git clean -fdx -e .env -n     # усе, окрім .env, - спершу перевірка

Інтерактивний режим зручний, коли більшість файлів треба видалити, а кілька - залишити:

git clean -id

Типові сценарії:

  • повернути репозиторій до стану щойно клонованого: git restore --staged --worktree . (відстежувані) + git clean -fd (невідстежувані);
  • прибрати лише результати збирання, не чіпаючи нові вихідні файли: git clean -fX;
  • CI на повторно використовуваному runner-і - git clean -ffdx перед збиранням (подвійний -f видаляє й вкладені репозиторії).

Альтернатива без ризику: якщо не впевнені, git stash -u прибере невідстежувані файли, але збереже їх у stash.

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