Що означає звичайна англійська мова, що робить “git reset”?


674

Я бачив цікаві пости, що пояснюють тонкощі git reset.

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

Я думаю, що git resetце близько hg revert, але, схоже, є відмінності.

Отже, що саме робить git reset? Будь ласка, включіть детальні пояснення щодо:

  • варіанти --hard, --softі --merge;
  • дивні позначення, які ви використовуєте з HEADтакими як HEAD^і HEAD~1;
  • конкретні випадки використання та робочі потоки;
  • наслідки для робочої копії, рівня HEADта рівня глобального стресу.

17
Я думаю, що Visual Git Reference дає хорошу інформацію про те, що відбувається при використанні загальних команд git.

Відповіді:


992

Загалом, git resetфункція полягає в тому, щоб взяти поточну гілку і скинути її на точку десь в іншому місці, і, можливо, принести індекс і дерево роботи. Більш конкретно, якщо ваша головна галузь (наразі перевірена) така:

- A - B - C (HEAD, master)

і ви розумієте, що хочете, щоб майстер вказував на B, а не на C, ви будете використовувати його git reset Bдля переміщення туди:

- A - B (HEAD, master)      # - C is still here, but there's no branch pointing to it anymore

Відступ: Це відрізняється від каси. Якщо ви біжите git checkout B, ви отримаєте це:

- A - B (HEAD) - C (master)

Ви опинилися в відокремленому стані HEAD. HEAD, дерево роботи, індексувати всі збіги B, але головна гілка залишилася в C. Якщо ви зробите нове зобов'язання Dв цей момент, ви отримаєте це, що, мабуть, не те, що ви хочете:

- A - B - C (master)
       \
        D (HEAD)

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

Використовуйте випадки

Я висвітлюю багато основних випадків використання в git resetмежах своїх описів різних варіантів у наступному розділі. Його справді можна використовувати для найрізноманітніших речей; загальний потік полягає в тому, що всі вони передбачають скидання гілки, індексу та / або робочого дерева, щоб вказувати / відповідати заданій комісії.

Речі, на які слід бути обережними

  • --hardможе призвести до того, що ви дійсно втратите роботу. Це змінює ваше робоче дерево.

  • git reset [options] commitможе призвести до (втрати) зобов'язань. У наведеному вище прикладі іграшки ми втратили прихильність C. Він все ще знаходиться в репо, і ви можете знайти його, переглянувши git reflog show HEADабо git reflog show master, але він фактично недоступний з жодної гілки.

  • Git назавжди видаляє такі зобов’язання через 30 днів, але до цього часу ви можете відновити C, знову вказуючи гілку на нього ( git checkout C; git branch <new branch name>).

Аргументи

Перефразовуючи сторінку чоловіка, найчастіше вживається форма git reset [<commit>] [paths...], яка скине задані шляхи до їх стану від даної фіксації. Якщо шляхи не надані, все дерево скидається, а якщо фіксація не передбачена, вважається, що це HEAD (поточне виконання). Це поширений зразок для команд git (наприклад, checkout, diff, log, хоча точна семантика відрізняється), тому це не повинно бути занадто дивним.

Наприклад, git reset other-branch path/to/fooскидає все в шляху / до / foo до свого стану в іншій галузі, git reset -- .скидає поточний каталог на його стан у HEAD, а простий git resetскидає все на його стан у HEAD.

Основні варіанти роботи дерева та параметри індексу

Існує чотири основні варіанти контролю того, що відбувається з вашим робочим деревом та індексом під час скидання.

