Git快速入门(03)Git核心命令使用
1. 创建git项目
比如目前是在demo文件夹中,那么
git init
可以吧demo文件夹创建为git的根目录:

创建完成后可以看到,demo文件夹下面多了一个.git文件夹,里面存放的是git仓库的所有文件和内容:

里面的内容包含了一个完整的git仓库,比如前面提到的元数据就存储在objects文件夹下面。
如果不想创建当前目录为git仓库,想把仓库创建在当前目录的子目录下面,比如test,可以执行下面的命令:
git init test
这样就创建了一个test仓库,进去可以看到.git文件夹:

2. 提交文件
在git仓库下面新建一个文件hello.txt,并写入内容“hello world 11”(新文件也可以从其他地方拷贝过来):

增加新文件后,来看一下仓库目前的状态:
git status

这个命令列出了一些仓库和文件的信息,其中hello.txt文件的名字为红色,代表文件本身还是独立的,没有交给git仓库来管理。要把文件提交到git里面,第一步就是要把文件添加到暂存区,使用add命令:
git add hello.txt

添加到暂存区后,文件名字变为绿色,git状态显示hello.txt是一个仓库新文件,现在的状态才允许我们去提交到git里面。
status命令打印出的内容也有一个提示,就是git rm名,这个命令允许我们把添加到暂存区的文件从暂存区删除:
git rm --cached hello.txt

可以看到把文件从暂存区删除以后,文件又恢复了新文件的一个状态,名字变为了红色。注意从git暂存区删除并不是从目录中删除,文件只是改变了状态。
有时候文件夹下面文件非常多,一个一个添加显然是很麻烦的,我们可以用批量添加到暂存区的命令来处理:
git add -A

文件添加到暂存区后,下一步就是提交到本地git仓库:
git commit hello.txt -m '提交hello文件 11'
其中-m后面的内容是对本次提交的一个注释。

可以看到提交命令打印出了git仓库变化的信息,一个文件发生了变化并新增到仓库中。再来看看仓库的状态:

这时候显示工作区很干净,没有要提交的文件。上面就是一个新文件从添加到提交到本地仓库的一个整个的流程。提交也有批量提交的命令:
git commit -am '快速提交所有'
如何回退到前面提交的版本呢?首先找到版本号,查看所有提交日志:
git log

commit后面的一长串序列化就是每次提交的唯一版本号,也是上一篇文章提到的元数据的id,第一行里面HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id:
git reset --hard abeb59745aed5ae0402d61d2defcc57b83b82cd1

再来看提交历史

显示的就是HEAD当前指向的版本和以前的提交,之后的提交并没有显示,那么如果我们想回到最新的一次提交,又忘了id了怎么办?可以用git reflog查看命令历史,以便确定要回到未来的哪个版本:
git reflog

打印出的内容中,HEAD指向的历史都打印了出来,显然第二个HEAD@{2}是最新的一次提交,我们可以把HEAD指向这个提交,前面的id只显示前几位,不过我们使用版本号的时候,只要唯一,输入前几位也可以:
git reset --hard 5d43c54

这样就又回来了。所以大家可以放心的在各个提交之间穿梭。
如果想要丢弃现在正在修改为部分,如何操作?你在已经提交的文件里又修改了一些内容,加了一些代码,没提交前发现这些东西是错误的,想把文件恢复到修改前的状态:

可以使用下面的命来丢弃对文件的修改:
git checkout -- hello.txt

如果丢弃前,发现已经添加到了暂存区,那么要先执行一条命令:
git reset HEAD hello.txt
然后再执行 git checkout -- hello.txt 就可以丢弃了。
如果已经提交了,那也很简单,直接使用上面的版本退回即可。如果已经推送到远程了,那只能再修改回来然后提交推送了。
如果项目优化后,其中一个文件已经彻底没用了,可以删除了,如何在git仓库中删除呢?首先要从操作系统文件夹中删除文件:

这时候查看仓库状态:

这时候有两种选择,一个是删除错了,需要恢复,那么直接从仓库里恢复即可:
git checkout -- delete.txt

还有一种情况是删除对了,但是现在只从文件夹中删除了,git中还没有删除,需要执行下面的命令:
git rm delete.txt
git commit -m 'delete delete.txt'

这样就从本地仓库中删除了。
3. 推送文件到远程仓库
本地仓库有了文件后,可以每次都推送到远程仓库,也可以积攒一批之后,整个推送过去。推送到远程仓库前,得先有一个远程仓库,可以自行注册github,gitlab,gitee等仓库:

