-
1. Вступ
- 1.1 Про систему контролю версій
- 1.2 Коротка історія Git
- 1.3 Основи Git
- 1.4 Git, зазвичай, тільки додає дані
- 1.5 Три стани
- 1.6 Командний рядок
- 1.7 Інсталяція Git
- 1.8 Початкове налаштування Git
- 1.9 Отримання допомоги
- 1.10 Підсумок
-
2. Основи Git
- 2.1 Створення Git-репозиторія
- 2.2 Запис змін до репозиторія
- 2.3 Перегляд історії комітів
- 2.4 Скасування речей
- 2.5 Взаємодія з віддаленими сховищами
- 2.6 Теґування
- 2.7 Псевдоніми Git
- 2.8 Підсумок
-
3. Галуження в git
- 3.1 Гілки у кількох словах
- 3.2 Основи галуження та зливання
- 3.3 Управління гілками
- 3.4 Процеси роботи з гілками
- 3.5 Віддалені гілки
- 3.6 Перебазовування
- 3.7 Підсумок
-
4. Git на сервері
- 4.1 Протоколи
- 4.2 Отримання Git на сервері
- 4.3 Генерація вашого публічного ключа SSH
- 4.4 Налаштування Серверу
- 4.5 Демон Git
- 4.6 Розумний HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Варіанти стороннього хостингу
- 4.10 Підсумок
-
5. Розподілений Git
-
6. GitHub
-
7. Інструменти Git
- 7.1 Вибір ревізій
- 7.2 Інтерактивне індексування
- 7.3 Ховання та чищення
- 7.4 Підписання праці
- 7.5 Пошук
- 7.6 Переписування історії
- 7.7 Усвідомлення скидання (reset)
- 7.8 Складне злиття
- 7.9 Rerere
- 7.10 Зневадження з Git
- 7.11 Підмодулі
- 7.12 Пакування
- 7.13 Заміна
- 7.14 Збереження посвідчення (credential)
- 7.15 Підсумок
-
8. Налаштування Git
-
9. Git and Other Systems
- 9.1 Git як клієнт
- 9.2 Міграція на Git
- 9.3 Підсумок
-
10. Git зсередини
- 10.1 Кухонні та парадні команди
- 10.2 Об’єкти Git
- 10.3 Посилання Git
- 10.4 Файли пакунки
- 10.5 Специфікація посилань (refspec)
- 10.6 Протоколи передачі
- 10.7 Супроводження та відновлення даних
- 10.8 Змінні середовища
- 10.9 Підсумок
-
A1. Додаток A: Git в інших середовищах
- A1.1 Графічні інтерфейси
- A1.2 Git у Visual Studio
- A1.3 Git в Eclipse
- A1.4 Git у Bash
- A1.5 Git у Zsh
- A1.6 Git у Powershell
- A1.7 Підсумок
-
A2. Додаток B: Вбудовування Git у ваші застосунки
- A2.1 Git з командного рядка
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
-
A3. Додаток C: Команди Git
- A3.1 Налаштування та конфігурація
- A3.2 Отримання та створення проектів
- A3.3 Базове збереження відбитків
- A3.4 Галуження та зливання
- A3.5 Поширення й оновлення проектів
- A3.6 Огляд та порівняння
- A3.7 Зневаджування
- A3.8 Латання (patching)
- A3.9 Електронна пошта
- A3.10 Зовнішні системи
- A3.11 Адміністрування
- A3.12 Кухонні команди
7.13 Інструменти Git - Заміна
Заміна
Як ми вже наголошували, обʼєкти в базі даних Git незмінні, проте Git надає цікавий засіб вдавати заміну обʼєктів своєї бази на інші.
Команда replace
дозволяє задати обʼєкт у Git та сказати "щоразу як ти звертаєшся до цього обʼєкту, вдай нібито це _інший обʼєкт".
Це найбільш корисно для заміни одного коміту вашої історії на інший без потреби переписувати всю історію за допомогою, наприклад, git filter-branch
.
Наприклад, скажімо ви маєте неосяжну історію коду та бажаєте розділити репозиторій на один з короткою історією для нових розробників та один з набагато довшою та більшою історією для людей, що зацікавлені у видобуванні інформації. Ви можете прищепити одну історію до іншої, замінюючи (replaceing) найдавніший коміт у новій лінії останнім комітом старої. Це зручно, адже означає, що вам нема потреби насправді переписувати кожен коміт нової історії, як вам зазвичай доводиться робити, щоб поєднати їх разом (адже батьківство впливає на SHA-1 суми).
Спробуймо це.
Візьмімо існуючий репозиторій, розділимо його на два, один новітній, інший історичний, та потім побачимо, як можна воззʼєднати їх без редагування значень SHA-1 новітнього репозиторія за допомогою replace
.
Ми використаємо простий репозиторій з пʼятьма простими комітами:
$ git log --oneline
ef989d8 fifth commit
c6e1e95 fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Ми бажаємо розбити це на дві лінії історії. Одна буде з першого до четвертого коміту - це буде історичною. Друга лінія буде містити лише четвертий та пʼятий коміти - це буде новітня історія.
![replace1](png/replace1.png)
Ну, створити давню історію просто: треба лише покласти гілку в історію, а потім надіслати цю гілку до гілки master нового віддаленого репозиторія.
$ git branch history c6e1e95
$ git log --oneline --decorate
ef989d8 (HEAD, master) fifth commit
c6e1e95 (history) fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
![replace2](png/replace2.png)
Тепер ми можемо надіслати нову гілку history
до гілки master
нового репозиторія:
$ git remote add project-history https://github.com/schacon/project-history
$ git push project-history history:master
Counting objects: 12, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (12/12), 907 bytes, done.
Total 12 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (12/12), done.
To git@github.com:schacon/project-history.git
* [new branch] history -> master
Добре, отже наша історія опублікована. Тепер складніше завдання — зрізати нашу новітню історію, щоб зменшити її. Нам потрібне перекривання, щоб ми могли замінити коміт одного репозиторія еквівалентним комітом з іншого, отже ми збираємося зрізати до четвертого та пʼятого комітів (і четвертий коміт перекривається).
$ git log --oneline --decorate
ef989d8 (HEAD, master) fifth commit
c6e1e95 (history) fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
У даному випадку корисно створити базовий коміт, що має інструкції щодо розширення історії, щоб інші розробники знали, що треба робити, якщо наштовхнуться на перший коміт зрізаної історії, а потребують більшого. Отже, ми збираємось створити обʼєкт первинного коміту в якості нашої базової точки з інструкціями, потім перебазуємо решту комітів (четвертий та пʼятий) поверх нього.
Щоб це зробити, нам треба вибрати точку розриву, у даному випадку це третій коміт, тобто 9c68fdc
словами SHA.
Отже, наш базовий коміт буде засновано на цьому дереві.
Ми можемо створити наш базовий коміт за допомогою команди commit-tree
, яка просто бере дерево та поверне нам SHA-1 новісінького коміту без батьків.
$ echo 'get history from blah blah blah' | git commit-tree 9c68fdc^{tree}
622e88e9cbfbacfb75b5279245b9fb38dfea10cf
Зауваження
|
Команда |
![replace3](png/replace3.png)
Добре, отже тепер, коли в нас є базовий коміт, ми можемо перебазувати решту нашої історії поверх нього за допомогою git rebase --onto
.
Параметр --onto
треба встановити у SHA-1 суму, щойно отриману від commit-tree
, а місцем перебазування буде третій коміт (батько першого коміту, який треба зберегти, 9c68fdc
):
$ git rebase --onto 622e88 9c68fdc
First, rewinding head to replay your work on top of it...
Applying: fourth commit
Applying: fifth commit
![replace4](png/replace4.png)
Добре, отже тепер ми переписали нашу новітню історію поверх технічного базового коміту, який тепер містить інструкції щодо відновлення повної історії, якщо комусь треба. Тепер ми можемо надіслати цю історію до нового проекту, та тепер при клонуванні цього репозиторія, вони побачать лише останні два коміти, а потім базовий коміт з інструкціями.
Поміняймося роллю з кимось, хто клонує проект вперше, та потребує повної історії. Щоб отримати дані історії після клонування зрізаного репозиторія, потрібно додати друге віддалене сховище — історичний репозиторій, та отримати з нього зміни:
$ git clone https://github.com/schacon/project
$ cd project
$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
622e88e get history from blah blah blah
$ git remote add project-history https://github.com/schacon/project-history
$ git fetch project-history
From https://github.com/schacon/project-history
* [new branch] master -> project-history/master
Тепер співробітники матимуть нові коміти в гілці master
, а історичні коміти у гілці project-history/master
.
$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
622e88e get history from blah blah blah
$ git log --oneline project-history/master
c6e1e95 fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Щоб їх обʼєднати, можна просто викликати git replace
з комітом, який ви хочете замінити, та комітом, яким ви бажаєте його замінити.
Отже, ми хочемо замінити "четвертий" коміт з гілки master "четвертим" комітом з гілки project-history/master
:
$ git replace 81a708d c6e1e95
Тепер, якщо подивитись на історію гілки master
, виявляється, вона матиме такий вигляд:
$ git log --oneline master
e146b5f fifth commit
81a708d fourth commit
9c68fdc third commit
945704c second commit
c1822cf first commit
Вражає, еге ж? Без необхідності змінювати всі SHA-1 суми першоджерела, ми впорались замінити один коміт історії геть іншим, і всі звичайні інструменти (bisect
, blame
тощо) працюватимуть, як ми й очікуємо.
![replace5](png/replace5.png)
Що цікаво, тут досі зазначено 81a708d
як SHA-1, хоч насправді використовуються дані коміту c6e1e95
, яким ми його замінили.
Навіть якщо виконати таку команду, як cat-file
, вона все одно покаже замінені дані:
$ git cat-file -p 81a708d
tree 7bc544cf438903b65ca9104a1e30345eee6c083d
parent 9c68fdceee073230f19ebb8b5e7fc71b479c0252
author Scott Chacon <schacon@gmail.com> 1268712581 -0700
committer Scott Chacon <schacon@gmail.com> 1268712581 -0700
fourth commit
Памʼятайте, що справжній батько коміту 81a708d
був наш коміт-заповнювач (622e88e
), а не 9c68fdce
, як тут зазначено.
Ще одна цікава річ: відомості про це зберігаються в посиланнях:
$ git for-each-ref
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/heads/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit refs/remotes/history/master
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/remotes/origin/HEAD
e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit refs/remotes/origin/master
c6e1e95051d41771a649f3145423f8809d1a74d4 commit refs/replace/81a708dd0e167a3f691541c7a6463343bc457040
Це означає, що поділитися нашою заміною з іншими просто, адже ми можемо надіслати це посилання до сервера, а інші можуть легко завантажити його. Це не дуже корисно в розглянутому тут сценарії з прищепленням історії (адже тоді всім доведеться завантажувати обидві історії, то ж яка в тому користь?), проте це може бути корисним за інших обставин.