Git

19 питань

Як скасувати коміт у Git?

Скасувати коміт можна трьома способами залежно від ситуації.

Якщо коміт ще не надіслано у спільний репозиторій (push не робився), використовуйте git reset. Команда git reset --soft HEAD~1 скасує останній коміт, але збереже зміни в індексі (staging area) — ви зможете змінити повідомлення і закомітити знову. Команда git reset --mixed HEAD~1 (це поведінка за замовчуванням) скасує коміт і прибере зміни з індексу, але залишить їх у робочій директорії. Команда git reset --hard HEAD~1 повністю видалить і коміт, і зміни — використовуйте її лише якщо точно впевнені, що код не потрібен.

Якщо коміт уже надіслано у спільний репозиторій, використовуйте git revert. Команда git revert HEAD створює новий коміт, який скасовує зміни попереднього. Це безпечно для командної роботи, тому що історія не переписується, а додається новий коміт скасування. Можна скасувати кілька комітів: git revert HEAD~3..HEAD.

Якщо потрібно змінити лише повідомлення останнього коміту або додати забутий файл, використовуйте git commit --amend. Ця команда замінює останній коміт новим з тим самим вмістом, але іншим повідомленням або додатковими файлами. Не використовуйте --amend для комітів, які вже надіслано у спільний репозиторій — це перепише історію і створить проблеми в інших розробників.

Правило: reset — для локальних комітів, revert — для спільних, amend — для виправлення останнього коміту до push.

Як вирішити конфлікт при злитті в Git?

Конфлікт виникає, коли дві гілки змінюють одні й ті самі рядки файлу і Git не може автоматично об'єднати зміни. Ось покроковий процес вирішення.

Спочатку виконайте git status, щоб побачити conflicted files. Git позначає конфлікти у файлах маркерами: <<<<<<< HEAD (ваші зміни), ======= (роздільник) і >>>>>>> branch-name (зміни з іншої гілки).

Відкрийте файл у редакторі і знайдіть маркери конфліктів. Вам потрібно вибрати, які зміни залишити: ваші, чужі, або об'єднати обидва. Видаліть маркери <<<<<<<, ======= і >>>>>>>, залишивши лише потрібний код. Збережіть файл.

Після вирішення всіх конфліктів у файлі виконайте git add <файл> для кожного вирішеного файлу. Коли всі конфлікти вирішені, виконайте git commit — Git створить merge-коміт автоматично з повідомленням за замовчуванням.

Для складних конфліктів використовуйте інструменти візуального злиття: git mergetool запустить налаштований інструмент (VS Code, Meld, KDiff3). У VS Code можна вирішувати конфлікти прямо в редакторі — кнопки Accept Current, Accept Incoming, Accept Both.

Якщо хочете скасувати злиття і повернутися до стану до нього, виконайте git merge --abort. Це корисно, коли конфліктів забагато і простіше почати заново.

Порада: щоб зменшити кількість конфліктів, частіше робіть rebase або merge з основної гілки у свою feature-гілку, а також робіть невеликі коміти, що зачіпають менше файлів.

У чому різниця між git merge та git rebase?

Merge та rebase — два способи об'єднати зміни з однієї гілки в іншу, але вони працюють по-різному.

git merge створює новий merge-коміт, який об'єднує історії обох гілок. Історія зберігається повністю — видно, яка гілка коли відокремлювалася і об'єднувалася. Це безпечно для спільних гілок, тому що не переписує історію. Недолік — історія може стати заплутаною з безліччю merge-комітів, особливо у великих командах. Використовуйте merge, коли об'єднуєте feature-гілку в main або коли працюєте з публічними репозиторіями.

git rebase переносить ваші коміти поверх цільової гілки, створюючи лінійну історію. Git бере ваші коміти, скасовує їх, оновлює гілку до цільового стану, і застосовує ваші коміти знову. Історія виглядає чистою і прямою, без merge-комітів. Недолік — rebase переписує історію, що небезпечно для спільних гілок: якщо хтось уже зробив pull вашої гілки, після rebase їхня історія не збігеться з вашою. Використовуйте rebase для локальних feature-гілок перед merge в main.

Головне правило: ніколи не робіть rebase публічних гілок (main, develop, або гілок, на які посилаються інші розробники). Якщо гілка вже надіслана в remote і хтось може на ній працювати — використовуйте merge.