Пам'ятайте, що індекс - це "інсценізація області" git - це те, куди йдуть справи, коли ви говорите, git addготуючись до вчинення.

  • --hardзмушує все відповідати виконанню, яку ви скинули. Це найлегше зрозуміти, напевно. Усі ваші локальні зміни стають обмеженими. Одним із основних напрямків роботи є знеструмлення вашої роботи, але не перемикання зобов'язань: git reset --hardозначає git reset --hard HEAD, тобто не змінюйте гілку, а позбавляйтеся від усіх локальних змін. Інший - просто переміщення гілки з одного місця в інше та синхронізація дерева індексу / роботи. Це той, який може насправді змусити вас втратити роботу, оскільки він модифікує ваше робоче дерево. Будьте дуже впевнені, що хочете відкинути місцеву роботу, перш ніж запустити будь-яку reset --hard.

  • --mixedє типовим, тобто git resetзасобом git reset --mixed. Він скидає індекс, але не дерево роботи. Це означає, що всі ваші файли є неушкодженими, але будь-які відмінності між оригінальним фіксуванням та тим, до якого ви скинули, відображатимуться як локальні модифікації (або не відстежені файли) зі статусом git. Використовуйте це, коли зрозумієте, що зробили якісь погані зобов’язання, але хочете зберегти всю роботу, яку ви зробили, щоб ви могли її виправити та повторно. Для здійснення зобов’язань вам доведеться знову додати файли до індексу ( git add ...).

  • --softне торкається індексу чи робочого дерева. Усі ваші файли є недоторканими, як і раніше --mixed, але всі зміни відображаються як changes to be committedзі статусом git (тобто зареєстровано під час підготовки до вчинення). Використовуйте це, коли зрозумієте, що зробили якісь погані вчинки, але робота - все добре - все, що вам потрібно зробити, - це повторити її по-іншому. Індекс недоторканий, тому ви можете скористатись негайно, якщо захочете - отримана комісія матиме той самий вміст, що і там, де ви були до скидання.

  • --mergeдодано нещодавно і призначено допомогти вам перервати невдале злиття. Це необхідно, тому що git mergeнасправді ви зможете спробувати злити з брудним робочим деревом (одне з локальними модифікаціями) до тих пір, поки ці модифікації будуть у файлах, на які не впливає злиття. git reset --mergeскидає індекс (як-от --mixed- всі зміни відображаються як локальні модифікації) та скидає файли, на які впливає злиття, а інші залишає в спокої. Це, сподіваємось, відновить усе, як було до поганого злиття. Зазвичай ви будете використовувати його як git reset --merge(означає git reset --merge HEAD), оскільки ви хочете скинути злиття, а не перемістити гілку. ( HEADще не оновлено, оскільки злиття не вдалося)

    Якщо бути конкретнішим, припустимо, що ви змінили файли A і B, і ви намагаєтеся об'єднатись у гілку, яка модифікувала файли C і D. Злиття чомусь не вдається, і ви вирішили перервати це. Ви використовуєте git reset --merge. Це повертає C і D до того, як вони були HEAD, але залишає ваші модифікації A і B у спокої, оскільки вони не були частиною спроби злиття.

Хочете дізнатися більше?

Я думаю, що man git resetце дуже добре для цього - можливо, вам потрібно трохи почуття того, як git працює для них, щоб дійсно зануритися в хоч. Зокрема, якщо ви знайдете час, щоб уважно їх прочитати, дуже корисні ці таблиці з деталізацією станів файлів в індексі та робочому дереві для всіх різних варіантів та випадків. (Але так, вони дуже щільні - вони дуже стисло передають дуже багато вищевказаної інформації.)

Дивні позначення

"Дивне позначення" ( HEAD^і HEAD~1), яке ви згадуєте, - це просто скорочення для вказівки комітетів, без використання хеш-імені типу 3ebe3f6. Це повністю задокументовано в розділі "уточнення змін" сторінки man для git-rev-розбору з великою кількістю прикладів та відповідних синтаксисів. Карета і тильда насправді означають різні речі :

  • HEAD~є скороченим HEAD~1і означає першого батька вчинення. HEAD~2означає перший з батьків перших батьків. Подумайте про те HEAD~n, що "п позначається перед ГОЛОВОЮ" або "родоначальником п’ятого покоління ГОЛА".
  • HEAD^(або HEAD^1) також означає перший з батьків. HEAD^2означає другий з батьків. Пам'ятайте, що у звичайній комісії злиття є два батьки - перший з батьків є об'єднаним в коміт, а другий з батьків - це об'єднання, яке було об'єднано. Взагалі злиття насправді можуть мати довільно багато батьків (злиття восьминога).
  • Ці ^та ~оператори можуть бути нанизані, як і в HEAD~3^2, другий батько третього покоління предка HEAD, HEAD^^2другий батько першого батька HEAD, або навіть HEAD^^^, що еквівалентно HEAD~3.

