checkout进阶与stash

checkout --讲解

我们先查看下git status是否正常,然后再编辑test1.txt增加了no hallo:

$ cat test1.txt
hello
world
hello world
hello hello hello
no hello

这时候我们再查看一下:

$ git status
On branch master
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:   test1.txt

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

发现是处于工作区了,我们使用git checkout --命令试试:

$ git checkout -- test1.txt

再查看一下:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ git status
On branch master
nothing to commit, working tree clean

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ cat test1.txt
hello
world
hello world
hello hello hello

这时候就可以发现添加的no hallo已经不见了,并且git的状态也恢复正常了

我们这时候还是修改test1.txt,对其加入no hallo:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ cat test1.txt
hello
world
hello world
hello hello hello
no hello

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ git status
On branch master
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:   test1.txt

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

然后执行git add .,再查看一下:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ git add .

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   test1.txt

我们如果再次修改test1.txt会发生什么了?我们现在只是将工作区的第一次修改的test1.txt提交到了暂存区,试试吧:

新的修改:must hello

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ vim test1.txt

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ cat test1.txt
hello
world
hello world
hello hello hello
no hello
must hello

git status查看:

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   test1.txt

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:   test1.txt

我们这时候再试试git checkout --:

$ git checkout -- test1.txt

查看内容变化:

$ cat test1.txt
hello
world
hello world
hello hello hello
no hello

发现第一次做的修改no hello保留下来了的,是保存在缓存区的,而第二次修改的must hello则没有了

git status查看状态:

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   test1.txt
总结:git checkout -- test1.txt

作用是:丢弃掉相对于暂存区中最后一次添加文件内容所做的修改

验证:

我们执行git reset HEAD命令将缓存区的no hallo放回到工作区:

$ git reset HEAD test1.txt
Unstaged changes after reset:
M       test1.txt

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ git status
On branch master
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:   test1.txt

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

再执行git checkout --:

$ git checkout -- test1.txt

查看内容:

$ cat test1.txt
hello
world
hello world
hello hello hello

查看状态:

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

checkout回退版本

我们原来学过git checkout切换分支,其实它也可以用来切换版本,但与我们上章所学的git reset --hard又有不同

git log查看一下:

commit 3573a2322e2590fa8abcf6dca93b2ea6b01b9506 (HEAD -> master)
Author: YQHP-YuKi <************@qq.com>
Date:   Sun Nov 22 16:02:40 2020 +0800

    3hello in test1.txt

commit b38e36d4fb88dcb629c855972eb86e5e9cbdf2b9
Author: YQHP-YuKi <***********@qq.com>
Date:   Sun Nov 22 15:58:38 2020 +0800

    hello world in test1.txt

commit 9ed5c2398694d005e7abf2468574d0fc91a42189
Author: YQHP-YuKi <***********@qq.com>
Date:   Sun Nov 22 15:57:34 2020 +0800

    world in test1.txt

commit 61adf63007461e6e5d79b19aa0ff09ff61033d76
Author: YQHP-YuKi <***********@qq.com@qq.com>
Date:   Sun Nov 22 15:53:29 2020 +0800

    hello test1.txt

假如这时候我们想回到9ed5c这个版本,我们可以这样做:git checkout+版本号:

$ git checkout 9ed5c
Note: switching to '9ed5c'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at 9ed5c23 world in test1.txt

翻译:现在你已经进入到了一个游离的状态,你可以四周查看一下,做一些环境的改变,如果你想创建一个新的分支,你可以用git switch -c <new-branch-name>,现在你的HEAD不再是指向master,而是指向 9ed5c23 world in test1.txt

我们查看一下test1.txt:

$ cat test1.txt
hello
world

查看历史:

$ git log
commit 9ed5c2398694d005e7abf2468574d0fc91a42189 (HEAD)
Author: YQHP-YuKi <*************@qq.com>
Date:   Sun Nov 22 15:57:34 2020 +0800

    world in test1.txt

