Git
Chapters ▾ 2nd Edition

7.3 Інструменти Git - Ховання та чищення

Ховання та чищення

Часто, коли ви працюєте над частиною свого проекту, усе перебуває в неохайному стані, а ви бажаєте переключити гілки щоб трохи попрацювати над чимось іншим. Складність у тому, що ви не бажаєте робити коміт напівготового завдання тільки щоб повернутися до цього стану пізніше. З цим нам допомагає команда git stash.

Ховання (stashing) бере чорновий стан вашої робочої директорії — тобто ваші змінені супроводжувані файли та індексовані зміни — та зберігає їх у стеку незавершених змін, які ви можете знову використати будь-коли (і навіть на іншій гілці).

Note
Міграція до git stash push

Наприкінці жовтня 2017 у поштовому списку Git відбувалися розлогі дискусії, через які команду git stash save було визнано застарілою на користь вже існуючої альтернативи git stash push. Основною причиною було те, що git stash push дозволяє ховати вибрані специфікації шляхів, а git stash save цього не підтримує.

git stash save не скоро зникне, тож не хвилюйтеся, що він раптом стане вам доступним. Проте ви можете почати використовувати push заради нового функціоналу.

Ховання ваших змін

Задля демонстрації ховання, треба перейти до проекту та почати працювати над декількома файлами та можливо проіндексувати один з них. Якщо виконати git status, можна побачити чорновий стан:

$ git status
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   index.html

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:   lib/simplegit.rb

Тепер ви бажаєте переключити гілки, проте наразі не бажаєте робити коміт ваших змін. Отже ви сховаєте ваші зміни. Щоб додати нове ховання (stash) до вашого стеку, виконайте git stash або git stash save:

$ git stash
Saved working directory and index state \
  "WIP on master: 049d078 added the index file"
HEAD is now at 049d078 added the index file
(To restore them type "git stash apply")

Як ви можете бачити, тепер ваша робоча тека чиста:

$ git status
# On branch master
nothing to commit, working directory clean

Тепер ви можете переходити до інших гілок та працювати деінде. Ваші зміни збережені в стеку. Щоб побачити збереженні ховання, використовуйте git stash list:

$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log

У даному випадку, два ховання були зроблені раніше, отже у вас є доступ до трьох різних схованих наборів змін. Ви можете використати щойно сховані зміни за допомогою команди, яку показано у виводу допомоги вищенаведеної команди ховання: git stash apply. Якщо ви бажаєте використати одне з попередніх ховань, то вам доведеться задати його назву, наприклад: git stash apply stash@{2}. Якщо ви не задаєте ховання, Git використовує найновіше та намагається його використати:

$ git stash apply
On branch master
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:   index.html
	modified:   lib/simplegit.rb

no changes added to commit (use "git add" and/or "git commit -a")

Як ви бачите, Git знову змінює файли, які ви відновили коли зберігали ховання. У даному випадку, ви мали чисту робочу директорію, коли намагались застосувати ховання, та намагались це зробити на тій самій гілці, з якої ви зберігали його. Проте мати чисту робочу директорію та використовувати ховання та тій самій гілці не обов’язково для його успішного застосування. Ви можете зберегти ховання на одній гілці, потім переключитися на іншу, та спробувати використати зміни там. Також ви можете мати змінені та не збережені файли в робочій директорії, коли ви застосовуєте ховання — Git надасть вам конфлікти зливання, якщо використати ховання чисто неможливо.

Зміни до ваших файлів повернулись, проте раніше індексовані файли не були знову проіндексовані. Щоб це зробити, треба виконати команду git stash apply з опцією --index, тоді команда використає та проіндексує зміни. Якби б ви виконали останню команду, ви б повернулися до попереднього стану:

$ git stash apply --index
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   index.html

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:   lib/simplegit.rb

Опція apply лише намагається використати сховані зміни — вона все одно залишається в стеку. Щоб видалити його, ви можете виконати git stash drop з назвою ховання, яке треба видалити:

$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log
$ git stash drop stash@{0}
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)

Також ви можете виконати git stash pop щоб застосувати ховання та відразу видалити його зі стека.

Винахідливе ховання

Є декілька варіантів ховання, що можуть бути корисними. Перша доволі популярна опція — це --keep-index команди stash save. Вона каже Git не не лише включити всі індексовані зміни до сховку, а й водночас залишити їх в індексі.

$ git status -s
M  index.html
 M lib/simplegit.rb

$ git stash --keep-index
Saved working directory and index state WIP on master: 1b65b17 added the index file
HEAD is now at 1b65b17 added the index file

$ git status -s
M  index.html

