Git暂存及目录清理Stashing and Cleaning

git stash和git clean命令能够暂存和清理修改,在切换分支时经常需要借助git stash和git clean对工作空间进行处理。

git stash 命令会暂存对当前分支的所有操作,将未完成的修改保存到一个栈上,方便在任意时刻重新应用这些改动。

git 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或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")

现在工作目录干净了(回到了最近一次push的状态):

$ 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 stash apply默认使用最近的暂存内容:

$ 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会使应暂存的内容来恢复当时的修改。以--index选项运行git stash apply命令,git会重新应用斩存的内容,但不会从暂存列表中删除暂存条目。

$ 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 pop。单纯的删除操作使用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命令变种

Stashing有几个变种比较也很有用。

第一个非常流行的选项是git stash save命令的--keep-index用法,它用来告诉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将在创建存储文件中包含untracked文件。

$ 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

从暂存处创建分支

如果暂存(Stash)了一部分修改,把它放在那里一段时间,而后在这个分支继续开发,在重新应用这些修改时可能会出现问题:产生合并冲突,而且需要得解决这种冲突。有没有一个简单方法来应对这种问题,可以使用git stash branch <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清理工作目录(Cleaning your Working Directory)

对于工作目录中的一些文件,想做的可能不是stash而是删除,git clean命令用来处理这类事情。一个更安全的选项是运行git stash --all来移除内容并存放在栈中。

使用git clean命令去除冗余文件,或者清理工作目录。使用git clean -f -d命令来移除工作目录中,所有未追踪的文件以及空的子目录。 -f意味着 强制 或 “确定移除”。

如果只是想要看看它会执行什么样的操作,可以使用-n选项运行命令,这意味着 “做一次演习演示将要移除哪些内容”。

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

默认情况下,git clean 命令只会移除没有忽略的未跟踪文件。 任何与 .gitiignore 或其他忽略文件中的模式匹配的文件都不会被移除。 如果要移除这些文件,可以给git clean命令增加一个 -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命令将会做什么,在将 -f改为 -n,在真正执行git clean之前总是先用-n来做检查。另一个小心处理的方式是使用 -i或 “interactive” 标记来运行它。

git clean交互模式

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

这种模式下可以检查每一个文件或者要删除的内容。