Git常用命令详解
写在前面
- git是我们常用的VSC(版本控制)工具,很多小伙伴在使用git时只是做简单的提交、克隆、解决冲突、拉取代码、提交代码.这也就是我们所说的常规情况
- 在可视化图形工具泛滥的今天,如IDE自带的VSC管理工具、totoriseGit使得我们工作效率更高,但同时也让我们偏离了事情的本质(究竟什么是git?)
- 我一直认为做开发需要一点钻研精神,要具有独立解决git问题的能力就必须充分理解git的运作机制,面向百度编程是治标不治本的做法
基本概念
- 在真正开始之前我们再简单的讲一下git的几个基本概念
- 仓库
- git和svn不同,git本地就维护一个仓库(就好比是svn的远程仓).我们所有的操作都在本地完成,也只影响本地的仓库内容,直到我们将它上传到远程仓库
- 工作流
- git的本地仓库由git维护的三棵树组成
- 1.工作目录:工作目录持有实际的文件
- 2.缓存区(index):缓存区临时保存你的文件改动
- 3.commit history:提交历史,每一次提交都会生成一个提交号,并存储在树中
- git的工作流一般是工作目录->缓存区->提交
创建仓库:git init
- 作为一般的开发人员,是不会去使用这一步的,想想我们是如何获取项目的?一般都是使用git clone.但是我们还是简单的说一说
- git init有什么作用?
- 现在让我们新建一个文件夹,就叫做gitlearn吧
- 打开我们的命令行程序(cmd/bash/terminal),键入如下的命令
$ git init Initialized empty Git repository in xxx/Desktop/gitlearn/.git/
- 可以看到 git init命令在我们的gitlearn/.git文件夹下创建了一个仓库,打开我们的gitlearn文件夹.我们发现确实多了一个.git文件夹,原来如此.我们的仓库文件就存在于这个.git的隐藏文件夹下面
- 总结
- git init命令在我们当前文件夹下面创建了一个.git隐藏文件夹,该文件夹下面存储的git仓库相关的信息,也正是这个文件夹将我们的gitlearn文件夹标识为一个本地仓库.
.git文件夹下的内容非常丰富,有许多git实现的核心文件,这里我们就不展开解释了
保存工作空间的修改
git add
- git add的作用就是将我们工作空间中的文件添加到缓存区
- 下面我们新建一个add.txt文件夹,并将它添加到缓存区
感兴趣的小伙伴可以看一看我们add之前的状态,git会告诉我们当前工作空间还有一个叫做add.txt的文件没有被添加到缓存区.也就是说当前工作空间的改变对我们下次提交是无效的$ touch add.txt 让我们来编辑一下文件的内容:this is a add test file $ git add add.txt 到这里,我们就向缓存区添加了add.txt文件,我们可以使用git status命令查看 $ git status On branch master //指明了我们当前的分支 No commits yet Changes to be committed: //说明我们缓存区的文件(可被用于下次提交) (use "git rm --cached <file>..." to unstage) new file: add.txt
git commit
- git commit 将缓存区中的改动提交给本地仓库
- 下面我们将缓存区的文件提交给本地仓库
$ git commit -m 'first commit' //-m参数后面跟着我们的commit message [master (root-commit) 19ebc79] first commit 1 file changed, 1 insertion(+) create mode 100644 add.txt $ git log --oneline //我们使用git log可以查看当前的提交记录,oneline表示显示在一行 19ebc79 (HEAD -> master) first commit 注意这里19ebc79是一个简短的提交号,我们可以通过git log查看完整的提交编号
临时文件存储:git stash
- git提供了一个栈空间,让我们临时存储文件
- 什么时候需要临时存储?
- 当我们开始一个提交时,工作空间是不可以有非缓存区文件的,所以当我们修改了某个文件,但并不想在本次提交中提交时,我们需要将该文件放入stash栈缓存栈中
- 试想一下我们有以下情形
1.开发工作做到了一半,开发组长突然让我们修改一个紧急bug.我们该怎么做? 注意:如果你说先将当前分支的内容提交.的确是一种解决办法.但是不符合提交的定义,这不是一次规范的提交 1)先将工作空间的文件add到缓存区,将缓存区的文件存入stash栈 2)修改Bug,提交,push到远程分支 3)将stash栈中的内容弹出到缓存区和工作空间 2.开发到一半,突然发现写错分支了.我们应该在myBranch上开发,而不是当前的dev分支,怎么办? 1)将工作空间的文件add到缓存区,将缓存区的文件存入stash栈 2)checkout myBranch 3)将stash栈中的内容弹出到缓存区和工作空间
- 这样看来我们的stash栈是非常有用的
- git stash的使用
- git stash save [ “message”] :将缓存区和工作空间的文件存到栈中
$ touch stash.txt //先创建一个新的文件 $ git add stash.txt //加入缓存区 $ git stash save //将缓存区即工作空间中对应的文件放到栈中 Saved working directory and index state WIP on master: 19ebc79 first commit $ ls add.txt //你发现了什么?我们的stash.txt不见了.只剩下之前commit的add.txt文件 注意:如果工作空间中的文件没有加入到缓存区,是不会放入stash栈中的
- git stash list:查看当前的stash list
$ touch stash2.txt stash3.txt $ git add stash2.txt stash3.txt $ git stash save Saved working directory and index state WIP on master: 19ebc79 first commit $ git stash list stash@{0}: WIP on master: 19ebc79 first commit //第一次保存:stash.txt stash@{1}: WIP on master: 19ebc79 first commit //第二次保存:stash2.txt和stash3.txt 我们可以看到栈中有两次保存,第一次保存在栈底,第二次保存在栈顶 注意.第一次保存是stash.txt一个文件 第二次保存是stash2.txt和stash3.txt两个文件 此时我们的文件夹中就只剩下add.txt了
- git stash pop:弹出一次保存,当我们弹出一次保存时,栈中的这次保存就不存在了,文件再次回到了缓存区和工作空间
$ git stash pop on branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: stash2.txt new file: stash3.txt Dropped refs/stash@{0} (274c98c093d0f8eb65774c4b5fd4809b170228d9) $ git stash list //再执行一次看看 stash@{0}: WIP on master: 19ebc79 first commit $ ls //可以看到栈顶的存储内容又回到了工作空间和缓存区 add.txt stash2.txt stash3.txt
- git stash apply [@{编号}]:使用某次保存,但不会从栈中弹出,这适用于多个分支需要使用相同的文件的情形.如果不带编号.默认使用编号0
$ git stash apply $ git stash list stash@{0}: WIP on master: 19ebc79 first commit $ ls add.txt stash.txt stash2.txt stash3.txt 我们可以看到stash栈中的第一次存储没有被丢弃,但是stash.txt已经回到了工作空间和缓存区
- git stash drop:丢弃栈中的所有存储
查看仓库状态
git status
- 之前我们已经介绍过git status的用法
- git status是一个相对简单的命令,它告诉你git add和git commit的进展,status信息中还包含了添加缓存和移除缓存的相关指令
git log
- git log 用于查看我们的提交日志,常用的命令如下
- 1.git log :使用默认格式显示完整的项目历史
- 2.git log -n <limit> : 只显示最近的前次提交
- 3.git log --oneline: 将每个条信息压缩到一行,便于查看
前面我一共做了3次提交,第一次是first commit
第二次是将stash中的第二次存储pop出,提交了stash2.txt和stash3.txt
第三次是将stash中的编号0存储apply,提交了stash.txt
$ git commit --oneline
efbc959 (HEAD -> master) stash.txt
fa5447e stash2&3
19ebc79 first commit
检出之前的提交
- 检出(checkout):在git中有三个不同的作用
- 1.检出文件
- 2.检出提交
- 3.检出分支
- 在本节中我们只讲前两个应用
git checkout
- 检出提交:git checkout <commit>
- 检出某个分支可以让我们进入detached HEAD模式,该操作其实是创建一个临时分支,然后将HEAD分支定位到我们要检出的分支上.在临时分支上做的所有修改、提交都不会影响到主分支,如果我们要保存临时分支上的提交,需要将临时分支检出到一个新的分支上,可以使用git checkout -b <new-branch-name>
$ git checkout fa554e //我们之前通过git log查到,该分支是倒数第二次提交 Note: checking out 'fa5447e' ... 省略了一堆提示... HEAD is now at fa5447e stash2&3 $ git branch //查看所有分支的情况,发现我们在一个临时分支上 * (HEAD detached at fa5447e) master $ touch detach.txt $ git add detach.txt $ git commit detach -m 'detach' $ git branch -b detach //我们将临时分支的提交保存在临时分支detach Switched to a new branch 'detach' !!注意:如果我们直接切换到一个已有的分支,所有的commit将会被丢弃!! $ git log --oneline //再查看我们的日志,发现提交日志已经改变了 113abb1 (HEAD -> detach) detach fa5447e stash2&3 19ebc79 first commit !!注意:此时只是改变了我们detach的提交记录,master(假设我们之前就在master上开发)的提交记录是不变的
- 检出文件:git checkout <commit> <file>
- 检出某个提交的某个文件
- 如果不带commit id,则检出缓存区的文件,如果暂存区为空,指定的文件会回到上一次提交的状态,我们可以利用这个特性来回滚对某个文件的修改
- 总结
- 1.checkout <commit>不会改变当前分支的提交状态,这是因为git要保证数据的完整性,checkout 提交只是给了我们回溯到某次提交的能力.在此基础上我们可以另起分支在这个提交节点之后继续开发
回滚错误的修改
- 我们常常面临这样的问题:我错误的提交了某个快照,我该如何撤销他?
git revert(撤销)
- 撤销操作常常用来移除一整个提交
- git revert <commit> -m ‘描述’
$ touch wrong.txt $ git add wrong.txt $ git commit -m 'someting wrong' [master 502884e] someting wrong 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 wrong.txt $ git log --oneline //让我们来查看master上的提交 502884e (HEAD -> master) someting wrong efbc959 stash.txt fa5447e stash2&3 19ebc79 first commit $ git revert 502884e 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 wrong.txt $ git long --oneline //让我们再来看看master上的提交 7c02632 (HEAD -> master) Revert "someting wrong" 502884e someting wrong efbc959 stash.txt fa5447e stash2&3 19ebc79 first commit
是不是和你想的不太一样?git revert并没有撤销我们的提交记录,而只是撤销了某一次提交的内容并作了一次新的提交
git reset(重设)
- 重设往往用于移除一系列提交
- 如果说revert是安全的回滚,那么reset是一个危险的回滚
- reset操作将HEAD移动到某个版本,并将之后的提交全部清除
- 1.git reset <file> :从缓存区移除特定文件,但不改变工作目录
$ touch reset.txt $ git add reset.txt $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: reset.txt $ git reset reset.txt On branch master Untracked files: (use "git add <file>..." to include in what will be committed) reset.txt 可以看到 git reset 清空了 缓存区的rest.txt文件,它现在仅存在于工作区中
- 2.git reset :重设缓存区,匹配最近一次提交.所有 之后的更改会保留在工作目录中,这允许你用更干净、原子性的快照重新提交项目历史。
- 3.git reset --hard <commit>:将当前分支的末端移到 <commit>,将缓存区和工作目录都重设到这个提交。它不仅清除了未提交的更改,同时还清除了 <commit> 之后的所有提交。
现在我们要重置到第一次提交 $ git log --oneline 7c02632 (HEAD -> master) Revert "someting wrong" 502884e someting wrong efbc959 stash.txt fa5447e stash2&3 19ebc79 first commit $ git reset --hard 19ebc79 HEAD is now at 19ebc79 first commit $ git log --oneline 19ebc79 (HEAD -> master) first commit $ ls add.txt
我们的提交记录都不见了,工作区也只剩下add.txt.所以一定要慎用reset 命令
git clean
- git clean用于清理未跟踪(也就是工作区但未加入缓存)的文件
- git clean后的文件不可恢复,也需要慎用
总结
- 1.reset 会改变提交记录,所以我们一定要慎用,尤其是已经提交到公共仓的commit一定要禁用reset
- 2.revert是更加安全和灵活的回滚方式
结语
- 以上就是git常用的命令,这里我没有加入分支的部分(我承认.是我偷懒了),但分支是我们常用的基础功能,大家应该没有什么疑问.
- 要掌握git的工作原理,最主要的就是要弄清楚git的工作流:工作空间、缓存区、提交树
参考文章
- 1.https://github.com/geeeeeeeeek/git-recipes 大神写的比我要好得多,大家可以参考,只不过我的这篇文章细节更加丰富
- 2.https://www.cnblogs.com/zndxall/archive/2018/09/04/9586088.html
- 3.https://www.cnblogs.com/0616–ataozhijia/p/3709917.html