Ще одна поширена річ, яку ви можете зробити за допомогою ховання — це сховати не супроводжувані файли разом із супроводжуваними. Без додаткових опцій, git stash зберігає лише змінені й індексовані супроводжувані файли. Якщо додати опцію --include-untracked або -u, Git також додасть до сховку створені не супроводжувані файли.

$ git status -s
M  index.html
 M lib/simplegit.rb
?? new-file.txt

$ git stash -u
Saved working directory and index state WIP on master: 1b65b17 added the index file
HEAD is now at 1b65b17 added the index file

$ git status -s
$

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

$ git stash --patch
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 66d332e..8bb5674 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -16,6 +16,10 @@ class SimpleGit
         return `#{git_cmd} 2>&1`.chomp
       end
     end
+
+    def show(treeish = 'master')
+      command("git show #{treeish}")
+    end

 end
 test
Stash this hunk [y,n,q,a,d,/,e,?]? y

Saved working directory and index state WIP on master: 1b65b17 added the index file

Створення гілки з ховання

Якщо ви сховаєте дещо, залишите його на деякий час, продовжите працю над гілкою, з якої ви робили ховання, у вас можуть виникнути проблеми при відновленні схованих змін. Якщо застосування ховання спробує змінити файл, який ви змінили, то з’явиться конфлікт зливання та вам доведеться його вирішувати. Якщо ви шукаєте легшого шляху знову подивитись на сховані зміни, то ви можете виконати git stash branch <гілка>, що створить нову гілку із заданою назвою, отримає коміт, з якого ви зробили ховання, відновить ваші зміни, та видалить ховання, якщо воно успішно застосується:

$ git stash branch testchanges
M	index.html
M	lib/simplegit.rb
Switched to a new branch 'testchanges'
On branch testchanges
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   index.html

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:   lib/simplegit.rb

Dropped refs/stash@{0} (29d385a81d163dfd45a452a2ce816487a6b8b014)

Це зручне скорочення щоб легко відновити сховані зміни та продовжити над ними працювати в новій гілці.

Очищення робочої директорії

Нарешті, можливо ви бажаєте не ховати якісь зміни до файлів у робочій директорії, а просто позбутися їх. Команда git clean зробить це.

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

Треба бути винятково обережним з цією командою, адже вона створена для видалення файлів з вашої робочої директорії, які не супроводжується. Якщо ви зміните думку, зазвичай не існує методу отримати зміст цих файлів. Безпечніше виконати git stash --all щоб видалити все, проте зберегти його у хованні.

Якщо ж ви хочете видалити непотрібні файли, або очистити свою робочу директорію, то це може зробити команда git clean. Щоб видалити всі не супроводжувані файли в робочій директорії, виконайте git clean -f -d, що видаляє будь-які файли та піддиректорії, які в результаті стають порожніми. Опція -f означає force (змусити) або "дійсно зроби це".

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

$ git clean -d -n
Would remove test.o
Would remove tmp/

Без додаткових опцій, команда git clean видаляє тільки не супроводжувані файли, які не ігноруються. Будь-які файли, що відповідають шаблонам .gitignore або інших файлів ігнорування не будуть видалені. Якщо ви бажаєте видалити й ці файли, наприклад видалити всі файли .o, що були згенеровані при останній компіляції, щоб зробити повністю чисту компіляцію, ви можете додати опцію -x до команди clean.

$ git status -s
 M lib/simplegit.rb
?? build.TMP
?? tmp/

$ git clean -n -d
Would remove build.TMP
Would remove tmp/

$ git clean -n -d -x
Would remove build.TMP
Would remove test.o
Would remove tmp/

Якщо ви не впевнені, що буде робити команда git clean, завжди спочатку виконайте її з опцією -n та двічі все перевірте перш ніж змінювати -n на -f та дійсно зробити очищення. Інший спосіб бути обережним — зробити процес інтерактивним за допомогою опції -i.

Це виконає команду очистки в інтерактивному режимі.

$ git clean -x -i
Would remove the following items:
  build.TMP  test.o
*** Commands ***
    1: clean                2: filter by pattern    3: select by numbers    4: ask each             5: quit
    6: help
What now>

Таким чином ви можете пройти по кожному файлу окремо, або задати шаблон для видалення в інтерактивному режимі.

Note

Ви можете потрапити в непросту ситуацію, коли треба ще більш наполегливо попросити Git очистити вашу робочу теку. Якщо ви скопіювали чи склонували інше сховище Git (можливо як підмодуль) до робочої теки, то навіть git clean -fd відмовиться знищувати ці репозиторії. У такому випадку треба додати другий -f для переконливості.