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

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

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

5 питань

  • git fetch завантажує нові коміти й гілки з віддаленого репозиторію і оновлює віддалені гілки (origin/main). Ваші локальні гілки й робочі файли не змінюються.
  • git pull = fetch + інтеграція змін у поточну гілку (за замовчуванням merge, або rebase, якщо так налаштовано).
git fetch
git log main..origin/main      # що нового на сервері, ще до злиття
git diff main origin/main      # які саме зміни
git merge origin/main          # або git rebase origin/main

Чому fetch корисний: спершу подивитися, що змінилося, і лише потім вирішити, як інтегрувати. pull робить усе одразу, і при конфлікті ви опиняєтеся посеред злиття без попередження.

Налаштування pull, яке варто знати:

git config --global pull.rebase true    # pull через rebase - без зайвих комітів злиття
git config --global pull.ff only        # або: лише перемотування, інакше помилка

Без явного налаштування сучасний Git при розбіжності гілок відмовляється робити pull і просить обрати стратегію.

Типова ситуація: «у мене не той код, що на сервері» - часто просто не зроблено fetch, і origin/main у вас застарілий.

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

Конфлікт виникає, коли обидві гілки змінили ті самі рядки (або одна змінила файл, а інша - видалила). Git не знає, яка версія правильна, і зупиняє злиття.

git merge feature
# CONFLICT (content): Merge conflict in app/Services/Billing.php
git status                       # список файлів з конфліктами

У файлі з'являються маркери:

<<<<<<< HEAD
$total = $subtotal * 1.2;
=======
$total = $subtotal + $this->tax($subtotal);
>>>>>>> feature

Що робити:

  1. Відредагувати файл: залишити потрібну версію, поєднати обидві чи написати третю. Прибрати всі маркери.
  2. Перевірити, що код працює: тести, запуск.
  3. Позначити конфлікт розв'язаним і завершити:
git add app/Services/Billing.php
git commit                      # для rebase: git rebase --continue

Передумали - повернути все як до злиття: git merge --abort (чи git rebase --abort).

Корисне:

  • git config --global merge.conflictStyle zdiff3 - маркери показують ще й спільного предка: видно не лише «що в кожній гілці», а й «що було до обох змін». Розв'язувати значно легше.
  • IDE (PhpStorm, VS Code) мають тристоронній редактор конфліктів.
  • Для файлів, які генеруються (composer.lock, package-lock.json), руками не мержать: беруть одну версію й заново виконують команду, що її створила.

Як конфліктів менше: короткоживучі гілки, часта інтеграція з main, невеликі pull request.

Докладніше в документації: git merge: розв'язання конфліктів

У Git три види гілок, і їх часто плутають:

Що Приклад Хто змінює
локальна гілка main, feature/export ви, комітами
віддалена гілка (remote-tracking) origin/main лише git fetch/pull/push - це «знімок» стану сервера на момент останньої синхронізації
гілка на сервері main у репозиторії на GitHub інші учасники через push

Upstream (відстежувана гілка) - зв'язок локальної гілки з віддаленою. Він дозволяє писати просто git push і git pull без параметрів і показує розбіжність:

git status
# Your branch is ahead of 'origin/main' by 2 commits.

Нова локальна гілка upstream не має - тому перший git push відповідає:

fatal: The current branch feature/export has no upstream branch.
To push the current branch and set the remote as upstream, use
    git push --set-upstream origin feature/export
git push -u origin feature/export   # -u = --set-upstream

Після цього git push і git pull працюють без аргументів.

Автоматизація: з git config --global push.autoSetupRemote true перший git push сам створює гілку на сервері й налаштовує upstream.

Корисні команди:

git branch -vv                       # усі гілки з їхнім upstream і розбіжністю
git branch -u origin/main            # задати upstream для поточної гілки
git switch feature/export            # якщо локальної гілки немає, а origin/feature/export є -
                                     # Git створить її з upstream автоматично
git fetch --prune                    # прибрати origin/* гілки, видалені на сервері

Типова плутанина: origin/main не оновлюється сам. Якщо git status каже «up to date with 'origin/main'», це означає «збігається з тим, що ви бачили під час останнього fetch», а не з поточним станом сервера.

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

Форк - ваша копія чужого репозиторію на GitHub. Через форк вносять зміни в проєкти, куди немає прав на запис: так працює більшість open source, включно з Laravel.

Схема з двома віддаленими репозиторіями:

git clone git@github.com:olena/framework.git          # ваш форк - це origin
cd framework
git remote add upstream https://github.com/laravel/framework.git
git remote -v
Remote Що це Права
origin ваш форк читання й запис
upstream оригінальний репозиторій лише читання

Робочий цикл:

git fetch upstream
git switch -c fix/route-cache upstream/13.x      # гілка від актуального стану оригіналу
# ... зміни, коміти ...
git push -u origin fix/route-cache               # у свій форк

Далі на GitHub - pull request з olena:fix/route-cache у laravel:13.x.

Синхронізація форку:

git fetch upstream
git switch main
git merge --ff-only upstream/main
git push origin main

Або кнопка «Sync fork» на сторінці форку на GitHub, або gh repo sync.

Правила, що позбавляють проблем:

  • не комітьте в main форку - тримайте його точною копією upstream/main, а кожну зміну робіть в окремій гілці. Тоді синхронізація завжди fast-forward;
  • гілку для pull request створюйте від свіжого upstream/<цільова гілка>, а не від застарілого main форку;
  • якщо оригінал просунувся під час рев'ю - git fetch upstream && git rebase upstream/13.x і git push --force-with-lease;
  • цільова гілка має значення: у Laravel виправлення йдуть у гілку поточної версії, а нові можливості - в master; це описано в правилах внеску.

Дозвіл супровідникам («Allow edits by maintainers») на pull request дає їм змогу дописати дрібні виправлення прямо у вашу гілку.

Докладніше в документації: GitHub: синхронізація форку

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

         C   (ваш)
        /
A ── B
        \
         D   (origin/main)

Сучасний Git при git pull у такому разі не обирає спосіб сам, а просить вирішити:

hint: You have divergent branches and need to specify how to reconcile them.
fatal: Need to specify how to reconcile divergent branches.

Три варіанти:

Налаштування Що відбувається Результат
pull.rebase false злиття коміт злиття Merge branch 'main' of ...
pull.rebase true rebase ваш коміт C переноситься поверх D, історія лінійна
pull.ff only лише fast-forward при розбіжності pull відмовляється, і ви вирішуєте вручну
git config --global pull.rebase true
# або разово
git pull --rebase

Що обрати для локальних комітів у спільній гілці - зазвичай rebase:

  • ваші ще не відправлені коміти просто «перебудовуються» поверх свіжих змін;
  • історія не засмічується безглуздими комітами Merge branch 'main' of github.com:..., які нічого не означають, крім «ми з колегою працювали одночасно».

Коли rebase при pull не підходить:

  • ваші локальні коміти - це злиття іншої гілки (rebase їх розгорне, якщо не вказати --rebase=merges);
  • конфліктів дуже багато: rebase розв'язує їх коміт за комітом, злиття - один раз.

Корисно разом з rebase: git config --global rebase.autoStash true - незакомічені зміни автоматично ховаються в stash перед rebase й повертаються після.

Якщо щось пішло не так: git rebase --abort під час rebase, git reflog - після.

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