Пошук уроків, статей та іншого контенту
Три різні способи «відмінити» щось у Git — і критична різниця між ними для спільної історії.
«Скасувати щось у Git» насправді означає одну з кількох принципово різних дій, кожна зі своєю командою: відкинути незакомічені зміни у файлі (restore), пересунути поточну гілку назад до попереднього коміту (reset), чи додати новий коміт, що скасовує ефект попереднього, не видаляючи його з історії (revert).
git restore index.js # повернути файл до стану останнього коміту, втративши незакомічені зміниgit reset пересуває вказівник поточної гілки на інший коміт — з трьома режимами, що по-різному впливають на staging area й робочу директорію:
--soft — пересуває лише гілку; зміни скасованих комітів лишаються в staging area, готові до нового коміту.
--mixed (типовий за замовчуванням) — пересуває гілку й очищає staging area; зміни лишаються в робочій директорії як незакомічені.
--hard — пересуває гілку, очищає staging area, і повністю відкидає зміни в робочій директорії — незворотна втрата незбережених змін цих комітів.
git reset --soft HEAD~1 # скасувати останній коміт, лишити зміни готовими до нового коміту
git reset --hard HEAD~1 # скасувати останній коміт і повністю відкинути його зміниgit reset --hard незворотно видаляє незбережені зміни — перед виконанням варто переконатись, що це справді потрібно. І, як і rebase, git reset переписує, до чого вказує гілка — небезпечний для вже опублікованої, спільної історії з тієї самої причини.
На відміну від reset, git revert не видаляє й не переписує існуючі коміти — він додає новий коміт, що застосовує зміни, протилежні обраному коміту, залишаючи повну історію (включно з тим, що щось було зроблено, а потім скасовано) недоторканою:
git revert a1b2c3d # створити новий коміт, що скасовує зміни коміту a1b2c3dЛокальний коміт, ще не запушений нікуди, — reset (легко переробити, ніхто інший на нього ще не покладається).
Коміт, уже запушений і потенційно отриманий іншими (main, спільна гілка), — revert (не переписує історію, безпечно для всіх, хто вже має цей коміт локально).
git reset --hard на вже запушеній спільній гілці — переписує історію так само небезпечно, як rebase спільної гілки, ламаючи локальні копії інших учасників.
git reset --hard без усвідомлення, що незбережені зміни втрачаються безповоротно, — на відміну від --soft/--mixed, тут немає способу відновити відкинуте через сам Git.
Використовувати reset там, де потрібен revert (чи навпаки) — плутанина найчастіше виникає саме через незнання, чи коміт уже опублікований і чи покладаються на нього інші.
git restore скасовує незакомічені зміни файлу; git reset пересуває поточну гілку назад (--soft/--mixed зберігають зміни, --hard відкидає їх безповоротно) і переписує історію, тому безпечний лише для локальних, ще не опублікованих комітів; git revert додає новий коміт, що скасовує ефект попереднього, не видаляючи й не переписуючи існуючу історію — тому це безпечний спосіб скасувати вже опубліковані, спільні зміни.