Skip to content

Git

Git

Що таке Git? Основні поняття

Git - це система керування версіями, призначена для відстеження змін у коді проекту, спільної роботи розробників та збереження історії змін. Вона дозволяє зберігати різні версії коду та керувати ними.

Основні функції

  • Історія змін: Зберігає повну історію змін проекту, включаючи коментарі до коммітів, авторів та тимчасові мітки змін. Це забезпечує прозорість та можливість відновлення попередніх станів проекту.
  • Розгалуження та злиття: Підтримує розгалуження, що дозволяє розробникам створювати окремі гілки для роботи над певними функціями чи завданнями. Після завершення роботи гілки можуть бути об'єднані назад у основну гілку (main).
  • Розподілена система: Кожен розробник працює з повною копією репозиторію, що робить його розподіленою системою керування версіями. Це означає, що розробники можуть працювати незалежно один від одного і навіть офлайн.

Локальний репозиторій - це копія проекту, яка зберігається на локальному комп'ютері. Віддалений репозиторій - це копія проекту, яка знаходиться на сервері, і з якою розробники можуть спільно працювати.

Коміт - це зафіксовані зміни у коді, які додаються до історії проекту. Кожен коміт має повідомлення, яке пояснює, що саме було змінено.

Гілка - це окрема гілка розвитку коду, яка дозволяє працювати над різним функціоналом паралельно без впливу на основний код.

Pull-request / merge-request - це механізм в Git, який дозволяє розробникам пропонувати зміни з своєї гілки до головної гілки проекту. Інші розробники можуть переглянути та обговорити ці зміни перед їх об'єднанням.

Злиття (merge) - це процес об'єднання змін із однієї гілки в іншу, зазвичай для того, щоб включити новий код до головної гілки.

Ребейз (rebase) - це процес перенесення всіх змін з однієї гілки на іншу, зазвичай для того, щоб зробити історію комітів більш зрозумілою та лінійною.

Ребейз (rebase) відрізняється від злиття (merge) тим, що він переписує історію комітів так, що вона виглядає більш лінійно і послідовно, але може бути складнішим для використання в спільній роботі.

Пул (pull) - це команда, яка витягує зміни з віддаленого репозиторію і об'єднує їх з вашим локальним репозиторієм.

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

Основні команди: clone (клонувати), commit (зробити коміт), push (завантажити зміни на віддалений репозиторій), pull (отримати зміни з віддаленого репозиторію), merge (злити гілки), rebase (перебудувати історію комітів).

Які основні етапи життєвого циклу файлу в Git (untracked, staged, committed)?

Основні етапи життєвого циклу файлу в Git

  • Untracked (Не відслідковується) - коли файл створено у робочій директорії, він знаходиться в стані untracked, тобто Git не відслідковує зміни в цьому файлі.
  • Staged (Підготовлений) - щоб додати файл до індексу Git, його потрібно підготувати, використовуючи команду git add. Після цього файл переходити в стан staged.
  • Committed (Зафіксований) - після того, як файли підготовлені та додані в індекс, їх можна зафіксувати у репозиторії за допомогою команди git commit. Файл переходити до табору committed, і зміни зберігаються в історії репозиторію.
  • Modified (Змінений) - якщо файл був змінений після того, як він був зафіксований, то він переходити в стан modified. Зміни не будуть збережені в репозиторії, доки файл не буде знову підготовлений і зафіксований.
  • Unmodified (Не змінений) - якщо файл не був змінений після останнього зафіксованого комміту, він знаходиться в стані unmodified.
  • Deleted (Видалений) - якщо файл був видалений з робочої директорії, він переходити в стан deleted. Щоб видалити файл з репозиторію, його потрібно видалити з індексу за допомогою команди git rm, а потім зафіксувати зміни.

Основні команди для роботи з репозиторієм

  • git init - Ініціалізація нового репозиторію у поточній директорії
  • git clone- клонування репозиторію - потрібно виконати команду git clone і вказати URL-репозиторію
git clone https://github.com/username/repository.git
  • git add - додавання змін - щоб додати зміни до індексу, потрібно виконати команду із зазначенням шляхів до змінених файлів
git add file1.txt file2.txt
  • git commit - створити коміт із зафіксованими змінами - виконати команду з повідомленням опису змін
git commit -m "Added Readme"
  • git status - показати стан робочої директорії та індексу
  • git fetch - витягує зміни з віддаленого репозиторію, але не зливає їх з локальною віткою.
  • git pull - витягує зміни з віддаленого репозиторію і автоматично зливає їх з поточною віткою. Це комбінація команд git fetch і git merge
  • git push - відправка локальних змін у віддалений репозиторій - щоб відправити зміни на віддалений репозиторій, потрібно виконати команду з зазначенням імені віддаленого репозиторію та гілки