Багато команд використовують гібридний підхід: rebase feature-гілки перед merge, потім merge в main з --no-ff (no fast-forward) для збереження точки злиття. Це дає чисту історію комітів і при цьому видно, які коміти належали feature-гілці.

Як працювати з гілками в Git?

Гілки — основа роботи в Git. Вони дозволяють вести паралельну розробку без втручання в основну лінію.

Створення гілки: git branch <name> створює нову гілку, але не перемикається на неї. Команда git checkout -b <name> (або git switch -c <name> у нових версіях Git) створює гілку і одразу перемикається на неї. Для створення гілки від певного коміту: git checkout -b <name> <commit-hash>.

Перегляд гілок: git branch показує локальні гілки, git branch -a показує всі, включно з remote. Поточна гілка позначена зірочкою. Команда git branch -v показує останній коміт кожної гілки.

Перемикання: git checkout <name> або git switch <name>. Для перемикання на гілку remote: git checkout -b <name> origin/<name>.

Видалення гілки: git branch -d <name> видаляє гілку, але лише якщо вона об'єднана з поточною. Команда git branch -D <name> видаляє примусово, навіть якщо є нез'єднані зміни. Для видалення remote-гілки: git push origin --delete <name>.

Злиття: переключіться на цільову гілку (git checkout main), потім git merge <feature-branch>. Якщо немає конфліктів, Git створить merge-коміт або зробить fast-forward.

Перейменування: git branch -m <new-name> перейменовує поточну гілку. Для remote: перейменуйте локально, потім git push origin -u <new-name> і видаліть стару remote-гілку.

Гарна практика: називайте гілки за схемою feature/короткий-опис, bugfix/issue-номер, hotfix/опис. Це допомагає команді розуміти призначення гілки.

Що таке git stash і як ним користуватися?

git stash зберігає незакомічені зміни у тимчасове сховище, повертаючи робочу директорію до стану останнього коміту. Це корисно, коли потрібно переключитися на іншу гілку, але поточні зміни ще не готові для коміту.

Збереження: git stash зберігає зміни в індексі та робочій директорії. За замовчуванням stash не включає untracked файли — використовуйте git stash -u для їх включення. Можна додати опис: git stash push -m "робота над авторизацією".

Перегляд: git stash list показує всі збережені stash-записи. Кожен запис має вигляд stash@{0}, stash@{1} і т.д. Команда git stash show stash@{0} показує файли в конкретному записі, а git stash show -p stash@{0} показує diff.

Відновлення: git stash pop застосовує останній запис і видаляє його зі списку. Команда git stash apply stash@{1} застосовує запис, але не видаляє його — корисно, якщо хочете застосувати зміни в кількох гілках. Якщо при pop виникають конфлікти, запис не видаляється, і ви можете вирішити конфлікти вручну.

Видалення: git stash drop stash@{0} видаляє конкретний запис. Команда git stash clear видаляє всі записи — використовуйте з обережністю.

Практичний сценарій: ви працюєте над feature-гілкою, з'являється терміновий баг у main. Виконуєте git stash, переключаєтеся на main, фіксите баг, комітите, повертаєтеся на feature-гілку і робите git stash pop — зміни відновлено.

Обмеження: stash зберігається локально і не синхронізується з remote. Якщо потрібно перенести зміни між машинами, краще створіть тимчасову гілку і закомітьте туди.

Як налаштувати .gitignore і що туди додавати?

Файл .gitignore вказує Git, які файли та директорії ігнорувати — не відстежувати і не включати в коміти. Це критично важливо для чистоти репозиторію.

Що додавати в .gitignore: файли збірки та артефакти (node_modules/, dist/, build/, *.o, *.class), файли залежностей (vendor/, package-lock.json — залежить від стратегії команди), файли IDE та редакторів (.idea/, .vscode/, *.swp), файли операційної системи (.DS_Store, Thumbs.db), файли з секретами (.env, config/local.json, *.pem, *.key), файли логів (*.log, logs/) та тимчасові файли (*.tmp, *.bak).

Синтаксис: кожен рядок — правило. Зірочка замінює будь-яку кількість символів: *.log ігнорує всі log-файли. Слеш у кінці означає директорію: node_modules/ ігнорує папку цілком. Знак оклику скасовує ігнорування: !important.log включить файл, навіть якщо *.log його ігнорує. Подвійна зірочка — рекурсивний шлях: logs/**/*.tmp ігнорує .tmp-файли на будь-якій глибині всередині logs/.

