Git
Chapters ▾ 2nd Edition

7.3 Инструменты Git - Прибережение и очистка

Прибережение и очистка

Часто пока вы работаете над одной частью вашего проекта и всё находится в беспорядке, у вас возникает желание сменить ветку и поработать над чем-то еще. Сложность при этом заключается в том, что вы не хотите фиксировать наполовину сделанную работу только для того, чтобы иметь возможность вернуться к ней позже. Справиться с ней помогает команда git stash.

Операция stash берет измененное состояние вашей рабочей директории, то есть измененные отслеживаемые файлы и проиндексированные изменения, и сохраняет их в хранилище незавершенных изменений, которые вы можете в любое время применить обратно.

Прибережение ваших наработок

Для примера, предположим, что вы перешли в свой проект, начали работать над несколькими файлами и, возможно, добавили в индекс изменения одного из них. Если вы выполните 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

Теперь вы хотите сменить ветку, но пока не хотите фиксировать ваши текущие наработки; поэтому вы спрячете эти изменения. Для того, чтобы спрятать изменение в выделенное для этого специальное хранилище, выполните 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
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#      modified:   index.html
#      modified:   lib/simplegit.rb
#

Как видите, 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
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#      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 add.

Это может быть полезно в случае, когда вы сделали какие-то изменения, но хотите зафиксировать только часть из них, а к оставшимся вернуться через некоторое время.

$ 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
Switched to a new branch "testchanges"
# On branch testchanges
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#      modified:   index.html
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#      modified:   lib/simplegit.rb
#
Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)

Это удобное сокращение для того, чтобы легко восстановить спрятанные изменения и поработать над ними в новой ветке.

Очистка вашей рабочей директории

Наконец, у вас может возникнуть желание не прятать некоторые из изменений или файлов в вашей рабочей директории, а просто избавиться от них. Команда 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.

$ 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>

Таким образом, вы сможете пройтись отдельно по каждому или выбранным с помощью шаблона файлам для их удаления в интерактивном режиме.