каре і тильда


"ви використаєте скидання git, щоб перемістити його туди." чому ви не використовуєте git checkout для цього?
e-satis

5
@ e-satis: git checkout перемістить HEAD, але залишить гілку там, де вона була. Це для тих випадків, коли потрібно перемістити гілку.
Каскабель

Тож якщо я добре розумію, скидання B зробить: - A - B - C - B (master), тоді як замовлення B зробить - A - B (master)?
e-satis

32
Документи хороші, хоча читати їх потрібно вічно, і вони дуже щільні, і потрібно вічно перевірити, що вони говорять, що це працює так, як ви вже знаєте, як це працює. Не здається, що документи добрі для мене ...
Кірбі

4
Дуже просте і зрозуміле пояснення дає ця відповідь ТА: stackoverflow.com/questions/3528245/whats-the-difference-bet
between-git-reset-mixed-soft-

80

Пам'ятайте, що в gitвас є:

  • HEADпокажчик , який говорить вам , що зробити ви працюєте
  • працює дерево , яке являє собою стан файлів у вашій системі
  • плацдарм (також званий індексом ), який «етапи» змінюється так , що вони в подальшому можуть бути вчинені разом

Будь ласка, додайте детальні пояснення щодо:

--hard, --softі --merge;

Збільшуючи порядок небезпечності:

  • --softрухається, HEADале не торкається області постановки або робочого дерева.
  • --mixedпереміщує HEADта оновлює область постановки, але не робоче дерево.
  • --mergeпереміщається HEAD, скидає область розміщення та намагається перемістити всі зміни вашого робочого дерева в нове робоче дерево.
  • --hardпереміщує HEAD та налаштовує вашу інсценівку та робоче дерево під нову HEAD, викидаючи все.

конкретні випадки використання та робочі процеси;

  • Використовуйте, --softколи ви хочете перейти на інший виклик і виправити речі, не втрачаючи місця. Досить рідко це вам потрібно.

-

# git reset --soft example
touch foo                            // Add a file, make some changes.
git add foo                          // 
git commit -m "bad commit message"   // Commit... D'oh, that was a mistake!
git reset --soft HEAD^               // Go back one commit and fix things.
git commit -m "good commit"          // There, now it's right.

-

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

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

  • Використовуйте --hardдля того, щоб витерти все і почати свіжий шифер на новому комітеті.


2
Це не призначений для використання спосіб reset --merge. Він не виконує тристороннього злиття. Це дійсно лише для скидання конфліктних злиттів, як описано в документах. Ви хочете використовувати, checkout --mergeщоб робити те, про що ви говорите. Якщо ви також хочете перемістити гілку, я думаю, що єдиний спосіб - це виконати деяку перевірку / скидання, щоб перетягнути її.
Каскабель

@Jefromi »Так, я не дуже добре висловився. Під "новим місцем" я мав на увазі "свіже місце, де у вас немає конфліктного злиття".
Джон Фемінелла

1
А, бачу. Я думаю, що тут важливим є те, що, якщо ви дійсно не знаєте, що ви робите, ви, ймовірно, ніколи не хочете використовувати reset --mergeжодну ціль, окрім (за замовчуванням) HEAD, тому що у випадках, крім припинення конфліктного злиття, воно викинеться інформація, яку ви могли в іншому випадку зберегти.
Каскабель

2
Цю відповідь я знайшов найпростішою та найкориснішою
Jazzepi

Будь ласка, додайте інформацію про ці команди: git resetі git reset -- ..
Flimm

35

Повідомлення Reset Demystified у блозі Pro Git дає дуже необмежене пояснення щодо git resetта git checkout.

Після всієї корисної дискусії вгорі цієї публікації автор зводить правила до наступних простих трьох кроків:

Це в основному це. resetКоманда переписує ці три дерева в певному порядку, зупиняючись , коли ви сказати йому.

  1. Перемістіть будь-яку галузь, на яку вказує HEAD (зупиніть, якщо --soft)
  2. ТАКОЖ, зробіть індекс таким чином (зупиняйтесь тут, якщо тільки --hard)
  3. ТАКОЖ, зробіть Робочий каталог таким чином

Є також --mergeі --keepваріанти, але я б швидше поки що спрощував речі - це буде для іншої статті.


25

Коли ви щось робите, ви спершу повинні поетапно (додавати до індексу) свої зміни. Це означає, що ви повинні git додати всі файли, які ви хочете включити до цього комітету, перш ніж git вважатиме їх частиною комісії. Давайте спочатку подивимось на зображення git repo: введіть тут опис зображення

Отже, його просто зараз. Ми повинні працювати в робочому каталозі, створюючи файли, каталоги та все. Ці зміни - це невиправлені зміни. Щоб зробити їх відстеженими, нам потрібно додати їх до git index за допомогою команди git add . Після їх додавання до git index. Тепер ми можемо здійснити ці зміни, якщо хочемо підштовхнути їх до сховища git.

Але раптом ми дізналися, маючи на увазі, що у нас є один додатковий файл, який ми додали в індекс, не потрібно запускати репозиторій git. Це означає, що ми не хочемо, щоб цей файл був в індексі. Тепер питання полягає в тому, як вилучити цей файл з індексу git, оскільки ми використовували git add, щоб помістити їх в індекс, було б логічно використовувати git rm ? Неправильно! git rm просто видалить файл і додасть видалення до індексу. Отже, що робити зараз:

Використання: -

git reset

Він очищає ваш індекс, залишає ваш робочий каталог недоторканим. (просто знімаючи все).

З ним можна використовувати кілька варіантів. Існує три основні варіанти використання для скидання git: --hard, --soft та --mixed . Вони впливають на те, що буде скинуто на додаток до покажчика HEAD при скиданні.

По-перше, --хард все скидає. Ваш поточний каталог був би таким самим, як це було б, якби ви весь цей час дотримувались цієї гілки. Робочий каталог та індекс змінені на цей коміт. Це версія, якою я користуюся найчастіше. git reset --hard - це щось на кшталт svn revert .

Далі, повна протилежність, —софт , не скидає робоче дерево та індекс. Він переміщує лише вказівник HEAD. Це залишає ваш поточний стан будь-якими змінами, відмінними від фіксації, яку ви перемикаєте на місце у вашому каталозі, та "поетапно" для здійснення. Якщо ви здійснили локальну передачу, але ви не підштовхнули комісію до сервера git, ви можете скинути попередню команду та повторно виконати повідомлення з гарним повідомленням про фіксацію.

Нарешті, --mixed скидає індекс, але не робоче дерево. Таким чином, зміни все ще є, але є "нефіксованими", і їм потрібно буде додавати git або додавати git -a . ми використовуємо це іноді, якщо ми здійснили більше, ніж ми мали на увазі git commit -a, ми можемо відмовити команду за допомогою git reset - змішати, додати речі, які ми хочемо зробити і просто зробити їх.

Різниця між відновленням git та скиданням git : -


Простими словами, git reset - це команда "виправити неполадки помилок", а git revert - це команда "виправити помилку, що допущена" .

Це означає, що якщо ми допустили помилку в якійсь зміні, і зробили це, і натиснули ту саму, щоб git repo, тоді відновлення git - це рішення. І якщо у випадку, коли ми виявили ту саму помилку перед тим, як натиснути / скористатись, ми можемо використати скидання git для усунення проблеми.

Сподіваюся, це допоможе вам позбутися від вашої плутанини.


2
Це хороша звичайна англійська відповідь на запитання ОП.
Episodex

1
Хоча я можу пропустити це у вашій відповіді. Що git reset HEADза замовчуванням? --hard, --softабо --mixed? Чудова відповідь btw.
giannis christofakis