commit 61adf63007461e6e5d79b19aa0ff09ff61033d76
Author: YQHP-YuKi <************@qq.com@qq.com>
Date:   Sun Nov 22 15:53:29 2020 +0800

    hello test1.txt

我们对这个历史版本不做任何操作尝试回到master试试:

$ git checkout master
Previous HEAD position was 9ed5c23 world in test1.txt
Switched to branch 'master'
A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ git branch
* master

发现又正常回到了master且没有任何多的分支增加,这几次操作的图片讲解:

 title=

我们最先前是在master,执行git checkout 9ed5c后,HEAD不再指向master,而是指向9ed5c,所以只能看到先前的两次提交信息,看不到在此版本与master之间的任何提交信息

那我们再回到9ed5c,并对其中的test1.txt进行改动试试:

增加了checkout back

$ cat test1.txt
hello
world
checkout back

再尝试返回到master:

$ git checkout master
error: Your local changes to the following files would be overwritten by checkout:
        test1.txt
Please commit your changes or stash them before you switch branches.
Aborting

但是出现了报错:错误,你当前的改变文件将会重写test1.txt,请提交你的改变或者stash他们在你改变分支前

那我们commit一下:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile ((9ed5c23...))
$ git add .
A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile ((9ed5c23...))
$ git commit -m "commit on 9ed5c again"
[detached HEAD 00fd224] commit on 9ed5c again
 1 file changed, 1 insertion(+)

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile ((00fd224...))

这时候就发生了变化,我们从9ed5c23变为了00fd224,查看历史看看:

$ git log
commit 00fd224b50c9ed5390257617220ad0e35064571b (HEAD)
Author: YQHP-YuKi <**************@qq.com>
Date:   Wed Nov 25 09:31:10 2020 +0800

    commit on 9ed5c again

commit 9ed5c2398694d005e7abf2468574d0fc91a42189
Author: YQHP-YuKi <**************@qq.com>
Date:   Sun Nov 22 15:57:34 2020 +0800

    world in test1.txt

commit 61adf63007461e6e5d79b19aa0ff09ff61033d76
Author: YQHP-YuKi <**************@qq.com@qq.com>
Date:   Sun Nov 22 15:53:29 2020 +0800

    hello test1.txt

这时候的状态图:

 title=

我们再尝试去切换回到master:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile ((00fd224...))
$ git checkout master
Warning: you are leaving 1 commit behind, not connected to
any of your branches:

  00fd224 commit on 9ed5c again

If you want to keep it by creating a new branch, this may be a good time
to do so with:

 git branch <new-branch-name> 00fd224

Switched to branch 'master'

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)

这时候就只是警告了:警告你正在离开一个落后的commit,没有与你的分支有任何联系,提交信息为: 00fd224 commit on 9ed5c again,如果你想保存就创造一个新的branch,这时候也许是个好时间去创造,命令为: git branch 00fd224

这时候的状态图:

 title=

创建一个新的分支:

$ git branch newcommot 00fd224

查看下:

$ git branch
* master
  newcommot

我们切换到newcommit看看:

$ git log --graph
* commit 00fd224b50c9ed5390257617220ad0e35064571b (HEAD -> newcommot)
| Author: YQHP-YuKi <1465722762@qq.com>
| Date:   Wed Nov 25 09:31:10 2020 +0800
|
|     commit on 9ed5c again
|
* commit 9ed5c2398694d005e7abf2468574d0fc91a42189
| Author: YQHP-YuKi <1465722762@qq.com>
| Date:   Sun Nov 22 15:57:34 2020 +0800
|
|     world in test1.txt
|
* commit 61adf63007461e6e5d79b19aa0ff09ff61033d76
  Author: YQHP-YuKi <1465722762@qq.com@qq.com>
  Date:   Sun Nov 22 15:53:29 2020 +0800

      hello test1.txt

可以看到我们在9ed5c上面的新提交00fd2这个已经成为了一个新的分支

此时的状态图:

 title=