Структура: .gitignore в корені репозиторію застосовується до всього проєкту. Файл .gitignore у піддиректорії додає правила для цієї директорії. Глобальний .gitignore (git config --global core.excludesfile ~/.gitignore_global) застосовується до всіх проєктів користувача — зручно для файлів OS та IDE.

Шаблони: сайт gitignore.io (або github.com/github/gitignore) містить готові шаблони для мов і фреймворків. Для Node.js проєкту: node_modules/, dist/, .env, .env.local, npm-debug.log*. Для Python: __pycache__/, *.pyc, .venv/, .env.

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

Як писати хороші повідомлення комітів у Git?

Гарне повідомлення коміту допомагає команді розуміти історію і спрощує налагодження. Ось правила та практики.

Структура повідомлення: перший рядок — коротка зведення, не більше 50 символів, у наказовому способі: "Додати валідацію форми реєстрації", а не "Додав валідацію". Після порожнього рядка — детальний опис: що змінилося і чому, не як (код сам показує як). Опис — до 72 символів на рядок.

Conventional Commits — популярний стандарт форматування. Формат: type(scope): description. Типи: feat (нова функція), fix (виправлення багу), docs (документація), style (форматування), refactor (рефакторинг), test (тести), chore (обслуговування), perf (продуктивність), ci (CI-конфігурація). Приклад: feat(auth): додати OAuth2 авторизацію через Google. Це дозволяє автоматично генерувати changelog і керувати версіями.

Практичні правила: один коміт — одна логічна зміна. Не змішуйте рефакторинг і нову функцію в одному коміті. Якщо зміна зачіпає кілька файлів, але всі вони частина одного завдання — це один коміт. Комітьте часто — маленькі коміти легше відкочувати і перевіряти.

Чого уникати: повідомлень типу "fix", "update", "wip", "різні правки". Повідомлення "виправити баг" не каже нічого — який баг, де, як. Краще: "fix(cart): виправити розрахунок суми при порожньому промокоді". Не пишіть повідомлення в минулому часі — "додав" замість "додати".

Багаторядкові повідомлення: використовуйте git commit без -m — відкриється редактор. Або кілька прапорців -m: git commit -m "короткий опис" -m "детальний опис у другому абзаці".

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

Як працювати з remote-репозиторіями (origin, fork) у Git?

Remote-репозиторій — це версія вашого проєкту, що зберігається на сервері (GitHub, GitLab, Bitbucket). Керування remote-репозиторіями — ключовий навик для командної роботи.

Перегляд remote: git remote -v показує всі налаштовані remote з URL. За замовчуванням перший remote називається origin. Команда git remote show origin показує детальну інформацію: гілки, tracking-налаштування.

Додавання remote: git remote add <name> <url>. Наприклад, якщо ви зробили fork проєкту і хочете отримувати оновлення з оригіналу: git remote add upstream https://github.com/author/project.git. Тепер можна робити git fetch upstream, щоб отримувати зміни з оригінального репозиторію.

Видалення та перейменування: git remote remove <name> видаляє remote. Команда git remote rename <old> <new> перейменовує.

Надсилання змін: git push origin <branch> надсилає локальну гілку в remote. Прапорець -u (git push -u origin <branch>) встановлює tracking — після цього git push і git pull працюватимуть без вказування remote та гілки. Команда git push --force примусово перезаписує remote-гілку — використовуйте лише для особистих гілок після rebase. Безпечна альтернатива: git push --force-with-lease — перезапише лише якщо ніхто інший не додав коміти.

Отримання змін: git fetch origin завантажує зміни, але не застосовує їх — ви можете переглянути їх через git log origin/main. Команда git pull = git fetch + git merge — завантажує і одразу об'єднує. Для лінійної історії: git pull --rebase.

Робота з fork: типовий цикл — fork на GitHub, git clone вашого fork, git remote add upstream оригінал, створення feature-гілки, коміти, git push origin feature-branch, потім pull request через веб-інтерфейс. Для синхронізації з оригіналом: git fetch upstream, git checkout main, git merge upstream/main, git push origin main.

Як безпечно повертати зміни у командній роботі?

Відкат змін у командній роботі вимагає обережності — неправильний відкат може затерти чужу роботу або створити конфлікти у всієї команди. Ось безпечні підходи.

git revert — найбезпечніший спосіб. Створює новий коміт, який скасовує зміни вказаного коміту. Історія не переписується, тому безпечний для спільних гілок. Команда git revert <commit-hash> відкочує один коміт. Можна відкотити діапазон: git revert HEAD~3..HEAD створить три revert-коміти. Якщо revert викликає конфлікти (код навколо змінився), вирішіть їх як при звичайному merge.

