git 基础
1、 git
在项目中使用git,一般有两种方式:一是先建立远程仓库,之后checkout到本地(或gitclone克隆到本地),将新建项目放置在本地仓库目录下;二是,先在本地建立git仓库,之后再关联到远程仓库,步骤如下:
1) git init
在本地目录创建一个空的仓库
2) git remote add origin xxx.git
关联到远程远程仓库
3) git pull origin master
抓取远程仓库数据,并自动合并远程分支
4) git pull origin master
推送到远程仓库
新建的项目只要位于仓库目录下,就可以在IDE中进行commit、update等操作了。
2、 Git版本管理
在Git中,每当文件修改到一定程度时,就可以“保存一个快照”,这个快照在Git中称为commit;也即每一次commit,就会产生一个快照。一旦误删了文件,就可以从最近的一个快照中恢复(查看提交的信息可以使用gitlog命令),然后继续工作。
在Git中恢复到那个快照版本,首先必须知道当前版本是哪个版本。在Git中,HEAD表示当前版本,也就是最新提交的版本,上一个版本就是HEAD^,上上一个版本就是HEAD^^,若往上的版本较多,可以写成HEAD~100,表示当前版本往上100个版本。
若要恢复到某一版本,可以使用gitreset命令,如下:
git reset --hard HEAD^
Git版本回退速度非常快,因为Git在内部指向当前版本的HEAD指针,当回退版本时,Git仅仅是把HEAD从指向回退到的版本位置,然后更新工作区的文件,如下
当回退到某一版本时,需要再次回退到最新版本,此时需要知道最新版本的提交的id,可以使用gitreflog来查看,之后才使用gitreset --hard commit_id就可以重新回到最新版本。
3、 工作区、暂存区
Git和其他版本控制系统一个不同之处就是暂存区的概念。工作区就是.git文件所在的目录,而.git目录并不算是工作区,而是Git的版本库。Git的版本库中存放了很多东西,其中最重要的就是称为stage的暂存区,以及master分支等,如下所示
向版本库添加文件的时候,是分两步执行的:
1) git add xxx,将文件添加到版本库,实际上就是把文件修改添加到暂存区;
2) git commit,提交更改,实际上是把暂存区所有的内容提交到当前分支,而暂存区被清空,如下
所以可以多次添加内容,而提交一次即可;
4、 文件管理
在Git中,必须将文件添加到暂存区,才能commit。在进行文件操作时,常常涉及文件恢复、文件删除等。
4.1、撤销修改
在工作区文件被修改之后,想要撤销修改,恢复到上一个gitadd或gitcommit的版本,可以使用git checkout -- file 命令来丢弃工作区的修改,使文件恢复到最近一次gitadd 或gitcommit时的状态。
若文件被添加到暂存区,此时要想撤销修改,可以使用gitreset HEAD file 可以把暂存区的修改撤销掉,重新放回工作区;之后,再使用gitcheckout – file将工作区的修改也撤销掉,就可以完成文件恢复了。
若文件被提交到了版本库,当本地仓库没有被推送到远程仓库时,可以回退到上一个版本。
4.2、删除文件
一般情况下,我们通常直接通过文件管理器把没用的文件删除了,或通过rm命令将文件删除。这时工作区和版本库就不一致了,可以使用gitstatus查看详情。
可以使用gitrm file 来删除版本库中的文件,并gitcommit。若删除的文件较多,则可以使用gitadd –A(gitadd --all)来删除版本库中的文件;之后再commit、push就可以删除远程仓库的文件(本地仓库和远程仓库是一致的)。
若由于误操作将工作区的文件删除了,版本库中还存在该文件,则可以使用gitcheckout --file来恢复文件。
5、 分支管理
Git保存的不是文件差异或者变化量,而是一系列文件快照。每当提交一次,会保存一个提交对象,该对象包含一个指向文件快照的指针,以及其他相关信息等。
在每次提交时,Git都把它们串成一条时间线,这条时间线就是一个分支。默认情况下,Git只有一条分支,叫做主分支(master分支);而HEAD则指向master,如下
每次提交时,master分支都会向前移动;随着不断的提交,master分支也就越来越长。
那么Git是如何创建一个新的分支呢?答案很简单,就是创建一个新的分支指针(gitbranch dev),并改变HEAD的指向(gitcheckout dev或通过gitcheckout –b dev创建并切换到dev分支),指向新创建的分支,而master指向不变,如下
以后,对工作区的修改和提交就针对HEAD指向的分支。当回到master分支(gitcheckout master),首先会把HEAD指针移回到master分支,并把工作目录切换到master分支指向的文件快照。
假如dev分支上的工作完成了,就可以把dev合并到msater上,此时只需将master指向dev的当前提交(首先切换到master分支,在进行合并,gitcheckout master,gitmerge dev),就完成了合并,如下
当合并完成后,master和dev指向同一个位置,就可以将dev分支删除(gitbranch –d dev,本质就是将dev指针删除而已)。复杂的情况如下所示
这次合并操作,并不是简单的移动指针。由于当前master分支所指向的是C4,C4并不是iss53的分支的直接祖先,Git不得不进行一些额外的处理。就此例而言,Git会用两个分支的末端(C4和C5)以及它们共同的祖先(C2)进行一次简单的三方合并,对合并后的结果重新做一个快照,并自动创建一个指向它的提交对象(C6),如下图所示
合并之后,分支iss53也就没有了,就可以删除iss53分支。
注意:在合并分支时,若遇到冲突,需要手动解决冲突后,再进行合并。
5.1、分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
l master分支应该是非常稳定的,也就是说仅用来发布新版本;
l 平时开发都在dev分支上,也就是说dev分支是不稳定的,到发布新版本时,再把dev分支合并到master上,在master上发布新版本。开发人员都在dev分支上干活,每个人都有自己的分支,时不时往dev分支上合并就可以了,如下所示
5.2、bug分支
在软件开发中,修复bug时往往要新建分支,在新分支上修复bug后,再合并分支。
在修复bug时,若现有的工作并没有完成,可以使用gitstash把当前的工作空间给“存储下来”,以便以后回复工作空间继续工作。当恢复工作空间时,可以使用gitstash list查看已保存的工作空间,使用gitstash pop可以恢复最近保存的工作空间,同时删除stash的保存记录。若有多个保存的空间,可以使用gitstash apply xxxx来恢复某一个工作空间,若要删除保存的记录需要使用gitstash drop来删除。
5.3、feature分支
在任何的项目中都可以使用特性分支来实现单一特性或其他相关的功能,当工作完成后,把它们合并到主干分支,再将其删除。
5.4、推送分支
推送分支就是把分支上的所有本地提交推送到远程仓库。推送时,需要指定推送那个本地分支,以便对应远程仓库的分支,如gitpush origin master,将主分支的提交推送到远程的master分支。
在实际开发中,master、dev分支需要与远程同步,而bug、feature分支可以选择同步,也可以不同步。
5.5、抓取分支
当从远程仓库克隆时,实际上Git自动把本地的master分支与远程分支master对应起来,并且远程仓库的默认名为origin。
当克隆远程仓库后,默认情况下,只能看到master分支。若要在其他分支上开发,如在dev分支,就必须创建远程仓库dev分支对应的本地分支,可以使用gitcheckout –b dev origin/dev(远程分支用(远程仓库名)/(分支名)表示))。
当开发人员在dev分支上提交更新,并推送到远程。如果推送失败,则先使用gitpull进行抓取并在本地合并。若有冲突,在本地解决冲突后,提交,再推送。
5.6、远程分支
远程分支是对远程仓库中分支的索引,它们是一些无法移动的本地分支,只有在git进行网络交互时才会更新。
远程分支采用(远程仓库名)/(分支名)来表示。当我们从远程仓库克隆时,git会自动将远程仓库命名为origin,并下载其中所有的数据,建立一个指向master分支的指针,在本地命名为origin/master,但却无法在本地更改数据。接着,git建立一个本地的master分支,master和origin/master指向相同的位置,但可以在master分支上工作,如下
如果你在本地master分支上做了些改动,与此同时,其他人向远程仓库推送了更新,那么本地的master分支和远程仓库上的master分支都不断向前推进,而本地的origin/master只要不和远程仓库通信,指针仍保持原位不会移动。
可以运行gitfetch origin来同步远程服务器上的数据到本地,并更新本地的数据库,然后把origin/master的指针移动到它最新的位置上,如下
当我们推送了一个分支iss53到远程仓库,虽然有一个本地的iss53分支,但指向服务器上最新更新的是origin/iss53分支。
需要注意的是:当fetch操作同步好远程的分支之后,仍无法在本地编辑远程仓库中的其他分支,如dev。换句话说,在本地中并不会有一个新的dev分支,有的只是无法移动的origin/dev分支。
如果要有一份自己的dev来开发,则可以在远程仓库的基础上分化出来一个新的分支dev,如下
git checkout –b dev origin/dev
这会切换到新建的dev本地分支,其内容同远程分支origin/dev一致,这样就可以在dev本地分支继续开发。
5.7、跟踪远程分支
从远程分支中checkout出来的分支本地分支,称为跟踪远程分支。跟踪远程分支是一种和远程分支有直接联系的本地分支。在跟踪分支中输入gitpush,Git会自动推断应该向那个服务器的哪个分支推送数据;相反,gitpull会获取对应远程分支上的所有远程索引,并把它们的数据合并到本地分支中。
在克隆仓库时,git通常会创建一个名为master的分支来跟踪origin/master分支,这也就是gitpush和gitpull一开始就能正常工作的原因。
5.8、删除远程分支
如果不再需要某个远程分支,可以使用如下命令来删除远程分支:
git push [远程名]:[分支名]
如git push origin:xxxx
6、 标签管理
发布一个新版本时,常常先在版本库中打一个标签,这样就唯一确定了打标签时的版本。标签本质上是一个指向是版本库快照的指针,但是该指针不能够移动过;所以创建和删除标签都将很快完成。
6.1、创建标签
创建标签前,首先要切换到打标签的分支上,然后使用gittag tagname来创建一个新的标签,可以使用gittag来查看所有的标签,而使用gitshow tagname查看某一个具体的标签信息。
若针对某次提交打标签,首先需要知道提交的Id,之后使用gittag tagname commit_id 就可以对某次提交打标签。
6.2、删除标签
git tag –d tagname可以删除本地标签。若标签推送到远程(git push origintagname或gitpush origin –tags推送全部为推送的标签),则先从本地删除,再从远程删除,如下
git tag –d v0.9
git push origin :refs/tags/v0.9
7、 .gitignore
如果工作区内的某些文件不想被提交,可以在.gitignore文件中设置过滤规则,git将自动过滤符合过滤规则的文件或文件夹。
在.gitignore文件中,#表示注释,其他的则表示过滤规则,如下
* | 代表任意数目的字符 |
? | 代表任意的一个字符 |
[abc] | 代表a,b,c中的任意一个字符 |
[^abc] | 非abc中任意一个字符 |
{ab,bb} | ab,bb任意一类型即可 |
{!ab} | 非ab类型 |
*.a | 忽略所有.a结尾的文件 |
/TODO | 仅仅忽略根目录下的TODO文件夹,不包括子目录中的TODO |
build/ | 忽略build目录下的所有文件 |
doc/*.txt | 只忽略doc目下的txt文件,不包括doc子目录下的txt文件 |
Eclipse中Java项目的.gitignore文件如下:
# Mobile Toolsfor Java (J2ME)
.mtj.tmp/
# Package Files
*.jar
*.war
*.ear
#eclipse
.classpath
.project
.settings/
.metadata/
RemoteSystemsTempFiles/
#maven
target/
#mac
.DS_Store
# virtual machinecrash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
8、 Git常用命令
2.1、 本地仓库
l 新建仓库: git init将在当前目录下建立一个空的仓库;
l 添加文件:git add xxx,其中.代表当前目录下所有文件
l 提交:git commit –m “description”
l 查看状态:git status
l 查看具体修改信息:git diff xxx
l 查看历史记录:git log
3.1、 远程仓库
l 检出仓库:$ git clone git://github.com/jquery/jquery.git
l 查看远程仓库:$ git remote -v
l 添加远程仓库:$ git remote add [name] [url]
l 删除远程仓库:$ git remote rm [name]
l 修改远程仓库:$ git remote set-url --push [name] [newUrl]
l 拉取远程仓库:$ git pull [remoteName] [localBranchName]
l 推送远程仓库:$ git push [remoteName] [localBranchName]
如果想把本地的某个分支test提交到远程仓库,并作为远程仓库的master分支,或者作为另外一个名叫test的分支,如下:
$git push origin test:master // 提交本地test分支作为远程的master分支$git push origintest:test // 提交本地test分支作为远程的test分支
3.2、分支
l 查看本地分支
$ git branch
l 查看远程分支
$ git branch -r
l 创建本地分支
$ git branch [name] ----注意新分支创建后不会自动切换为当前分支
l 切换分支
$ git checkout [name]
l 创建新分支并立即切换到新分支
$ git checkout -b [name]
l 删除分支
$ git branch -d [name] , -d选项只能删除已经参与了合并的分支,对于未有合并的分支是无法删除的。如果想强制删除一个分支,可以使用-D选项
l 合并分支
$ git merge[name] ,将名称为[name]的分支与当前分支合并
l 创建远程分支(本地分支push到远程)
$ git push origin [name]
l 删除远程分支
$ git push origin :heads/[name] 或 $ gitpush origin:[name]
l 创建空的分支
(执行命令之前先提交当前修改的分支,否则会被强制删干净没得后悔)
$gitsymbolic-ref HEAD refs/heads/[name]
$rm.git/index
$gitclean -fdx
3.3、版本
l 查看版本:$ git tag
l 创建版本:$ git tag [name]
l 删除版本:$ git tag -d [name]
l 查看远程版本:$ git tag -r
l 创建远程版本(本地版本push到远程):$ git push origin [name]
l 删除远程版本:$ git push origin:refs/tags/[name]
l 合并远程仓库的tag到本地:$ git pull origin --tags
l 上传本地tag到远程仓库:$ git push origin --tags
l 创建带注释的tag:$ git tag -a [name] -m'yourMessage'
3.4、子模块
l 添加子模块
$ git submodule add [url] [path]
$git submodule add git://github.com/soberh/ui-libs.gitsrc/main/webapp/ui-libs
l 初始化子模块
$ git submodule init ----只在首次检出仓库时运行一次就行
l 更新子模块
$ git submodule update ----每次更新或切换分支后都需要运行一下
l 删除子模块
1) $ gitrm --cached [path]
2) 编辑“.gitmodules”文件,将子模块的相关配置节点删除掉
3) 编辑“ .git/config”文件,将子模块的相关配置节点删除掉
4) 手动删除子模块残留的目录
参考资料:
2、http://www.open-open.com/lib/view/open1328069889514.html