总结:

  • 通过checkout进行版本回退会造成游离的提交对象链,需要额外创建一个分支进行保存
  • 因此,使用checkout进行版本回退的思路为,先切换到想要回退的提交版本,再删除进行版本回退的分支dev,最后,创建一个新的dev分支指向游离的提交对象链,完成分支dev的版本回退
  • 只要有分支指向,提交就不会被丢弃

stash

我们在msater执行git status检查一下:

master:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ git status
On branch master
nothing to commit, working tree clean

然后创建一个新的分区test:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ git checkout -b test
Switched to a new branch 'test'

然后回到master上修改test1.txt,并将其提交:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ vim test1.txt

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ git commit -am "stash in master"
[master 69d720d] stash in master
 1 file changed, 1 insertion(+)

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ git status
On branch master
nothing to commit, working tree clean

查看下修改的test1.txt:

$ cat test1.txt
hello
world
hello world
hello hello hello
stash in master

这时候我们回到test分支,也对test1.txt进行修改,并进行查看一下:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ cat test1.txt
hello
world
hello world
hello hello hello
status in test

假如这时候你公司叫你回到master进行一个另一个文件的修改或者版本控制,这时候你想回到master分支:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git checkout master
error: Your local changes to the following files would be overwritten by checkout:
        test1.txt
Please commit your changes or stash them before you switch branches.
Aborting

翻译:你本地的改变的test1.txt文件会被checkout所重写覆盖,请将文件进行提交或者stash在你改变分支之前

我们尝试将文件从工作区放进缓存区试试:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git add .

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git status
On branch test
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   test1.txt


A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git checkout master
error: Your local changes to the following files would be overwritten by checkout:
        test1.txt
Please commit your changes or stash them before you switch branches.
Aborting

但是还是出现这个报错,说明无论是工作区还是缓存区的修改都会被checkout所覆盖,原因如图:

 title=

我们最先前的msater已经做出了改变,但这个test分支还是在第一次的master的分支上分离出来的,是处于一个游离的状态,所以现在要切换到现在的msater,就会被现在的master所覆盖

这种情况在工作日常开发中很常见,但在develop分支上开发新功能的时候,master分支出现紧急情况需要切换回去进行修复,但是当前分支的新功能还没完全开发完全,贸然切换分支,会导致所做的修改而被覆盖,而且也不可以随意的commit,因为你这分支版本还是处于测试版本,不能随意提交到版本库中,这时候你就需要用到stash

我们将缓存区的test1.txt文件放回到工作区:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git reset HEAD test1.txt
Unstaged changes after reset:
M       test1.txt

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git status
On branch test
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:   test1.txt

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

然后执行git stash:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git stash
Saved working directory and index state WIP on test: 3573a23 3hello in test1.txt

查看下test1.txt:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ cat test1.txt
hello
world
hello world
hello hello hello

发现所做的修改stash in test没有了,这时候我们再切换到master:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git checkout master
Switched to branch 'master'

发现没问题了,再切换到test分支并且查看test1.txt:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (master)
$ git checkout -
Switched to branch 'test'

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ cat test1.txt
hello
world
hello world
hello hello hello

发现我们所做的修改还是没有,那stash in test到底在哪了?

  • 其实通过了git stashtest分支上工作区或暂存区中的修改,提交到了stash区域进行保存,并将test分支退回到修改前的状态,如下图所示:

 title=

  • 切换到master分支时test分支上的修改依旧会被覆盖,所以,再次回到test分支时需要从stash区域中恢复切换分支前所保存的修改
查看stash日志

我们可以通过git stash list查看stash的版本日志:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git stash list
stash@{0}: WIP on test: 3573a23 3hello in test1.txt

注:后面的WIPworking in process的意思,表示的是正在进行的工作

这样就看到我们先前stash in test的日志了,编号为stash@{0},我们也可以对stash日志进行每个日志命名,例如我们再次修改test1.txt:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ vim test1.txt

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ cat test1.txt
hello
world
hello world
hello hello hello
stash try again