git reset — переписує історію, використовуйте лише для локальних гілок або гілок, які ніхто не використовує. git reset --hard <commit> переміщує вказівник гілки назад і видаляє всі зміни після цього коміту. Ніколи не робіть reset --hard на гілках, надісланих у remote, якщо інші розробники на них працюють — це створить розбіжності.

git revert vs git reset: revert додає новий коміт скасування — безпечно для всіх. reset видаляє коміти з історії — безпечно лише локально. Правило: якщо гілка в remote і хтось міг зробити pull — використовуйте revert. Якщо гілка локальна або ви єдиний розробник — reset допустимий.

Відкат push: якщо ви вже зробили push небажаних комітів, використовуйте git revert, потім git push. Якщо потрібно видалити коміти з remote (наприклад, випадково запушили секрети), зробіть git reset --hard <останній-хороший-коміт>, потім git push --force-with-lease. Попередьте команду — їм доведеться git fetch і git reset --hard origin/<branch>, бо їхня локальна історія більше не збігається.

Відновлення випадково видалених комітів: git reflog показує історію переміщень HEAD. Знайдіть хеш потрібного коміту і виконайте git reset --hard <hash> — коміт відновиться. Reflog зберігає історію 90 днів за замовчуванням.

Золоте правило: при сумніві — використовуйте revert, а не reset. Revert завжди безпечний, тому що він додає, а не видаляє.

Як встановити і налаштувати Git з нуля?

Установка та налаштування Git — перший крок до контролю версій. Процес залежить від операційної системи.

Установка: на Windows завантажте Git for Windows з git-scm.com — установник включає Git Bash та Git GUI. На macOS використовуйте Homebrew: brew install git, або встановіть Xcode Command Line Tools: xcode-select --install. На Linux (Ubuntu/Debian): sudo apt install git, на Fedora: sudo dnf install git.

Перевірка: git --version покаже встановлену версію. Якщо команда не знайдена, перезапустіть термінал або перевірте PATH.

Базове налаштування: вкажіть ім'я та email — вони будуть у кожному коміті. git config --global user.name "Ваше Ім'я" та git config --global user.email "you@example.com". Використовуйте реальний email, прив'язаний до GitHub/GitLab, щоб коміти зв'язувалися з вашим акаунтом. Перевірити налаштування: git config --list.

Налаштування SSH-ключів: замість пароля при кожному push використовуйте SSH. Створіть ключ: ssh-keygen -t ed25519 -C "you@example.com". Натисніть Enter для шляху за замовчуванням. Додайте ключ в ssh-agent: eval "$(ssh-agent -s)" і ssh-add ~/.ssh/id_ed25519. Скопіюйте публічний ключ: cat ~/.ssh/id_ed25519.pub і додайте його в GitHub: Settings → SSH and GPG keys → New SSH key. Перевірте: ssh -T git@github.com має показати "Hi username!".

Корисні глобальні налаштування: git config --global init.defaultBranch main — встановлює main як гілку за замовчуванням замість master. git config --global core.editor "code --wait" — використовує VS Code як редактор для комітів. git config --global pull.rebase true — робить pull з rebase замість merge для лінійної історії.

Перший репозиторій: створіть папку, виконайте git init, створіть файл, git add ., git commit -m "Initial commit". Якщо використовуєте GitHub: git remote add origin git@github.com:username/repo.git, git push -u origin main.

Чим відрізняється git pull від git fetch?

git fetch та git pull обидва отримують зміни з remote-репозиторію, але працюють по-різному. Розуміння різниці допомагає уникати несподіваних конфліктів.

git fetch завантажує зміни з remote, але не застосовує їх до вашої робочої директорії. Оновлюються лише remote-tracking-гілки (origin/main, origin/feature і т.д.). Ваша локальна гілка та робочі файли залишаються недоторканими. Після fetch ви можете переглянути зміни: git log origin/main, git diff main origin/main. Це безпечно — ви нічого не ламаєте, просто отримуєте інформацію про нові коміти.

git pull = git fetch + git merge. Спочатку завантажує зміни, потім одразу об'єднує їх з вашою поточною гілкою. Якщо в remote є нові коміти, а у вас — локальні зміни, pull створить merge-коміт або, при розбіжностях, конфлікти. Це може бути несподіваним, якщо ви не перевірили, що саме змінилося в remote.

