3 Внаслідок розподіленої природи Git, історію змін можна легко редагувати. Однак, якщо ви втручаєтеся в минуле, будьте обережні: змінюйте тільки ту частину історії, якою володієте ви і тільки ви. Інакше, як народи вічно з'ясовують, хто ж саме зробив і які безчинства, так і у вас будуть проблеми з примиренням при спробі поєднати різні дерева історії.
5 Деякі розробники переконані, що історія повинна бути незмінна з усіма огріхами та іншим. Інші вважають, що дерева потрібно робити презентабельними перед випуском їх у публічний доступ.
6 Git враховує обидві думки. Переписування історії, як і клонування, розгалуження і злиття, — лише ще одна можливість, яку дає вам Git. Розумне її використання залежить тільки від вас.
8 === Залишаючись коректним ===
10 Щойно зробили комміт і зрозуміли, що повинні були ввести інший опис? Запустіть
14 щоб змінити останній опис. Усвідомили, що забули додати файл? Запустіть *git add*, щоб це зробити, потім виконайте вищевказану команду.
16 Захотілося додати ще трохи змін в останній комміт? Так зробіть їх і запустіть
18 $ git commit --amend -a
22 Давайте уявимо, що попередня проблема насправді в десять разів гірше. Після тривалої роботи ви зробили ряд коммітів, але ви не дуже-то задоволені тим, як вони організовані, і деякі описи коммітів треба б злегка переформулювати. Тоді запустіть
24 $ git rebase -i HEAD~10
26 і останні десять коммітів з’являться у вашому улюбленому редакторі (задається змінною оточення $EDITOR). Наприклад:
28 pick 5c6eb73 Додав посилання repo.or.cz
29 pick a311a64 Переставив аналогії в „Працюй як хочеш“
30 pick 100834f Додав ціль для push в Makefile
32 Старі комміти передують новим коммітам у цьому списку, на відміну від команди `log`.
33 Тут, 5c6eb73 є найстарішим коммітом і 100834f є найновішим. Тепер ви можете:
35 - Видаляти комміти шляхом видалення рядків. Подібно команді revert, але видаляє запис: це буде так ніби комміта ніколи не існувало.
36 - Міняти порядок коммітів, переставляючи рядки.
38 * `edit`, щоб позначати комміт для внесення правок;
39 * `reword`, щоб змінити опис у журналі;
40 * `squash`, щоб злити комміт з попереднім;
41 * `fixup`, щоб злити комміт з попереднім і відкинути його опис.
43 Наприклад, ми могли б замінити другий `pick` з `squash`:
45 pick 5c6eb73 Додав посилання repo.or.cz
46 squash a311a64 Переставив аналогії в „Працюй як хочеш“
47 pick 100834f Додав ціль для push в Makefile
49 Після того, як ми збережемо і вийдемо, Git зіллє a311a64 у 5c6eb73. Так *squash* зливає
50 у наступний комміт вгорі: думайте ``squash up''.
52 Тоді Git об’єднує повідомлення журналу і подає їх для редагування. Команда
53 *fixup* пропускає цей етап; злиті повідомлення журналу просто відкидаються.
55 Якщо ви позначили комміт командою *edit*, Git поверне вас в минуле, до найстарішого такого комміта.
56 Ви можете відкоректувати старий комміт як описано в попередньому параграфі і, навіть, створити нові комміти, які знаходяться тут.
57 Як тільки ви будете задоволені ``retcon'', йдіть вперед у часі, виконавши:
59 $ git rebase --continue
61 Git виводить комміти до наступного *edit* або до поточного, якщо не залишиться нічого.
63 Ви також можете відмовитися від перебазування (rebase) з:
67 Одним словом, робіть комміти раніше і частіше — ви завжди зможете навести порядок за допомогою rebase.
69 === Локальні зміни зберігаються ===
71 Припустимо, ви працюєте над активним проектом. За якийсь час ви робите кілька коммітів, потім синхронізуєте з офіційним деревом через злиття. Цикл повторюється кілька разів, поки ви не будете готові влити зміни в центральне дерево.
73 Проте тепер історія змін в локальному клоні Git являє собою кашу з ваших та офіційних змін. Вам би хотілося бачити всі свої зміни неперервною лінією, а потім — всі офіційні зміни.
75 Це робота для команди *git rebase*, як описано вище. Найчастіше, має сенс використовувати опцію *--onto*, щоб прибрати переплетення.
77 Також дивіться *git help rebase* для отримання детальних прикладів використання цієї чудової команди. Ви можете розщеплювати комміти. Ви можете навіть змінювати порядок гілок у дереві.
79 Будьте обережні: rebase — це потужна команда. Для складних rebases, спочатку зробіть резервну копію за допомогою *git clone*.
81 === Переписуючи історію ===
83 Іноді вам може знадобитися в системі керування версіями аналог «замазування» людей на офіційних фотографіях, як би стираючого їх з історії в дусі сталінізму. Наприклад, припустимо, що ми вже збираємося випустити реліз проекту, але він містить файл, який не повинен стати надбанням громадськості з якихось причин. Можливо, я зберіг номер своєї кредитки в текстовий файл і випадково додав його в проект. Видалити файл недостатньо: він може бути доступним зі старих коммітів. Нам треба видалити файл з усіх ревізій:
85 $ git filter-branch --tree-filter 'rm цілком/таємний/файл' HEAD
87 Дивіться *git help filter-branch*, де обговорюється цей приклад і пропонується більш швидкий спосіб вирішення. Взагалі, *filter-branch* дозволяє змінювати істотні частини історії за допомогою однієї-єдиної команди.
89 Після цієї команди каталог +.git/refs/original+ буде описувати стан, який був до її виклику. Переконайтеся, що команда filter-branch зробила те, що ви хотіли, і якщо хочете знову використовувати цю команду, видаліть цей каталог.
91 І, нарешті, замініть клони вашого проекту виправленою версією, якщо збираєтеся надалі з ними працювати.
93 === Створюючи історію ===
96 Хочете перевести проект під управління Git? Якщо зараз він знаходиться під управлінням якоїсь із добре відомих систем керування версіями, то цілком імовірно, що хтось вже написав необхідні скрипти для експорту всієї історії проекту в Git.
98 Якщо ні, то дивіться в сторону команди *git fast-import*, яка зчитує текст в спеціальному форматі для створення історії Git з нуля. Зазвичай скрипт, який використовує цю команду, буває зліплений похапцем для одиничного запуску, що переносить весь проект за один раз.
100 В якості прикладу вставте такі рядки в тимчасовий файл, на зразок `/tmp/history`:
101 ----------------------------------
102 commit refs/heads/master
103 committer Alice <alice@example.com> Thu, 01 Jan 1970 00:00:00 +0000
108 M 100644 inline hello.c
113 printf("Hello, world!\n");
118 commit refs/heads/master
119 committer Bob <bob@example.com> Tue, 14 Mar 2000 01:59:26 -0800
121 Замінений printf() на write()
124 M 100644 inline hello.c
129 write(1, "Hello, world!\n", 14);
134 ----------------------------------
136 Потім створіть сховище Git з цього тимчасового файлу за допомогою команд:
138 $ mkdir project; cd project; git init
139 $ git fast-import --date-format=rfc2822 < /tmp/history
141 Ви можете витягти останню версію проекту за допомогою
143 $ git checkout master .
145 Команда *git fast-export* перетворює будь-яке сховище в формат, зрозумілий для команди *git fast-import*. Її результат можна використовувати як зразок для написання скриптів перетворення або для перенесення сховищ в зрозумілому для людини форматі. Звичайно, за допомогою цих команд можна пересилати сховища текстових файлів через канали передачі тексту.
147 === Коли ж все пішло не так? ===
149 Ви тільки що виявили, що деякий функціонал вашої програми не працює, але ви досить чітко пам’ятаєте, що він працював лише кілька місяців тому. Ох ... Звідки ж взялася помилка? Ви ж це перевіряли відразу як розробили.
151 У будь-якому випадку, вже надто пізно. Однак, якщо ви фіксували свої зміни досить часто, то Git зможе точно вказати проблему:
154 $ git bisect bad HEAD
155 $ git bisect good 1b6d
157 Git витягне стан рівно посередині. Перевірте чи працює те, що зламалося, і якщо все ще ні:
161 Якщо ж працює, то замініть "bad" на "good". Git знову перемістить вас в стан посередині між хорошою і поганою версіями, звужуючи коло пошуку. Після декількох ітерацій, цей двійковий пошук приведе вас до того комміту, на якому виникла проблема. Після закінчення розслідування, поверніться у початковий стан командою
165 Замість ручного тестування кожної зміни автоматизуйте пошук, запустивши
167 $ git bisect run my_script
169 За поверненим значенням заданої команди, зазвичай одноразового скрипта, Git буде відрізняти хороший стан від поганого. Скрипт повинен повернути 0, якщо теперішній комміт хороший; 125, якщо його треба пропустити, і будь-яке інше число від 1 до 127, якщо він поганий. Від'ємне значення перериває команду bisect.
171 Ви можете зробити багато більше: сторінка допомоги пояснює, як візуалізувати bisect, проаналізувати чи відтворити її журнал, або виключити наперед відомі хороші зміни для прискорення пошуку.
173 === Через кого все пішло не так? ===
175 Як і в багатьох інших системах керування версіями, в Git є команда blame:
179 Вона забезпечує кожен рядок вибраного файлу примітками, що розкривають, хто і коли останнім його редагував. На відміну ж від багатьох інших систем керування версіями, ця операція відбувається без з’єднання з мережею, вибираючи дані з локального диску.
181 === Особистий досвід ===
183 У централізованих системах керування версіями зміни історії — досить складна операція, і доступна вона лише адміністраторам. Клонування, розгалуження і злиття неможливі без взаємодії по мережі. Так само йдуть справи і з базовими операціями, такими як перегляд історії або фіксація змін. У деяких системах мережеве з’єднання потрібне навіть для перегляду власних змін, або відкриття файлу для редагування.
185 Централізовані системи виключають можливість роботи без мережі і вимагають більш дорогої мережевої інфраструктури, особливо із збільшенням кількості розробників. Що важливіше, всі операції відбуваються повільніше, зазвичай до такої міри, що користувачі уникають користування „просунутими“ командами без крайньої необхідності. У радикальних випадках це стосується навіть більшості базових команд. Коли користувачі змушені запускати повільні команди, продуктивність страждає через переривання робочого процесу.
187 Я відчув цей феномен на собі. Git був моєю першою системою керування версіями. Я швидко звик до нього і став відноситься до його можливостей як до належного. Я припускав, що й інші системи схожі на нього: вибір системи керування версіями не повинен відрізнятися від вибору текстового редактора або переглядача.
189 Коли трохи пізніше я був змушений використовувати централізовану систему керування версіями, я був шокований. Ненадійне інтернет-з’єднання не має великого значення при використанні Git, але робить розробку нестерпною, коли від нього вимагають надійності як у жорсткого диска. На додачу я виявив, що став уникати деяких команд через затримку у їх виконанні, що завадило мені дотримуватися кращого робочого процесу.
191 Коли мені було потрібно запустити повільну команду, порушення ходу моїх думок надавало несумірний збиток розробці. Чекаючи закінчення зв’язку з сервером, я змушений був займатися чимось іншим, щоб згаяти час; наприклад, перевіркою пошти або написанням документації. До того часу, як я повертався до початкової задачі, виконання команди було давно закінчено, але мені доводилося витрачати багато часу, щоб згадати, що саме я робив. Люди не дуже пристосовані для перемикання між завданнями.
193 Крім того, є цікавий ефект „трагедії суспільних ресурсів“: передбачаючи майбутню перевантаженість мережі, деякі люди в спробі запобігти майбутнім затримкам починають використовувати більш широкі канали, ніж їм реально потрібні для поточних завдань. Сумарна активність збільшує завантаження мережі, заохочуючи людей задіяти все більш високошвидкісні канали для запобігання ще більшим затримкам.