git push origin main  # main - branch name, origin - repository name
  • git branch [branch name] - створює нову вітку або виводить список існуючих віток
  • git checkout [branch name or commit] - переключає робочу директорію на вказану вітку або комміт
  • git merge [branch name] - зливає вказану вітку з поточною віткою. Використовується для інтеграції змін з однієї вітки в іншу
  • git log - відображає історію комітів для поточної вітки, включаючи повідомлення комітів, авторів і дати
  • git stash - приймає поточний стан робочого каталогу та індексу, поміщає їх у стек на майбутнє та повертає чистий робочий каталог. Наприклад, якщо в процесі роботи над фічею в гілці X потрібно терміново перейти до іншої гілки Y, і в той же час щоб не втратити поточні зміни X, можна використовувати команду git stash, щоб тимчасово відкласти незакомічні зміни в схованку (stash). Щоб отримати останні зміни назад - git stash pop

Що таке Git Rebase ?

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

Git працює з комітами. Кожен коміт — набір змін. У кожного коміта є унікальний хеш. Коли відбувається об'єднання гілок за допомогою git merge, то всі коміти зберігаються — зберігаються коментарі коміта, його хеш + зазвичай додається ще один штучний merge коміт.

Використання операції rebase дозволяє тримати історію зрозумілішою і лінійною, та поміщати зміни в кінці головної вітки.

before rebase

main:     o---o---o 
           \ 
feature:    o---o

after `git rebase main`

main:  o---o---o---o---o

Також rebase можна застосувати, щоб відредагувати історію комітів за допомогою інтерактивного режиму