Коли використовувати fetch: завжди, коли хочете подивитися, що нового в remote, перед застосуванням. Особливо корисно перед початком роботи — виконайте git fetch вранці, перевірте git log origin/main, і якщо все гаразд, зробіть git merge або git rebase. Також fetch потрібен для перегляду чужих feature-гілок без перемикання на них.

Коли використовувати pull: коли ви впевнені, що в remote немає несподіваних змін і хочете швидко оновитися. Наприклад, ви єдиний розробник на гілці або зміни тривіальні.

Pull з rebase: git pull --rebase замість merge застосовує ваші локальні коміти поверх завантажених. Це дає лінійну історію без merge-комітів. Можна налаштувати за замовчуванням: git config --global pull.rebase true. Багато команд віддають перевагу цьому варіанту.

Практична порада: використовуйте git fetch за звичкою, а git pull — лише коли точно знаєте, що отримаєте. Це позбавить від несподіваних конфліктів у невідповідний момент.

Як переглянути історію комітів і знайти потрібний?

Історія комітів — головний інструмент для розуміння, що і коли змінилося в проєкті. Git пропонує кілька способів перегляду та пошуку.

Базовий перегляд: git log показує історію у зворотному хронологічному порядку — останні коміти вгорі. Кожен коміт містить хеш, автора, дату та повідомлення. Команда git log --oneline показує компактний формат — один коміт на рядок, лише хеш і повідомлення. Корисно для огляду великої історії.

Фільтрація: git log -5 показує останні 5 комітів. git log --author="Іван" фільтрує за автором. git log --since="2 weeks ago" — коміти за останні 2 тижні. git log --since="2026-01-01" --until="2026-06-01" — діапазон дат. git log -- <файл> показує лише коміти, що зачіпають конкретний файл.

Пошук за повідомленням: git log --grep="авторизація" шукає коміти з ключовим словом у повідомленні. Прапорець -i робить пошук нечутливим до регістру: git log --grep="auth" -i. Можна шукати за регулярними виразами: git log --grep="fix\(.*\)".

Візуалізація: git log --graph --oneline --all показує дерево гілок у вигляді графа — зручно для розуміння структури злиттів. Прапорець --all включає всі гілки, не лише поточну. Альтернатива: git log --graph --oneline --decorate — додає мітки гілок та тегів.

Зміни в коміті: git show <hash> показує повний diff конкретного коміту. git show HEAD — останній коміт. git show HEAD~2 — третій з кінця. Команда git diff <hash1>..<hash2> показує різницю між двома комітами.

Пошук багу з bisect: git bisect start, git bisect bad (поточний коміт зламаний), git bisect good <hash> (цей коміт працював). Git автоматично переключає коміти в середині діапазону. Ви тестуєте і позначаєте git bisect good або git bisect bad. Git знаходить коміт, який вніс баг. Завершення: git bisect reset.

Blame: git blame <файл> показує, хто і в якому коміті написав кожен рядок файлу. Корисно для розуміння контексту змін. git blame -L 10,20 <файл> — лише рядки 10-20.

Що таке git cherry-pick і коли його використовувати?

git cherry-pick бере зміни з конкретного коміту і застосовує їх до поточної гілки, створюючи новий коміт з тими самими змінами. Це корисно, коли потрібно перенести окрему зміну, не об'єднуючи всю гілку.

Коли використовувати: ви зробили fix у feature-гілці, але він потрібен і в main — cherry-pick переносить лише цей коміт. Або ви випадково закомітили в wrong branch — cherry-pick переносить коміт у правильну гілку. Також для перенесення hotfix між релізними гілками: cherry-pick fix з main в release-1.2.

Як використовувати: переключіться на цільову гілку (git checkout main), потім git cherry-pick <commit-hash>. Git застосує зміни з вказаного коміту і створить новий коміт у поточній гілці. Хеш нового коміту буде іншим, але зміни і повідомлення — ті самі. Можна перенести кілька комітів: git cherry-pick <hash1> <hash2> <hash3> або діапазон: git cherry-pick <hash1>..<hash2>.

Конфлікти при cherry-pick: якщо код навколо змін відрізняється між гілками, виникне конфлікт. Вирішіть його як при звичайному merge: відредагуйте файли, git add, git cherry-pick --continue. Для скасування: git cherry-pick --abort.