通过参数save可以命名,例如:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git stash save 'stash try again'
Saved working directory and index state On test: stash try again

git stash list查看一下:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git stash list
stash@{0}: On test: stash try again
stash@{1}: WIP on test: 3573a23 3hello in test1.txt

这样我们就看到1的第一提交,0第二次提交的try again

想要恢复stash所做的修改前模样,主要有三种方法:

  • git stash pop
  • git stash apply
  • git stash apply stash@{number}
git stash pop

我们使用git stash pop看看:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git stash pop
On branch test
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:   test1.txt

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (6e4d554894d1db504cf525db30b3f7196ce1478f)

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ cat test1.txt
hello
world
hello world
hello hello hello
stash try again

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git stash list
stash@{0}: WIP on test: 3573a23 3hello in test1.txt

发现第二次修改的try again stash恢复回来了,git stash list中的第二次历史信息没了,而且状态也变为工作区了

git stash pop作用:恢复并删除stash中存储的最新修改

git stash apply

我们再使用git stash apply试试:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git stash apply
On branch test
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:   test1.txt

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

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ cat test1.txt
hello
world
hello world
hello hello hello
stash try again

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git stash list
stash@{0}: On test: try again stash
stash@{1}: WIP on test: 3573a23 3hello in test1.txt

可以发现跟pop相比,也是恢复到了工作区,但是git stash list中的历史记录还是存在

git stash apply:恢复但不删除stash中存储的最新修改

如果我们想删除git stash list中的记录,我们需要手动删除,命令是:git stash drop stash@{number},例:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git stash drop stash@{0}
Dropped stash@{0} (8307e8403059797eed5d2c4f1356649146e167d1)

再查看一下list:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git stash list
stash@{0}: WIP on test: 3573a23 3hello in test1.txt
A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ cat test1.txt
hello
world
hello world
hello hello hello
stash try again

就发现没了,而且文件也改回来了,如果这时候我们使用git stash pop,会发生什么了?

$ git stash pop
error: Your local changes to the following files would be overwritten by merge:
        test1.txt
Please commit your changes or stash them before you merge.
Aborting
The stash entry is kept in case you need it again.

这时候又出现要求你提交要不就stash的报错,那我们进行提交:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git commit -am 'apply stash'
[test a5300dd] apply stash
 1 file changed, 1 insertion(+)

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git status
On branch test
nothing to commit, working tree clean

我们再执行git stash pop:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ git stash pop
Auto-merging test1.txt
CONFLICT (content): Merge conflict in test1.txt
The stash entry is kept in case you need it again.

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ cat test1.txt
hello
world
hello world
hello hello hello
<<<<<<< Updated upstream
stash try again
=======
status in test
>>>>>>> Stashed changes

这时候发生了合并冲突,这是因为,stash中保存的每一次修改代表的都是一个版本:

 title=

  • 如上图所示,在test分支上,进行第一次修改后,通过git stash将该修改作为修改0保存到stash中,此时分支中的文件并没有发生改变
  • 进行第二次修改后,通过git stash将修改作为修改1保存到stash中,分支中的文件依旧没有发生改变,此时的stash中相当于保存着同一分支上两个修改后的版本
  • 此时通过git stash apply取出0,与test分支进行合并,再通过git stash pop取出1,再次与test分支进行合并,两个版本合并自然会发生冲突

如果想解决冲突,则vim修改文件即刻:

A@DESKTOP-6DP8MG1 MINGW64 /e/Gitfile (test)
$ cat test1.txt
hello
world
hello world
hello hello hello
status in test
git stash apply stash@{0}

指定恢复的版本,就跟git reset指定log一样

总结:
  • git stash pop:恢复并删除stash中存储的最新修改
  • git stash apply:恢复但不删除stash中存储的最新修改
  • git stash apply stash@{0}:恢复但不删除stash中存储的特定提交

画图图片转载:

AhuntSun

最后修改:2023 年 05 月 10 日
如果觉得我的文章对你有用,请随意赞赏