Git
前言
Git在现在应该是使用得最多的版本管理工具了,本片博客将持续更新它的使用方法,以及在平时开发中遇到的问题及解决方法,至于它与SVN或者其它的工具有什么区别这里就不谈了,不是重点
仓库(repository)
在Git中,我们将需要控制的文件目录叫做一个仓库,每个仓库可以简单理解成一个仓库。假如现在建立一个仓库,那这个文件夹中就有以下文件
-
WorkSpace文件夹:需要通过git进行版本控制的项目文件,通常是你的项目文件夹,除了.git文件夹之外的都属于工作区
-
.git 文件夹:创建仓库的时候自动创建,所有的版本信息,操作记录全在这个文件夹里面,尽量不要修改或者删除里面的文件
一个仓库分区图如下:
其中的
Index/Stage:暂存区或者待提交更新区,在提交进入仓库前,所有add命令操作的更新放在暂存区
Local Repository:存放在本地的仓库,指当前的开发分支
Stash:工作状态保存栈,用于保存或者恢复工作区的临时状态,后续会有样例进行使用
在.git目录下有以下文件
- config: 包含了项目特有的配置选项
- info: 目录保存了一份不希望在 .gitignore 文件中管理的忽略模式的全局可执行文件
- hooks:保存了客户端或者服务端脚本
- objects:存储所有数据内容
- refs:存储指向数据的提交对象的指针,里面既有stash栈指针和tag等
- HEAD:文件指向当前分支
- index:文件保存了暂存区信息
git clone
在公司里我们做的最多的就是拿到远程仓库地址,然后使用命令将远程仓库拉到本地
先进入你要存放仓库的目录,然后右键,选中git bash,这是一个git的命令行工具,输入以下命令:
git clone https://github.com/xxxxx/xxxxxx.git 或者
git clone ssh://github.com/xxxxx/xxxxxx.git
git支持多种协议,但通过ssh支持的原生git协议速度最快
git branch
代码拉下来后,我们一般不在master分支提交代码,需要切换到开发分支进行操作,可以通过以下命令进行操作
列举分支
- git branch : 查看本地当前分支
- git branch -r:查看远程仓库分支
- git branch -a:查看所有分支,包括本地和远程的
比如所有分支情况如下:
~/mxnet$ git branch -a
* master
devp
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/devp
可以看到 * master 表示我们本地当前分支,remotes/origin/master和remotes/origin/devp是远程仓库的两个分支,第二行意思是指向当前远程分支
切换分支
这时候我们需要切换到devp分支,使用如下命令
$ git checkout devp
新建分支
如果需要新建分支,使用如下命令
$ git branch new_branch
新建并切换分支
在上面的例子中,分别使用两个命令创建和切换分支。 Git为checkout命令提供-b选项; 此操作将创建一个新的分支,并立即切换到新分支。
$ git checkout -b devp
上面是在本地操作,还可以checkout远程分支,如下:
$ git checkout -b devp origin/devp
checkout远程的分支devp,在本地命名为devp分支,并进入本地的devp分支
删除分支
可以通过向git branch命令提供-D选项来删除分支。,但在删除现有分支之前,请切换到其他分支
$ git branch -D new_branch
重命名分支
假设需要在项目中添加对宽字符的支持。并且已经创建了一个新的分支,但分支名称需要重新命名。那么可通过使用-m选项后跟旧的分支名称和新的分支名称来重新命名分支名称
$ git branch -m new_branch wchar_support
git pull
git pull命令的作用是:取回远程主机某个分支的更新,再与本地的指定分支合并,它的完整格式如下:
$ git pull [options] [<repository> [<refspec>…]]
$ git pull <远程主机名> <远程分支名>:<本地分支名>
-
比如,要取回origin主机的next分支,与本地的master分支合并,需要写成下面这样
$ git pull origin next:master
-
如果远程分支(next)要与当前分支合并,则冒号后面的部分可以省略
$ git pull origin next
上面命令表示将远程origin主机的next分支拉取过来和本地的当前分支进行合并。实质上,这等同于先做git fetch,再执行git merge
$ git fetch origin next:master $ git merge master
在某些场合,Git会自动在本地分支与远程分支之间,建立一种追踪关系(tracking)。比如,在git clone的时候,所有本地分支默认与远程主机的同名分支,建立追踪关系,也就是说,本地的master分支自动”追踪”origin/master分支
如果当前分支与远程分支存在追踪关系,git pull就可以省略远程分支名
$ git pull origin
上面命令表示,本地的当前分支自动与对应的origin主机”追踪分支”(remote-tracking branch)进行合并
如果当前分支只有一个追踪分支,连远程主机名都可以省略
$ git pull
上面命令表示,当前分支自动与唯一一个追踪分支进行合并
Git也允许手动建立追踪关系
$ git branch --set-upstream master origin/next
上面命令指定本地master分支追踪origin/next分支
git fetch和git pull的区别
-
git fetch:相当于是从远程获取最新版本到本地,不会自动合并
$ git fetch origin master $ git log -p master..origin/master $ git merge origin/master
以上命令的含义:
- 首先从远程的origin主机的master分支下载代码到本地的origin master
- 然后比较本地的master分支和origin/master分支的差别
- 最后把远程下载下来的代码合并到本地仓库
上述过程其实可以用以下更清晰的方式来进行:
$ git fetch origin master:tmp //从远程的origin仓库的master分支下载到本地并新建一个分支temp $ git diff tmp //比较master分支和temp分支的不同 $ git merge tmp//合并temp分支到master分支 $ git branch -d temp//删除temp分支
-
git pull:相当于是从远程获取最新版本并merge到本地
git pull origin master
上述命令其实相当于git fetch 和 git merge
在实际使用中,git fetch更安全一些,因为在merge前,我们可以查看更新情况,然后再决定是否合并
git add
当我们正常进行项目开发后需要将修改的文件和新增的文件推送到远程仓库,首先进行add命令,将内容添加到暂存区
add命令有如下几种:
-
git add msg.txt:将msg.txt文件添加到暂存区
-
git add document/:将document整个目录提交到暂存区
-
git add document/*.txt:将document目录下以.txt结尾的文件提交到暂存区
-
git add . :这个 . 表示当前目录所有文件及文件夹,会监控工作区的状态树,包括所有内容修改的文件和新文件,但不包括被删除的文件,然后提交到暂存区
-
git add -u . :仅监控已经被add过的文件(即tracked file),会将被修改的文件和删除的文件提交到暂存区,
-
git add -A . :第4个命令和第5个命令的合集,表示将所有已跟踪的文件的修改和删除,未被跟踪的新增文件都提交到暂存区
-
git add -i . :查看所有文件的状态信息,不包括新增且没被提交到暂存区的文件
当我们习惯性的通过git add . 命令将所有文件添加到了暂存区,但是其实我们想其中一个文件不要被提交到暂存区,这就需要撤销这个文件的add操作,这时使用 git reset HEAD 文件名 进行撤销。该文件就回到了add之前的状态。
当我们对已被追踪的文件进行错误的修改后,希望取消这些修改(这些修改没有被add),回到修改前的状态,使用git checkout – 文件名
git commit
当将修改的文件,新增的文件,删除的文件进行add操作添加到暂存区后,需要提交到本地仓库,提交命令如下
-
git commit -m “此次提交描述”
-m 表示在后面追加此次提交的备注信息, 当我们要输入的这个备注信息很长,可以使用如下命令 git commit -m ' message1 message2 ' 这样提交操作就完成了; 如果不加这个 -m 参数,只有git commit ,那么敲回车键后会调用一个编辑器,默认是vim来让你输入这个 备注信息; 输入后怎么退出这个编辑器回到命令行呢,按下 esc ,这样就会退出编辑状态,然后连续按两次大写字母 Z ,这样就会 正常回到命令行界面。
-
git commit -a -m “此次提交描述”
加-a参数可以将所有已跟踪文件中的执行修改或删除操作的文件都提交到本地仓库,即使它们没有经过git add添加到暂 存区,注意,新加的文件(即没有被git系统管理的文件)是不能被提交到本地仓库的。建议一般不要使用-a参数,正 常的提交还是使用git add先将要改动的文件添加到暂存区,再用git commit 提交到本地版本库。
git push
当代码提交到本地仓库后要做的就是推送到远程仓库,推送命令如下
git push 的一般形式为 git push <远程主机名> <本地分支名> : <远程分支名>
-
git push origin master: master
意思是将本地master分支推送到远程主机origin上的master分支, origin是主机名,第一个master是本地分支名,第二个是远程分支名。 可以使用git branch 查看本地分支,git branch -r查看远程分支
-
git push origin master
如果远程分支名忽略,表示将本地分支推送到与之存在追踪关系的远程分支(通常两者同名),如果远程分支不存在, 则会被新建一个远程分支,可以通过git branch -vv 命令 查看本地分支与远程分支的追踪关系
-
git push origin : master
如果本地分支名省略,表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支, 等同于git push origin --delete master (master是远程分支名)
-
git push origin
如果本地分支和远程分支都省略,表示将当前分支推送到origin主机对应的分支,前提是当前分支和远程分支存在追踪关系
-
git push
如果当前分支只有一个远程分支,那么主机名都可以省略
-
git push -u origin master
如果当前分支与多个主机存在追踪关系,则可以使用 -u 指定一个远程主机, 意思就是将本地master分支推送到origin主机,同时指定origin为默认主机,这样之后就可以不加参数直接使用git push
-
git push --all origin
表示将所有本地分支推送到远程主机上 如果远程主机的版本比本地版本更新,推送时Git会报错,要求先在本地做git pull合并差异,然后再推送到远程主机
-
git push --force origin
强制将本地所有分支推送到远程主机,可能会导致在远程主机产生一个”非直进式”的合并,不建议这么做
-
git push origin master : refs/for/master
表示推送到远程master分支的代码需要经过code review之后才能进行merge的,而使用refs/heads/ 就不会检查, 直接推送到远程分支
恢复被删除/被编辑的文件
假如在操作过程中误删了文件或文件夹,也有可能是你就想删除这个文件夹;删除了以后怎么恢复呢?
这时候需要使用git checkout 命令
-
第一步
通过git status命令查看文件状态
lenovo@DESKTOP-L1PKA9E MINGW64 /d/Web/ISP/ISPServerv1.0 (master) $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) deleted: sql/isp.sql modified: src/main/resources/application.properties no changes added to commit (use "git add" and/or "git commit -a")
可以看到这里我将isp.sql这个文件删掉了
-
第二步
使用git checkout sql/isp.sql,这个命令的意思就是放弃对工作目录中的更改
lenovo@DESKTOP-L1PKA9E MINGW64 /d/Web/ISP/ISPServerv1.0 (master) $ git checkout sql/isp.sql
这样这个被你删除的文件就又恢复了
其实不光被删除的文件,包括对已被追踪的文件进行的修改都可以使用这个命令恢复到修改前的状态,前提是没有对这些修改使用add命令
撤销add命令
有时候你会碰到这种情况,我修改了某些文件,并将其通过add命令添加到暂存区了,但是此刻我发现提错了,需要撤回,这时候就可以使用git reset命令了,如下:
-
查看状态
lenovo@DESKTOP-L1PKA9E MINGW64 /d/Web/ISP/ISPServerv1.0 (master) $ git status On branch master Your branch is up-to-date with 'origin/master'. 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: sql/isp.sql no changes added to commit (use "git add" and/or "git commit -a")
可以看到isp.sql文件被修改了
-
接下里通过 git add sql/isp.sql 命令添加到暂存区,再次查看状态
lenovo@DESKTOP-L1PKA9E MINGW64 /d/Web/ISP/ISPServerv1.0 (master) $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: sql/isp.sql
可以看到这个文件正在等待commit
-
这时候发现文件有错误,想要撤回刚才的add操作,就可以通过reset命令,如下
lenovo@DESKTOP-L1PKA9E MINGW64 /d/Web/ISP/ISPServerv1.0 (master) $ git reset HEAD sql/isp.sql Unstaged changes after reset: M sql/isp.sq
这时候再查看状态
lenovo@DESKTOP-L1PKA9E MINGW64 /d/Web/ISP/ISPServerv1.0 (master) $ git status On branch master Your branch is up-to-date with 'origin/master'. 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: sql/isp.sql no changes added to commit (use "git add" and/or "git commit -a")
这样就回到了add操作前的状态了
注意:这时候你要想把文件内容也恢复到修改前的内容,可以通过checkout命令将本地仓库的内容覆盖掉该文件
lenovo@DESKTOP-L1PKA9E MINGW64 /d/Web/ISP/ISPServerv1.0 (master)
$ git checkout sql/isp.sql
这时候再查看状态
lenovo@DESKTOP-L1PKA9E MINGW64 /d/Web/ISP/ISPServerv1.0 (master)
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
该文件就跟仓库里的一模一样了,抹除了你的修改
处理冲突
有时候使用git pull命令会出现以下提示
lenovo@DESKTOP-L1PKA9E MINGW64 /d/Web/ISP/ISPServerv1.0.local (master)
$ git pull
error: Your local changes to the following files would be overwritten by merge:
sql/isp.sql
src/main/resources/mybatis/mybatis-config.xml
Please commit your changes or stash them before you merge.
Aborting
Updating 086076a..e2b5535
出现这个的原因就是同一个文件同事修改过并且将修改提交到了远程仓库,这时候你也对这个文件进行了修改,那么直接拉取代码会覆盖掉本地你做的修改,所以你要做的就是要么先commit你的修改,要么使用stash命令先备份
一般情况下不直接使用commit,因为会导致冲突,且会将冲突提交到远程仓库
所以就是用stash命令,使用方法如下:
-
第一步:备份
lenovo@DESKTOP-L1PKA9E MINGW64 /d/Web/ISP/ISPServerv1.0.local (master) $ git stash
这个命令做的就是将工作区的代码恢复到上次提交的内容,并且将你本地的所做的修改进行备份
-
第二步:恢复且备份后就可以拉取代码了
lenovo@DESKTOP-L1PKA9E MINGW64 /d/Web/ISP/ISPServerv1.0.local (master) $ git pull
-
第三步:恢复备份
lenovo@DESKTOP-L1PKA9E MINGW64 /d/Web/ISP/ISPServerv1.0.local (master) $ git stash pop
这个命令就是从最近的一个stash中读取内容并恢复工作区的相关内容,也就是将第一步备份的修改合并到本地代码,有可能会产生冲突,需要手动在文件中进行修改
注意:git中由于可能存在多个Stash的内容,所以用栈来管理,pop会从最近的一个stash中读取内容并恢复。
git stash list: 显示Git栈内的所有备份,可以利用这个列表来决定从那个地方恢复。
git stash clear: 清空Git栈。此时使用gitg等图形化工具会发现,原来stash的哪些节点都消失了
dst ref refs/heads/master receives from more than one src
git push origin master : master
当我使用这个命令进行提交的时候,报错如下
error: dst ref refs/heads/master receives from more than one src.
error: failed to push some refs to 'http://xx.xx.xx.xx/xx/xxv3.00.xx.git'
出现这种情况是因为我们的命令里出现了两个空格,即冒号与分支名之间存在空格
其实对于用户来说,是由于空格导致的问题,毕竟在shell中由于空格出现的问题不少
但是对于git来说,多一个空格就多了一个参数,
本来只需要【push】 【origin】 【master:master】 三个参数
但是我却给了5个参数【push】 【origin】 【master】 【:】 【master】