Відмінність від merge: merge об'єднує всю гілку з усіма комітами. cherry-pick переносить лише вказані коміти. Відмінність від rebase: rebase переносить усі коміти гілки поверх іншої. cherry-pick — точкове перенесення.

Обережність: cherry-pick створює дублікат коміту з новим хешем. Якщо пізніше зробити merge гілки, з якої був cherry-pick, можливі конфлікти через однакові зміни. Намагайтеся cherry-pick лише коли merge або rebase неможливі або недоцільні.

Практичний сценарій: ви виправили критичний баг у develop, але його потрібно терміново доставити в main. git checkout main, git cherry-pick <fix-commit-hash>, git push. Fix в main без merge всієї develop-гілки.

Як видалити гілку локально і в remote-репозиторії?

Видалення гілок — рутинне завдання після завершення роботи. Гілки потрібно видаляти і локально, і в remote, щоб репозиторій не засмічувався.

Локальне видалення: git branch -d <name> видаляє гілку, але лише якщо вона об'єднана з поточною (merged). Git перевірить, що всі коміти гілки є в поточній — якщо ні, видасть помилку. Це захист від втрати роботи. Команда git branch -D <name> видаляє примусово, навіть якщо є нез'єднані коміти. Використовуйте -D лише якщо впевнені, що зміни не потрібні — після видалення відновити гілку можна лише через reflog.

Видалення в remote: git push origin --delete <name> видаляє гілку на сервері (GitHub, GitLab). Скорочена форма: git push origin :<name>. Після видалення remote-гілки інші розробники побачать її як "gone" при наступному git fetch.

Видалення tracking-гілок: після видалення remote-гілки локальна tracking-копія (origin/<name>) залишається. Очистіть її: git fetch --prune або git remote prune origin. Це видаляє локальні посилання на неіснуючі remote-гілки.

Масова очистка: git branch --merged main показує гілки, об'єднані з main — їх можна безпечно видаляти. Команда для видалення всіх merged-гілок окрім main: git branch --merged main | grep -v "main" | xargs git branch -d. Будьте обережні — переконайтеся, що не видалите гілку, на якій працюєте.

Відновлення видаленої гілки: git reflog покаже хеш останнього коміту видаленої гілки. Виконайте git branch <name> <hash> — гілка відновиться. Reflog зберігає історію 90 днів, тому відновлення можливе протягом цього терміну.

Гарна практика: видаляйте feature-гілки одразу після merge в main. Це keeps гілок чистим і полегшує навігацію. На GitHub можна налаштувати автоматичне видалення гілок після merge: Settings → Options → Automatically delete head branches.

Як скасувати git add (прибрати файли з індексу)?

Якщо ви випадково додали файли в індекс через git add і хочете прибрати їх до коміту, є кілька способів.

git restore --staged <файл> — сучасний спосіб (Git 2.23+). Прибирає файл з індексу, але зберігає зміни в робочій директорії. Наприклад: git restore --staged index.html прибере файл з staged, але зміни у файлі залишаться. Команда git restore --staged . прибирає з індексу всі файли.

git reset HEAD <файл> — класичний спосіб, працює у всіх версіях Git. Робить те саме: прибирає файл з індексу, зміни залишаються. git reset HEAD . прибирає всі файли. Команда git reset (без аргументів) прибирає з індексу все.