git rebase -i {HEAD~number of commits|commit hash}`

git rebase -i запустить редактор і запропонує відредагувати коміти (порядок слідування комітів — зверху вниз на відміну від git log). Можна залишити коміт як є, можна змінити коментар, можна склеїти з попереднім (pick "commit_hash" "comment"fixup "commit_hash" "comment")

При цьому всі коментарі, які були в fixup комітах, зникнуть і буде використаний коментар з першого коміта. Якщо коментарі важливі, то варто використовувати squash замість fixup.

Після rebase push можливий тільки з опцією -f, через те що ми змінюємо історію комітів, і git нас про попереджає.

git push -f

Не можна використовувати rebase на вітках, з якими працюють інші розробники, оскільки це призведе до конфліктів в історії комітів.

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

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

Особливості git merge

  • Збереження історії розгалужень. git merge додає зміни з однієї гілки до іншої зі збереженням "розгалуженої" історії. Це означає, що всі комміти з обох гілок будуть видні в їхньому початковому порядку.
  • Створення комміту злиття. git merge створює окремий комміт для об'єднання, так званий "merge commit", який відзначає момент злиття двох гілок.
  • Зручно для роботи в команді. Історія коммітів залишається точною та відображає, хто і коли додавав зміни.

Приклад історії після git merge

main: o---o---o---o---o
               \     /
feature:        o---o

Особливості git rebase

  • Лінійна історія. git rebase переписує комміти однієї гілки поверх іншої, прибираючи "розгалуження". Це робить історію проекту більш лінійною та простою для сприйняття.
  • Немає окремого комміту злиття. Після git rebase не створюється додаткового коміту для об'єднання. Коміти з вашої гілки будуть "переміщені" у хронологічному порядку поверх основної гілки.
  • Корисно для спрощеної історії. git rebase дозволяє створити акуратну, лінійну історію проекту, що може бути корисним при роботі над функціями або при підготовці до відправки змін до загального репозиторію.

Приклад історії після git rebase

main: o---o---o---o---o

Коли використовувати

  • git merge варто застосовувати, якщо треба зберегти повну історію, що відображає всі розгалуження та злиття. Це корисно в проектах з багатьма учасниками, де важливо бачити, як розвивалася робота
  • git rebase буде зручним, якщо важлива чистота історії, і треба бачити її лінійною. Наприклад, під час підготовки до відправки змін до основний репозиторій, щоб уникнути зайвих коммітів злиття.

Що таке pull request (merge request)?

Pull request (merge request) - це запит на злиття змін з однієї гілки в іншу в системах контролю версій, таких як Git. Pull request дозволяє розробникам обговорювати і переглядати зміни перед їх включенням у основну гілку проєкту.

Коли розробник завершує роботу над новою функцією або виправленням, через веб-інтерфейс GitHub створюється pull request, де вказується основна гілка для злиття і додається опис змін. Це дозволяє іншим учасникам команди переглянути код, залишити коментарі та запропонувати покращення до того, як зміни будуть об'єднані в основну гілку проекту. Pull request також дозволяє автоматично запускати тести забезпечуючи додаткову перевірку коректності змін. Після того, як всі зауваження будуть враховані та зміни схвалені, pull request може бути злитий з основною гілкою.

Що таке Git Flow

Git Flow – це підхід управління релізами та вітками у Git, призначена для роботи над проектами та керування їх версіями. Пропонує стандартизований підхід до розгалуження та злиття.

Основні типи гілок

  • master/main: Містить код продакшн-версії. Усі релізи починаються і завершуються у цій гілці.
  • develop/dev: Розробка, яка містить останні зміни коду для наступного релізу. Усі фічі та виправлення спочатку зливаються сюди.

Допоміжні типи гілок

  • feature/feat: Розробка нових функцій. Кожна нова функція створюється в окремій гілці feature і зливається назад в develop.
  • release: Підготовка нових продуктових релізів. Вони дозволяють останні підгонки та виправлення перед злиттям гілки release у master та develop. Відгалужуються від develop і об'єднуються до master та develop після релізу.
  • hotfix: Для негайного виправлення помилок у продакшн-версії коду. Hotfix-гілки створюються від master і після виправлення зливаються назад в master і develop.

Принцип роботи Git Flow

  • Замість використання лише однієї гілки main, використовуються дві гілки для відстеження історії проекту. Гілка main зберігає офіційну історію релізів, у гілці develop ведеться активна розробка. Також важливо тегувати коміти в гілці main номерами версій.
  • Розробка нових функцій починається з створення feature гілки. Кожна функція розробляється в окремій гілці, яка базується на develop. Коли розробка функції завершена, вона зливається з develop. Функції ніколи не повинні зливатися безпосередньо з гілкою main.
  • Як тільки гілка develop набере достатньо функцій для релізу (або на заздалегідь визначену дату), створюється release гілка. Це дозволяє провести фінальні перевірки та виправлення перед випуском. Створення цієї гілки починає новий цикл релізів, тому в цю гілку вже не повинно потрапляти жодних нових функцій - лише виправлення помилок, генерація документації та інші завдання, спрямовані на реліз.
  • Як тільки гілка релізу готова, її зливають з main та тегують новою версією. Також її повинні злити з гілкою develop, яка, ймовірно, вже рушила вперед від моменту релізу. Використання окремої гілки для релізу дозволяє одній команді підготувати реліз, тоді як інша команда продовжуватиме розробку нових функцій в develop для наступних релізів.
  • Якщо виникає критична помилка, створюється hotfix гілка від main. Це єдина гілка, яка створюється з main. Після виправлення зміни інтегруються як у main, так і у develop. Це дозволяє виправляти помилки, не перериваючи весь робочий процес або не чекаючи наступного релізу для внесення виправлень.

Links

Що таке сherry pick в Git?

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

git cherry-pick <coomit hash>

Якщо виникають конфлікти, їх необхідно вирішити вручну, після чого завершити процес командою git cherry-pick --continue.

Cherry-picking особливо корисний у сценаріях, коли є необхідність швидко впровадити виправлення з основної гілки в релізну гілку, або перенести окрему функцію з однієї галузі розробки в іншу, без включення всіх проміжних змін.

Що таке force push

Якщо виправити деякі старі коміти в історії git, наприклад: виправити ім'я автора або e-mail, або відмінили останній коміт або скористалися amend або revert, то під час спроби push-а git відмовиться приймати зміни.

Щоб все ж таки запушити зміни, нам потрібно виконати або git push --force origin <ім'я_гілки>

Але у цьому випадку є ризик перетерти чиїсь зміни, якщо з того часу, як забирали зміни з сервера, хтось встиг запушити свої коміти. Тому краще використовувати більш безпечну команду: git push --force-with-lease origin <ім'я_гілки>

Такий варіант краще тим, що якщо хтось встиг запушити свої коміти після того, як забирали зміни з сервера, то він не буде їх перетирати, а видаватиме помилку, після чого можна інтегрувати чужі коміти з локальними змінами і спробувати зробити push --force-with-lease ще раз.

Що таке pre-commit check

Як і в багатьох інших системах контролю версій, в Git є можливість запускати власні сценарії у ті моменти, коли відбуваються деякі важливі дії. Існують дві групи подібних перехоплювачів (hook): на стороні клієнта і на стороні сервера. Перехоплювачі на стороні клієнта призначені для клієнтських операцій, таких як створення коміту та злиття. Перехоплювачі на стороні сервера потрібні для серверних операцій, таких як прийом відправлених комітів. Перехоплювачі можуть бути використані для виконання різноманітних завдань. Про деякі з таких завдань ми і поговоримо.

Pre-commit check можна використовувати наприклад так

  • виконувати перевірку коду на валідність (наприклад: відповідність вимогам PEP8, наявність документації і т.д.);
  • виконувати комплексну перевірку проекту (юніт-тести і т.д.);
  • переривати операцію commit'а у разі виявлення помилок і відображати докладний журнал для розбору подій.

Для чого використовується .gitignore?

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