然后在远程仓库中创建一个test项目:

可以看到项目连接的左侧显示有https和ssh两种传输方式,一般我们开发都是使用最快的ssh方式支持的原生git协议,使用这种方式需要做一下配置,首先初始化本地git用户信息:
git config --global user.name "Your Name"
git config --global user.email "Your email"
然后执行命令创建 SSH Key:
ssh-keygen -t rsa -C "Your email"

中间所有的停顿不用输入任何东西,直接回车即可。生成完后,进入用户目录下的.ssh文件夹中:
cd ~/.ssh

这里有两个文件,一个id_rsa,一个id_rsa.pub,我们打开id_rsa.pub文件,把里面的内容复制一份。进入远程仓库的个人设置页面的ssh key设置页码:

添加一个公钥:

点击确定,公钥就添加完成了。

注意即使一个账号,有多个电脑开发提交,也要为每个电脑生成公钥并添加到远程配置中。公钥设置完成后,就可以在本地仓库添加远程仓库的地址了:
git remote add origin git@gitee.com:blueses/test.git

其中origin是远程仓库的名字,也是git为默认远程仓库起的名字。下面就可以推送到远程了:

第一次推送,提示当前分支没有对应的远程分支,我们要把两个对应起来,那么第一次推送必须加上--set-upstream参数:
git push --set-upstream origin master

中间提问是否继续连接,输入yes,回车即可,打印出来的信息可以看到本地master分支已经设置为跟踪远程仓库的master分支,并已经将本地master分支的文件推送到了远程master上面。
第一次也可以使用命令:
git push -u origin master
效果一样。
上面的第一次添加远程仓库,第一次建立跟踪确实有点麻烦,不过添加远程仓库时也就是这一次,后面再推送就不会这么麻烦了。现在来看一下远程仓库:

可以看到远程仓库多了一个我们本地的文件,并显示了我们提交文件时写的注释,证明推送是成功的!
来修改一下文件的内容:

现在看一下仓库状态:

状态显示文件以及进行了修改,把修改后的文件提交到本地仓库也很简单,和前面一样,先加到暂存区:
git add hello.txt

然后提交到本地仓库:
git commit hello.txt -m '提交修改后的hello.txt 11-222'

现在向远程仓库推送第二次提交内容,就不会像第一次那么麻烦了:
git push origin

查看远程仓库变化:

我们自己的提交和推送的工作很顺利,如果别人想加入项目,拉取代码进行开发怎么办呢?需要使用到克隆命令,不过在下载代码之前,也需要本身生成ssh-key,然后配置远程自己的账户中,这个不再描述。
准备工作做好以后,接下来是克隆代码:
git clone git@gitee.com:blueses/test.git

可以看到克隆命令就是一个下载仓库的过程,在克隆的目录中,会生成一个项目名命名的文件夹:

如果想用别的名字,可以在路径后面加上想要命名的名字,比如想命名为hello:
git clone git@gitee.com:blueses/test.git hello
克隆完后进入文件夹,可以看到一个一模一样的git仓库:

新加入的人员也可以修改文件内容并提交:

保存后查看状态:

然后是添加到暂存:

提交到本地仓库:

由于新加入的人员是主动克隆的代码,在克隆的时候已经做了本地和远程分支的跟踪配置,所以可以直接推送:
git push origin
前面说过origin是默认远程仓库的名字,所以这个是可以省略的:
git push

查看远程仓库的变化:

原来的项目人员,或者说所有其他的项目人员也要继续工作,提交和推送。关于添加到暂存区,和提交到本地仓库中,两个都是本地操作,可以毫无顾忌的完成。但是推送到远程仓库前要注意,因为不知道远程仓库是否有变化,是否有他人的新的提交推送,所以,我们执行git push前往往都会先执行一次从远程更新到本地的操作:
git pull origin

注意上面的仓库名origin是默认的,所以同样可以省略:
git pull
执行完后再把本地提交推送到远程仓库:
git push
所以,上面的整个流程就是开发时的常用流程(添加远程仓库和克隆因为只进行一次所以不算),添加(add),提交(commit),更新(pull),推送(push),当然这个流程的前提是在一个分支上,多分支开发后面再说。
查看所有远程仓库:
git remote

查看远程详细信息:
git remote -v