git reset --soft HEAD~1 — якщо ви вже закомітили, але хочете повернути зміни в індекс (uncommit). Коміт скасовується, зміни залишаються staged. git reset --mixed HEAD~1 (або просто git reset HEAD~

— скасовує коміт і прибирає зміни з індексу (unstaged), але залишає в робочій директорії.

Повний сброс: git reset --hard HEAD видаляє всі зміни в робочій директорії та індексі — повертає до стану останнього коміту. Використовуйте лише якщо впевнені, що зміни не потрібні. Це необоротно (окрім через reflog).

Різниця між станами: Modified — файл змінено, але не додано в індекс. Staged — файл додано через git add, готовий до коміту. Committed — зміни збережені в коміті. git add переводить Modified → Staged. git restore --staged переводить Staged → Modified. git commit переводить Staged → Committed.

Практичний сценарій: ви зробили git add . і зрозуміли, що додали .env випадково. Виконайте git restore --staged .env — файл прибереться з індексу. Додайте .env в .gitignore, щоб уникнути цього в майбутньому. Якщо вже закомітили .env: git rm --cached .env, закомітьте видалення, додайте в .gitignore.

Що робити, якщо зробив push у неправильну гілку?

Push у неправильну гілку — часта помилка. Дії залежать від того, помітили чи ви одразу і чи є вже pull від інших розробників.

Ситуація

push у свою feature-гілку, але коміти мають бути в іншій. Створіть правильну гілку від поточного стану: git branch correct-branch. Перемкніться на неї: git checkout correct-branch. Тепер коміти в правильній гілці. Видаліть неправильні коміти з вихідної гілки: git checkout wrong-branch, git reset --hard origin/wrong-branch (повертає до стану remote до вашого push), git push --force-with-lease. Потім push правильну гілку: git checkout correct-branch, git push -u origin correct-branch.

Ситуація

push у спільну гілку (main, develop). Якщо коміти невеликі і ніхто ще не зробив pull, можна скасувати через git revert. Перемкніться на main: git checkout main. Створіть revert-коміти: git revert HEAD~2..HEAD (якщо запушили 2 коміти). Надішліть: git push. Це безпечно — історія не переписується, додаються revert-коміти.

Ситуація

push містив секрети (паролі, ключі). Просто revert недостатньо — секрети залишаться в історії. Потрібно переписати історію: git reset --hard <коміт-до-секрету>, git push --force-with-lease. Потім обов'язково ротуєте утічені секрети — змініть паролі, перевипустіть ключі. Навіть після force-push секрети залишаються в reflog та cached-об'єктах Git на сервері 90 днів. Для повної очистки використовуйте BFG Repo-Cleaner або git filter-repo. На GitHub: зверніться в підтримку для видалення cached-копій.

Ситуація

кілька розробників уже зробили pull вашої помилкової гілки. Не робіть force-push — це зламає їхні локальні репозиторії. Використовуйте git revert — створіть revert-коміти і запуште. Команда отримає revert через pull, і помилкові зміни скасуються у всіх.

Профілактика: налаштуйте branch protection на GitHub — забороніть прямий push в main. Використовуйте pull requests для всіх змін. Перед push перевіряйте поточну гілку: git branch — зірочка показує поточну.

Як об'єднати кілька комітів в один (squash)?

Squash — об'єднання кількох комітів в один. Це корисно, коли ви зробили багато дрібних комітів у feature-гілці ("wip", "fix typo", "fix again") і хочете представити їх як один чистий коміт в історії.

Через interactive rebase: git rebase -i HEAD~4, де 4 — кількість комітів для об'єднання. Відкриється редактор зі списком комітів. Кожен коміт позначений словом pick. Змініть pick на squash (або просто s) для комітів, які хочете об'єднати з попереднім. Перший коміт залиште pick — він буде базовим. Збережіть і закрийте редактор. Відкриється другий редактор для редагування повідомлення об'єднаного коміту — впишіть чистий опис всіх змін.

Приклад: у вас 4 коміти — "feat: add form", "fix: validation", "typo", "style: button". Виконайте git rebase -i HEAD~

Змініть: pick a1b2c3d feat: add form squash d4e5f6g fix: validation squash h7i8j9k typo squash l0m1n2o style: button

Після збереження впишіть нове повідомлення: "feat: add registration form with validation and styling". Вийде один коміт замість чотирьох.

Через git merge --squash: при злитті feature-гілки в main виконайте git merge --squash feature-branch. Git об'єднає всі зміни, але не створить merge-коміт. Потім git commit -m "feat: add registration form" — один коміт з усіма змінами. Це простіше, але втрачається історія feature-гілки.

Через git reset --soft: для об'єднання останніх N комітів виконайте git reset --soft HEAD~N. Всі зміни з N комітів зберуться в індексі. Потім git commit -m "нове повідомлення" — один коміт замість N. Швидко, але не дає вибрати, які коміти об'єднувати.

Після squash на вже запушеній гілці: git push --force-with-lease. Історія переписана, тому потрібен force-push. Попередьте команду, якщо гілка спільна.

Гарна практика: робіть squash перед merge feature-гілки в main. Це дає чисту історію main, де кожен коміт — одна завершена функція, а не серія "wip" і "fix typo".

Як відновити видалений коміт або гілку в Git?

В Git майже нічого не видаляється безповоротно. Навіть після reset --hard або видалення гілки, коміти залишаються в reflog і можуть бути відновлені протягом 90 днів.

git reflog — головний інструмент відновлення. Reflog записує кожне переміщення HEAD: коміти, checkout, reset, pull. Виконайте git reflog — побачите історію з хешами комітів і описами дій. Знайдіть хеш коміту до видалення. Запис виду "a1b2c3d HEAD@{2}: reset: moving to HEAD~1" означає, що на HEAD@{3} був коміт, який ви скинули.

Відновлення коміту: git reset --hard <hash> переміщує поточну гілку до вказаного коміту. Якщо не хочете переписувати поточну гілку, створіть нову: git branch recovered <hash>, потім git checkout recovered.

Відновлення видаленої гілки: git reflog покаже останній коміт гілки. Знайдіть запис виду "abc1234 branchname@{1}: commit: last commit". Виконайте git branch branchname <hash> — гілка відновиться з цим комітом як HEAD.

Відновлення після git reset --hard: якщо ви випадково зробили reset --hard і втратили зміни, git reflog покаже попередній стан HEAD. Знайдіть хеш до reset і виконайте git reset --hard <hash>. Зміни повернуться.

Відновлення після force-push: якщо хтось зробив force-push і затер ваші коміти в remote, коміти все ще є у вашому локальному reflog. Знайдіть хеш через git reflog, створіть гілку git branch backup <hash>, потім git push origin backup. Коміти знову в remote.

Відновлення uncommitted змін: якщо ви зробили git stash drop і втратили stash, git fsck --no-reflog --unreachable покаже dangling-об'єкти. Знайдіть commit-об'єкт зі змінами вашого stash: git fsck --no-reflog --unreachable | grep commit. Потім git stash apply <hash> відновить stash.

Терміни: reflog зберігає записи 90 днів для reachable-комітів і 30 днів для unreachable. Після цього Git видаляє об'єкти через git gc. Якщо минуло більше 90 днів — відновлення неможливе. Тому перевіряйте reflog одразу після випадкового видалення.

Профілактика: перед reset --hard або force-push створюйте backup-гілку: git branch backup-$(date +%s). Це займає секунду і рятує від втрати роботи.

Як налаштувати git hooks для автоматизації?

Git hooks — скрипти, які запускаються автоматично при певних подіях Git. Вони допомагають автоматизувати перевірки коду, форматування та тести.

Де зберігаються hooks: у кожному репозиторії є папка .git/hooks з прикладами hooks з розширенням .sample. Щоб активувати hook, видаліть розширення .sample і зробіть файл виконуваним: chmod +x .git/hooks/pre-commit. Hooks не версіонуються — вони локальні для кожного розробника. Для спільних hooks використовуйте Husky або подібні інструменти.

Client-side hooks: pre-commit запускається перед комітом. Якщо повертає ненульовий код — коміт скасовується. Використовуйте для лінтингу, форматування, перевірки секретів. prepare-commit-msg — перед відкриттям редактора повідомлення, можна генерувати шаблон. pre-push — перед push, можна запускати тести.

Server-side hooks: pre-receive, update, post-receive — на сервері (GitHub, GitLab). Використовуються для перевірки прав, відхилення push при порушенні правил. На GitHub налаштовуються через GitHub Actions або Branch Protection Rules.

Husky — популярний інструмент для спільних client-side hooks. Установка: npm install --save-dev husky, npx husky init. Це створює папку .husky/ з hooks, які версіонуються в репозиторії. Всі розробники отримують однакові hooks автоматично. Приклад .husky/pre-commit: npx lint-staged — запускає лінтер лише для staged-файлів.

lint-staged — companion для Husky. Конфігурація в package.json: "lint-staged": {"*.js": "eslint --fix", "*.ts": "tsc --noEmit", "*.css": "prettier --write"}. Запускає інструменти лише для файлів в індексі, а не для всього проєкту — швидко.

pre-commit framework — альтернатива Husky, мультиязична. Установка: pip install pre-commit, pre-commit install. Конфігурація в .pre-commit-config.yaml. Підтримує hooks на Python, Node, Go, Rust. Має вбудовані hooks: trailing-whitespace, end-of-file-fixer, check-yaml.

Практичний pre-commit hook: перевірка відсутності console.log, перевірка форматування, перевірка секретів (git-secrets, trufflehog), запуск unit-тестів для зачеплених файлів. Якщо hook падає — коміт скасовується, розробник бачить помилки і виправляє.

Порада: не робіть hooks занадто повільними. pre-commit має виконуватися за секунди, інакше розробники будуть його обходити (--no-verify). Важкі перевірки (інтеграційні тести, повний аналіз) — в CI/CD, не в hooks.