1
Чудова відповідь, але я б зрозумів, що git reset --hardви втратите деякі дані. І є момент, який може бути неправильним (хоча я не впевнений на 100% ... Все ще вчуся!): Говорячи про --mixedвас, ви говорите, що "ми використовуємо це іноді, якщо ми скоїли більше, ніж ми мали на увазі з git commit -a". Ви мали на увазі: "якби ми поставили більше, ніж ми мали намір git stage ."? Якщо ви дійсно це зробили, я думаю, що це вже пізно (як ви говорите в кінці кінця, git reset - це команда "виправляти неполадки помилок")
Фабіо каже: Поновити Моніку

6

TL; DR

git resetскидає Постановку до останнього комітету. Використовуйте --hardтакож для скидання файлів у вашому робочому каталозі до останнього виконання.

ДОВГА ВЕРСІЯ

Але це очевидно спрощено, отже, безліч багатослівних відповідей. Мені було більше сенсу читати далі git resetв контексті скасування змін. Наприклад, дивіться це:

Якщо відновлення git - це "безпечний" спосіб скасування змін, ви можете вважати скидання git небезпечним методом. Коли ви скасовуєте функцію скидання git (і на комітети вже не посилаються жодні посилання чи відхилення), немає можливості отримати оригінальну копію - це постійне скасування. Необхідно бути обережними при використанні цього інструменту, оскільки це одна з єдиних команд Git, яка може втратити вашу роботу.

З https://www.atlassian.com/git/tutorials/undoing-changes/git-reset

і це

На рівні фіксації скидання - це спосіб перемістити кінчик гілки до іншого коміту. Це можна використовувати для видалення комітетів із поточної гілки.

З https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations


2

Зауважте, що це спрощене пояснення, яке є першим кроком у розумінні цього складного функціоналу.

Може бути корисним для візуальних учнів, які хочуть уявити, як виглядає їхній проект після кожної з цих команд:


Для тих, хто використовує термінал із увімкненим кольором (git config --global color.ui auto):

git reset --soft A і ви побачите речі B та C зеленим кольором (поетапно та готові до вчинення)

git reset --mixed A(або git reset A), і ви побачите речі B і C червоним кольором (нестандартним і готовим до постановки (зеленим), а потім скоєним)

git reset --hard A і ви більше не побачите змін B і C ніде (буде так, ніби їх ніколи не було)


Або для тих, хто використовує програму GUI типу "Tower" або "SourceTree"

git reset --soft A і ви побачите матеріали B і C в області "поетапних файлів", готових до введення

git reset --mixed A(або git reset A), і ви побачите матеріали B і C в області "нестандартних файлів", готових до переміщення в інсценовані та потім скоєні

git reset --hard A і ви більше не побачите змін B і C ніде (буде так, ніби їх ніколи не було)


1

Каса вказує голову на певний комітет.

Скидання вказує гілку на певний комітет. (Гілка - вказівник на коміт.)

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


1
Чи не чіплятися, але (та, насправді це є причіпки , але давайте додамо його завершення) ваше третє речення технічно невірно. Скажімо, ваша ГОЛОВКА вказує на гілку B, яка в свою чергу вказує на введення abc123. Якщо тепер ви здійснили оформлення abc123, ваша HEAD та відділення B вказують на фіксацію abc123, і ваша HEAD від'єднана. Здійснення цього зобов’язання не оновить позицію відділення B. Ви могли сказати "якщо ваша голова не вказує на гілку, то у вас є відсторонена голова"
RomainValeri

@RomainValeri Що робитимуть вчинення за таких обставин?
Ян Варбуртон

1
Здійснення зобов'язань створюватиме коміти, на які не посилається жодна гілка, і гілка B продовжує вказувати на одну і ту ж фіксацію abc123 навіть після того, як ви зробили кілька разів після цього. Це означає, що ці комісії можуть стати кандидатами на вивезення сміття, коли HEAD перестане вказувати на останнє зобов'язання в цій "дикій" серії комісій.
RomainValeri
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.