可以看到除了一个push还有一个fetch,git fetch命令是用来同步信息的,比如远程新建了一个分支,本地是不能马上看到的,执行git fetch之后才能看到:

添加和删除远程仓库的命令是:
git remote add url remote_name
git remote remoge remote_name
如果是默认的远程仓库,那么推送和拉取命令中可以省略名字,其它的还是要加上,这样git可以操作多个远程仓库。如何知道默认的远程仓库是哪个呢?可以借助分支的命令:
git branch -avv

可以看到当前分支默认关联的是origin的master分支,所以不加名字会默认推送到origin下面的master分支。
4. 分支管理
使用git的都知道分支的管理。在使用中有很多人也遇到过各种风格。比如一个master分支一路走到底。还比如一个bug一个分支,一个需求一个分支。还有的是根据环境进行分支管理,有开发分支,测试分支,正式环境分支。等等各种风格都有,这里值得一说的是,没有最好的,只有最合适的,git之所以牛就是因为它适用于从最简单到最复杂的各种情况,只要团队使用方便,舒服,就是最好的分支使用方式。反之因为git分支的使用造成了开发的混乱,那就得不偿失了。
分支可以简单也可以复杂,那么分支有没有用呢?实践证明还是很有用的,比如现在已经二月份了,正在紧急开发,而线上运行的是一月份打包的版本,这个时候突然发现线上有个bug需要修改,这个时候在开发分支上改显然是不合适的,不仅和新代码新功能混在一起,测试和上线都是问题。最好的办法是从线上运行版本的那个时间点的提交的地方拉取一个分支,进行修改这样可以将影响最小化,修改完成测试通过上线以后,再讲修改的代码合并到其他分支。所以线上正式版本的分支最好用于是master分支,开发,测试,修改问题等等可以从线上拉取分支进行操作,这样可以将影响降到最低。分支的使用有很多地方,不止这一个例子。
使用分支也能体现git相对于svn的一个优势,以前使用svn切换分支,速度是相当慢的,git则不同,分支的切换速度是秒级的。
下面来看一些具体的分支命令,首先是查看当前分支:
git branch

可以看到当前只有一个本地分支,就是master。但是本地分支有一个,总的分支数量却不是一个,我们已经将远程仓库关联到了本地仓库中,所以本地还有一个远程仓库的分支,查看本地和远程所有分支的命令是:
git branch -avv

远程分支和本地分支已经进行了关联,那么它们是一个分支吗?其实并不是,下面来修改一下文件,然后进行提交操作,完成后,查看所有分支:

可以看到本地提交后,远程分支并且有随之改变,所以它们的内容并不一样,因此并不是同一个分支。来看一下本地分支文件的内容:

下面切换分支,切换到远程分支:
git checkout origin/master
然后查看文件内容:

可以看到远程分支的文件内容还是上一个版本的内容,因为我们还没有push,所以远程没有变化。切换回本地分支:

总结:
git branch 查看本地分支
git branch -a 查看本地和远程所有分支
git branch -av 查看所有分支的详细信息
git branch -avv 查看所有分支详细信息以及本地和远程分支的关联信息
git checkout 切换分支
下面来说说创建分支,首先基于本地当前正在使用的分支,创建一个新分支,比如基于本地master创建一个分支test:
git branch test

如果是基于非当前分支创建,比如基于test创建dev,那么需要加上test分支的名字:
git branch dev test

然后是基于远程分支创建一个本地新分支,比如基于远程master分支,创建一个本地demo分支:
git branch demo origin/master

然后是基于一个提交创建一个新分支,首先看一下有哪些提交:
git log

可以看到越下面的记录是越早的,第一次的提交是 02686ca5d87f9380d72ec845eb94d716e353d6f3 ,基于第一次提交创建一个分支 first:
git branch first 02686ca5d87f9380d72ec845eb94d716e353d6f3

其实本质上来说,基于分任何内容任何形式创建一个新分支,都是基于一个提交创建分支,这个后面讲底层原理的时候回更清楚的讲解。
还可以基于一个tag(标签)创建一个新分支,标签本质上可以理解为一个特殊的分支,它是只读的,因此更加安全,可以用来作为版本里程碑,查看所有标签:
git tag
基于当前分支创建一个标签
git tag v1.0

然后基于这个标签创建一个分支tagbranch:
git branch tagbranch v1.0

一个简单的命令是,创建并切换分支dev:
git checkout -b dev
下面来看一下删除分支,比如删除分支tagbranch:
git branch -d tagbranch
git branch -D 分支名(表示强行删除一个分支)

