TL;DR: git revert додає новий коміт, що скасовує старий — історія збережена, безпечно на гілках, які хтось уже пуллив. git reset відсуває вказівник гілки назад — історія переписується, безпечно лише на комітах, яких не пушив. На спільних гілках — revert, на локальному прибиранні — reset. А reset --hard ще й видаляє незакомічені зміни робочого дерева; саме він з’їдає справжню роботу.
У чому різниця між git revert і git reset?
Обидва змушують проєкт виглядати так, ніби минулого коміта не було. Розходяться в тому, як:
git revert <sha>обчислює обернений патч коміта і комітить його. Історія росте на один коміт «скасуй той». Поганий коміт лишається в логу, за ним — його скасування. SHA всього іншого недоторкані.git reset <sha>зсуває вказівник поточної гілки на<sha>. Коміти після нього відв’язуються — досі в базі об’єктів до збирання сміття, але більше не досяжні з жодної гілки чиgit log.
Один нічого не переписує, інший прикидається, що нічого не було. Саме та властивість вирішує, кого вимагає кожна ситуація:
# Демонстрація: одноразовий runnable-репозиторій
git init /tmp/revert-vs-reset && cd /tmp/revert-vs-reset
echo one > file.txt && git add . && git commit -m "one"
echo two > file.txt && git add . && git commit -m "two"
# Revert: історія тримає обидва коміти, плюс третій, що скасовує "two"
git revert --no-edit HEAD
git log --oneline # 3 коміти: revert, two, one
# Reset: вказівник гілки відсунувся, "two" зникає з логу
git reset --hard HEAD~1
git log --oneline # 1 коміт: one Запустіть обидва й огляньте git log — асиметрія і є вся відповідь.
Що насправді роблять reset —soft, —mixed і —hard?
Три режими керують тим, де виживають ваші зміни після руху вказівника:
| Режим | Вказівник гілки | Стейджинг | Робоче дерево | Ваші правки |
|---|---|---|---|---|
--soft | назад | лишилися застейдженими | недоторкані | збережені повністю |
--mixed (дефолт) | назад | нестейджені | недоторкані | збережені, нестейджені |
--hard | назад | скинуто | скинуто | видалені |
Конкретні прочитання таблиці:
git reset --soft HEAD~1— «я закомітив зарано». Усе повертається застейдженим, готовим до повторного коміту, можливо разом з іншою роботою.git reset HEAD~1(mixed) — «я застейджив не ті файли». Правки живуть у робочому дереві, стейдж порожній. Спарте з видаленням невідстежуваних файлів, коли хочете прибрати й блукаючі файли.git reset --hard HEAD~1— «цей коміт і мої незакомічені правки — сміття». Git не спитає двічі; для частини з робочим деревом скасування немає, якщо не застешували і не комітили заздалегідь.
У revert режимів немає, бо він ніколи нічого не нищить — лише додає компенсувальний коміт.
Коли брати git revert замість git reset?
Одне питання: чи хтось ще пуллив цей коміт? Якщо так — reset за бортом.
Скидання гілки, на якій хтось будував роботу, переписує спільну історію. Їхній наступний pull зустрічає розбіжні гілки, а «виправлення» — це зазвичай force-push, що перетворює вашу помилку на проблему всіх. Revert додає звичайний коміт, тож звичайний git pull мержиться чисто для всіх — тому revert є типовою відповіддю на main і в GitHub pull request’ах, де кнопка «Revert» існує саме тому, що переписати історію змерженого PR — не варіант.
Reset — для вікна до поширення: коміт, зроблений тридцять секунд тому, помилка стейджингу, локальна експериментальна гілка. Якщо єдина копія роботи — на вашій машині, ви вільні переставляти історію — у цьому весь сенс передпушного вікна.
Проміжний випадок: ви запушили у власну feature-гілку, і ніхто на ній не будує. Force-push після reset там соціально прийнятний, але revert досі коштує менше координації. Залишіть force-push-и для гілок, якими володієте самі.
Чи revert безпечніший за reset для спільних гілок?
Так, структурно — не лише за конвенцією:
- Revert породжує звичайний коміт: CI на ньому біжить, диф придатний до рев’ю, а
git logпояснює майбутньому-вам, чому зміна зникла. - Reset мовчки викидає проміжний стан. Ніхто, хто ревʼюватиме пізніше, не побачить, що скасували і коли, бо лог показує пряму лінію, яка ніколи його не містила.
Два практичні гострі краї:
- Скасування merge-коміта вимагає
git revert -m 1 <sha>(лишити лінію першого батька). Без-mgit відмовляється і лишає вам читати помилку. - Revert — не машина часу: він скасовує диф одного коміта. Якщо пізніші коміти чіпали ті самі рядки, можливо, доведеться розв’язувати конфлікти — це git каже, що скасування переплелося, корисна інформація, а не несправність.
Про скасування саме вашого останнього коміта — зі збереженням чи викиданням змін — варіанти розкладені в git undo last commit: зі збереженням змін.
А git restore — де його місце?
git restore (і git switch, його пара) приїхали в git 2.23, щоб забрати роботи, які reset робив погано через перевантаження іменами:
| Задача | Стара дорога | Зрозуміла дорога |
|---|---|---|
| Відкинути правки робочого дерева у файлі | git checkout -- file / git reset --hard | git restore file |
| Зняти файл зі стейджингу | git reset HEAD file | git restore --staged file |
| Перемістити гілку на інший коміт | git reset <sha> | git reset <sha> (заміни немає — це лишається) |
Отже сучасний поділ: restore лагодить файли, reset рухає гілки, revert скасовує опубліковані коміти. Автодоповнення показує, що люди шукають «git revert vs reset vs restore» разом недаремно — це одна ментальна модель, розлита на три команди. Старі форми git checkout досі працюють всюди; нові просто не дають вам відправити цілу гілку в шредер, коли ви мали на увазі зняти один файл зі стейджингу.
Якщо ж ваша ціль — скопіювати хороший коміт уперед, а не стерти поганий, то це робота cherry-pick — дивіться git cherry-pick: кілька комітів, гілок і конфліктів.
Швидкий підсумок рішень
- Коміт публічний (запушено, інші пуллили) →
git revert <sha>. - Коміт лише локальний, хочете переробити →
git reset --softчи--mixedі перекомітіть. - Лише локальний, хочете, щоб зник разом із незакоміченим безладдям →
git reset --hard, після одного чесного погляду, що ще тримає робоче дерево. - Поламали один файл →
git restore <file>і лишіть гілку в спокої.
Єдиний справді небезпечний пункт у тому списку — --hard; усе інше можна відбити назад через git reflog. Постійні втрати в git вузькі й здебільшого вимагають, щоб ви ввели їх явно; звичка, варта вироблення, — застигнути на пів секунди перед --hard, а не уникати гострих інструментів git узагалі.
— mrsaynothing
— mrsaynothing
Польові нотатки про ШІ, Linux і self-hosting.
Обговорити пост на dev.to dev.to ↗
Наступний гайд — на email
Один лист на пост. Полагодили — і далі.
що це таке?Systemd-сервіс не стартує? Як це виправити
Читається добре? Таке я будую за гроші. найміть мене