Git
Chapters ▾ 2nd Edition

2.4 Основи на Git - Възстановяване на направени действия

Възстановяване на направени действия

Във всеки един момент може да се окаже, че искате да отмените дадена промяна по проекта. Тук ще разгледаме някои основни средства за отмяна на промени. Бъдете внимателни, защото не винаги може да отмените отмяна! Това е една от малките области в Git, където можете да загубите част от данните си, ако не действате прецизно.

Едно от най-честите действия по отмяна е да направите къмит твърде рано и да сте забравили да добавите няколко файла или да сте объркали commit съобщението. Ако искате да опитате този къмит отново, направете забравените промени, индексирайте ги и можете да къмитнете отново с параметъра --amend:

$ git commit --amend

Тази команда взема съдържанието на индекса и го използва за къмита. Ако не сте правили промени от последния къмит (например, пускате командата веднага след предишния къмит), тогава вашият snapshot ще изглежда по идентичен начин и единственото нещо, което ще промените е commit съобщението.

Ще се отвори същия редактор, но със заредено съобщението от последния къмит. Можете да промените съобщението както обикновено, но то ще се презапише върху предишния ви къмит.

Като пример, ако къмитнете и веднага след това се сетите, че не сте индексирали промените във файл, който искате да влиза в къмита, можете да направите следното:

$ git commit -m 'Initial commit'
$ git add forgotten_file
$ git commit --amend

Ще си останете с един къмит — вторият замества резултатите от първия.

Забележка

Важно е да се запомни, че когато коригирате последния къмит, го заменяте с изцяло нов, подобрен такъв, който изцяло замества стария. По същество историята ще изглежда така, сякаш предишния къмит никога не се е случвал и няма да се показва в нея.

Очевидната полза от amending къмитите е, че можете да правите малки промени по последния къмит, без да трябва да задръствате историята със съобщения от сорта на “упс, забравих да добавя файл” или “поправям грешка в последния къмит”.

Забележка

Отменяйте къмити само ако те са все още локални и не са изпращани никъде отдалечено. Отмяна на предишно изпратени към сървъра къмити и форсираното изпращане на клона ще предизвика проблеми за сътрудниците ви. За повече информация какво се случва, когато се прави това и как да възстановите пораженията, ако сте от приемащата страна, прочетете [_rebase_peril].

Изваждане на файл от индекса

Следващите две секции демонстрират как се работи с индексната област и промените в работната директория. Хубавото е, че командата, която използвате за да определите статуса на тези две области, също така ви подсказва и как да отменяте направени в тях промени. За пример, нека кажем, че сте променили два файла и искате да ги къмитнете като две отделни промени, но неволно сте изпълнили git add * и сте ги индексирали и двата. Как да извадите от индекса единия от двата? Командата git status ви подсказва начина:

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
    modified:   CONTRIBUTING.md

Точно под текста “Changes to be committed”, пише git reset HEAD <file>…​ to unstage. Да ползваме този съвет за да де-индексираме файла CONTRIBUTING.md:

$ git reset HEAD CONTRIBUTING.md
Unstaged changes after reset:
M	CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

Командата е леко странна, но работи. Файлът CONTRIBUTING.md сега си е променен, но вече не е в индекса

Забележка

Вярно е, че git reset може да се окаже опасна команда, особено ако ѝ подадете флага --hard. Обаче, в горния случай, файлът в работната ви директория е недокоснат, така че тя е сравнително безопасна.

Към момента, това мистериозно извикване е всичко, което трябва да знаете за git reset командата. Ще навлезем в много по-дълбоки подробности за това какво прави reset и как да я ползваме за да правим наистина интересни неща в Мистерията на командата Reset.

Отмяна на промените в променен файл

Какво се случва, ако установите, че не искате да пазите промените си във файла CONTRIBUTING.md? Как можем лесно да го "де-модифицираме" — тоест да го превъртим назад до съдържанието, което е имал при последния къмит (или както е бил при първоначалното клониране в работната ви директория)? За късмет, git status ни подсказва и това. В последния ни пример, работната област изглеждаше така:

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

Това ви казва доста недвусмислено как да отмените промените, които сте направили. Нека да го изпълним:

$ git checkout -- CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

Можете да се уверите, че промените изчезнаха.

Важно

Важно е да се помни, че git checkout -- <file> е опасна команда. Всички локални промени, които сте правили по този файл са изчезнали необратимо — Git просто заменя файла с последната му индексирана или къмитната версия. Никога не ползвайте тази команда, освен ако не сте абсолютно сигурни, че не желаете промените във файла.

Ако желаете да запазите промените си по файла, но все още държите да пазите този файл настрани от проекта към дадения момент, има по-добри начини да го направите, ще ги разгледаме в материала за скриване (stashing) и клонове код (branchingКлонове в Git).

Помнете, всичко което е къмитнато в Git може почти винаги да бъде възстановено по-късно. Дори къмити, които са били в изтрити клонове или комити презаписани с къмит от тип --amend, могат да бъдат възстановени (вижте Възстановяване на данни за повече информация). Обаче, всичко което загубите и не е било къмитното - най-вероятно няма да може да се възстанови.

Възстановяване с git restore

Git 2.23.0 има нова команда: git restore. Тя по същество е алтернатива на git reset, която разгледахме. От версия 2.23.0 натам, Git ще използва git restore вместо git reset за много undo операции.

Нека повторим стъпките и да възстановяваме данните с git restore вместо с git reset.

Изваждане от индекса на файл с git restore

Следващите 2 секции демонстрират как се работи с индексната област и работната директория посредством git restore. Добрата страна е, че командата, която използвате за да установите статуса на тези две области също така ви подсеща и как да отменяте промени в тях. Например, да кажем, че сте променили два файла и искате да ги къмитнете като две отделни промени, но без да искате сте изпълнили git add * и сте ги индексирали и двата. Как да извадие от индекса единия? Командата git status ви напомня:

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   CONTRIBUTING.md
	renamed:    README.md -> README

Веднага след реда “Changes to be committed”, виждате: git restore --staged <file>…​ to unstage. Нека използваме този съвет, за да извадим от индекса файла CONTRIBUTING.md:

$ git restore --staged CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    README.md -> README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   CONTRIBUTING.md

Сега CONTRIBUTING.md е модифициран, но вече не е индексиран.

Отмяна на промените по променен файл с git restore

Какво да направим, ако установим, че не искаме промените, които сме направили по CONTRIBUTING.md? Как лесно да ги отменим — да го върнем до състоянието му, в което е бил преди последния къмит (или в което е бил при клонирането)? За щастие, git status подсказва и това. В последния ни пример, работната директори изглеждаше така:

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   CONTRIBUTING.md

Командата доста недвусмислено показва как да отменим промените. Нека го направим:

$ git restore CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    README.md -> README
Важно

Важно е да помним, че git restore <file> е опасна команда. Всички локални промени, които сме направили по даден файл изчезват — Git просто подменя файла с последната му индексирана или къмитната версия. Не я ползвайте, освен ако не сте абсолютно сигурни, че не искате тези локални несъхранени промени.