-d后面可以跟多个分支,用空格隔开,这样可以批量删除多个分支。
上面讲了创建分支,切换分支,删除分支,下面来看一下合并分支,首先整理分支,讲本地master与远程master保持一致:

基于当前master分支创建一个dev分支,然后切换到dev分支,修改并提交:

注意当前分支在dev上,我们需要把dev的内容合并到master上面,这时候先切换到master分支上,然后合并:
git checkout master
git merge dev (表示将dev分支的内容合并到当前分支,也就是master分支)
git push

上面显示的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。下面来看一下,创建并切换到一个新分支:
git checkout -b dev2

修改文件并提交,然后回到master分支:

准备合并dev2分支,请注意--no-ff参数,表示禁用Fast forward:
git merge --no-ff -m 'merge with no-ff' dev2

合并后,我们用git log看看分支历史:
git log --graph --pretty=oneline --abbrev-commit

git push到远程后,从远程仓库也能看到分支历史:

建议开发过程中充分利用分支,并且都使用普通模式,禁用Fast forward,否则看不出分支合并历史。
下面看一个问题,如果文件修改到一半还么有提交,恰好有个bug需要新建一个分支修改,但是现在的代码又不能提交怎么办?可以把正在进行的工作隐藏起来:
git stash

然后完成新建分支,修改bug,最后切换回来,并合并分支的过程,完成后需要恢复刚才改到一半的工作,修改查看stash列表:
git stash list

工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下:
git stash pop

这样就恢复到了修改一半但是没有提交的进度,是非常实用的功能!
很多人有这样的习惯,git push前先进行git pull,从理论上来说没啥问题,提交前先把远程代码拉到本地然后进行合并,最后将本地最新的结果推送到远程,这样是可以的,但是提交时会出现很多自动merge的情况,其实git多人开发也是可以一条直线干净的提交的。在最后push之前,可以进行rebase操作:
git rebase
然后再执行git push即可。
还有一些命令,比如在本地创建和远程分支对应的分支,使用
git checkout -b demo origin/demo
demo是分支名,本地和远程分支的名称最好一致。
5. 标签管理
标签可以理解为只读的一个分支,不能用来推送,可以下载,发布新版本往往会专门创建一个标签,创建一个新标签:
git tag tag1
创建一个标签也可以指定标签的信息:
git tag -a v1.0 -m "创建v1.0版本标签"
基于某个分支创建一个标签:
git tag tag2 master
基于某个提交创建一个tag:
git tag tag3 02686ca5d87f9380d72ec845eb94d716e353d6f3
查看标签列表:
git tag
删除标签
git tag -d tag_name

注意tag的名字最好不要和分支的名字一样!
推送一个本地标签到远程:
git push origin <tagname>

推送全部未推送过的本地标签:
git push origin --tags
删除一个远程标签(注意origin后面是一个空格):
git push origin :refs/tags/<tagname>

6. 日志管理
列出当前master下面所有的提交日志:
git log

来看最上面的一个记录:

commit右面的序列化表示最新的提交id,后面跟了一些分支和标签表示本次提交和那些分支标签同步,下面两行是提交人和提交时间,最下面是提交备注。
git log命令输入后,并不会马上退出,最下面是一个冒号,这里可以输入内容进行搜索:

输入左斜杠加上内容,然后回车,可以看到符合的内容开始高亮显示:

直接git log打印出来的内容是非常繁琐的,可以简化输出:
git log --oneline

可以看到省略了提交人和提交时间,把版本和注释放在了一行。
查看指定分支的提交:
git log dev --oneline

查看master分支上面有多少提交dev分支上是没有的:
git log dev..master
如果反过来就是查看dev上面有多少没有提交到master上:
git log master..dev

两个分支merge之后,互相对比才完全没有区别。
查看提交网络:
git log --graph

简化输出:
git log --graph --oneline

查看完整的提交id:
git log --graph --pretty=oneline

还有一个是我们上面讲分支的时候用到的:
git log --graph --pretty=oneline --abbrev-commit
展示当前本次改动的文件内容:
git show

也可以展示别的分支:
git show dev

以上就git常用的一些命令,在实际开发中大家基本上就是使用IDE操作git,但是建议大家能用命令的时候尽量用命令,这样可以加深我们对git的理解。
我们的交流基地,“JAVA互联网技术交流:789650498”欢迎小伙伴们一起来交流:
