相信你们对远程版本库都有所了解,而且也都有在使用相似github,gitlab或者bitbucket之类的服务,那咱们这里就主要来讲一下本地版本库与远程版本库交互时的一些注意点,一般在咱们建立好了远程仓库而且打算将本地同步上去时会先执行git remote add origin https://github.com/test/test.git
,这里其实就能够当作咱们给远程版本库取了一个别名origin,其实origin这个名字是能够随便定的,固然服从规范的话咱们一般不会去用其余的名字。接着咱们就会执行git push -u origin master
来将咱们本地版本库推送到远程,这里注意咱们加-u实际上是创建了一个origin和master之间的关联,以后咱们就只要简单执行git push
,git就会自动去选择将master分支推送给远程,一样git pull也是。这时候咱们能够经过执行git remote show
来查看现有的远程地址,git
因此其实本地的一个版本库是能够同时有多个远程地址的,例如origin2,origin3。若是想要只查看某个远程地址的信息和通本地的关联可使用git remote show origin
github
这里还有一点要注意的是,有时候在提交时会出现这样一段警告服务器
这里就涉及了git push的一个默认行为模式,在git 2.0以前默认是使用matching的,也就是它会将如今本地关联的这条分支推送到远程存在而且同名的分支,例如如今咱们将master与origin关联,那么当咱们使用matching模式去提交时,就会去找远程是否有一个也叫作master的分支,若是有而后就将咱们本地分支推送到远程这个master分支,而在git 2.0以后就采起了一个相对保守的策略,也就是simple模式,它在push时用的远程分支会是咱们执行git pull时的那个远程分支,例如说我git pull是默认是一个叫作dev的分支,那么git push时就会推送去这个dev分支,为何说simple是更保守呢,就是由于matching实际上是一种git大胆的猜想咱们默认推送和拉取用的是同名的远程分支和本地分支。咱们能够像图中所说使用git config去设置或改变默认行为模式。app
在咱们将本地版本库与远程版本库作了关联以后,每次咱们调用git status
就会看到这样一条信息gitlab
这里背后其实在本地咱们会多了一个叫作origin/master的分支,它被用来追踪远程的master分支,每次当咱们执行git pull时,其实背后是跑了两条指令git fetch与git merge,git fetch会先同步远程的master分支与本地的origin/master分支,而后再将origin/master分支merge到本地的master分支,而在咱们执行git push时,也是先将本地分支与origin/master分支同步,再将本地master分支推送到远程分支测试
要查看远程分支时执行git branch -a
便可fetch
分支的实践我的以为没有所谓的最佳,其实都是要根据不一样项目的状况来决定,这里就介绍一个本身日常比较多使用的方式,分为3个主要分支加一种类型的分支:版本控制
git裸库实际上就是一个没有工做区的git仓库,那它的做用是什么呢,其实就是用来作一个存放于中转的仓库,一般咱们会把它放到本身的服务器上,由于咱们不须要在服务器上去作文件的操做,因此其实咱们只须要有.git那个文件夹的内容便可,根据前面几篇的内容咱们知道其实全部版本信息都在这个.git文件夹下。建立裸库的话只要咱们执行git init --bare
便可code
在开发中,有时候咱们可能有多个项目会依赖于一个库,那若是咱们把项目放在不一样的版本控制系统,那么每当咱们的库有更新时咱们就须要从新把最新的库拷贝过来从新部署升级到各个项目中,试想若是这个库一天有屡次的改动,那项目也要跟着去修改屡次就显得很没效率,因而就有了git的submodule来专门解决这种问题,git submodule可让咱们在项目中引用另一个版本库的项目,使它在咱们当前的项目中可见,而且只要另外那个版本库一旦有更新,咱们要作的只是使用一条git指令而后就会自动更新最新的代码了。咱们经过在项目中执行git submodule add git@github.com:desmond/git_child.git mymodule
就能够将git_child这个项目以submodule的形式加入到咱们当前项目的mymodule文件夹下,注意的是这个目录事先不能够存在,若是已存在的话git会报错orm
当child发生了更新时,咱们只要在mysubmodule中执行git pull就能够获取到最新的提交信息了,那假设咱们项目中有多个submodule的话那更新不就很麻烦了吗,git其实也提供了一个git submodule foreach git pull
,当咱们在项目根目录下执行后,git就会循环的给每一个submodule都执行一次pull操做
固然在pull完以后,咱们还要记得对submodule执行一次git push才会真正把本地项目中submodule的改动推送到远程版本库中
另外要注意的是,若是咱们是第一次从远程版本库clone下来时,git是不会帮咱们把submodule中的内容也一块儿获取的,这时候咱们就要先执行git submodule init
,再执行git submodule update --recursive
,此时本地就会获取到submodule中的内容。不过git clone也提供了一个选项帮咱们简化上面的操做,咱们只需执行git clone git地址 --recursive
就能作到上面的效果了
若是咱们clone完以后进入submodule的文件夹,就会看到如今的分支会变成一个游离的状态指向submodule的最新commit,不过本质上它就是咱们submodule上master的最新commit,因此咱们能够直接执行git checkout master
切换回去
subtree和submodule其实解决的都是同一类问题,但当咱们在使用submodule时一般是经过更新被依赖的module而后再在使用这个module的项目中去更新,可是若是反过来想作到修改项目中的module也能够更新module自己的话submodule就会出现些bug,因此咱们就引入了subtree。也就是说subtree能够用来完成双向修改。因此在咱们掌握了subtree以后,其实就彻底能够代替本来的submodule了,git官方也是这么推荐的。
首先咱们执行git remote add subtree-origin git地址
在本地项目中添加module的远程地址,接着执行git subtree add --predix=subtree subtree-origin master --squash
就能够在本支的subtree文件夹下加入module的master分支内容,这里squash的意思是在加入module内容时,会将module的全部commit合并成一个commit合并进本地的内容,这样在本地的提交记录中咱们就只会看到一条关于module库的commit。而若是不加squash的话,就能够看到module的每一条commit都会被合并到本地
下图能够看到若是使用squash的状况下产生的commit,其实就两条,一条就是把全部的commit合并成一个,另外一条就是将这条commit合并成一个subtree
当咱们建立submodule时,其实那个文件夹表明的是指向module地址的一个引用,而subtree是真正的在项目中放入了module的内容,这是它与submodule不一样的地方
那么此时若是module自己更新了,咱们能够在项目中执行git subtree pull --prefix=subtree subtree-origin master --squash
来更新项目
那若是咱们要经过更新项目自身来后将修改apply到module时要怎么作呢,咱们只须要执行git subtree push --prefix=subtree subtree-origin master
便可,可是这里若是咱们使用squash选项的话实际上是会失败的,这里就牵扯到了squash这个选项的问题,当咱们使用squash时其实咱们等因而产生了一个新的commit,即使咱们知道这个commit其实就是把三个commit合并在一块儿,可是在commit对象链中经过该commit是没法与module里面的commit联结上的,这就会致使咱们在git pull和git push时因为没有共同的父commit对象而出现conflict的状况,须要咱们手动去处理,那若是不加squash呢,那这个问题就能够解决了,由于两边其实都是拥有同样的commit对象链,可是不用squash的话,咱们就会在项目自己中看到module的commit,这部分commit其实咱们大多数时候是不关心的,同时若是项目中有除了subtree修改提交外仍是其余文件的修改,那当咱们同步推送回module自己时,也会把这些不属于module的commit给同步过去,这样就会形成两边都被污染。因此你们能够根据实际状况来选择使用哪种,可是一个宗旨就是若是一开始就使用squash选项,那么务必要确保以后全部subtree操做都要使用squash,反之亦然。
git branch -a
,若是想显示每条分支的最后一条commit,能够执行git branch -av
git clone 远程地址 文件夹名
git push --set-upstream origin 分支名
,这时候关联就会创建而且在本地多了一个'origin/分支名'的branch,其实这条命令与git push -u origin master
是达到同样的效果,不过git新版本推荐是使用--set-upstream
的作法
一般在咱们拉取了远程仓库以后会在本地建立一个关联分支
git checkout -b test origin/test
来建立,这样就会多出一个本地的test分支而且commit对象链与origin/test同样,另外,咱们也可使用git checkout --track origin/test
来建立一个本地分支,它与前者的区别就在于它会自动用origin/后面的这个远程名字来做为本地建立的分支名 第一种方法咱们能够经过执行git push origin :test
,这里其实表明的意思是咱们将一个空分支推送到远程的test分支,就变相作了一个删除的动做,第二种方法比较直接,就是执行git push origin --delete test
git push --set-upstream origin develop:develop2
这条指令就表明在远程建立一个develop2分支而且与本地develop分支关联,可是若是这个时候咱们在本地develop分支调用git push会受到这样一个错误警告
git push origin HEAD:develop2
或者git push origin develop:develop2
的方式来推送,固然其实这两条命令背后是同样的,由于当前HEAD指向的就是develop分支 git remote rename origin origin2
,这样就把远程仓库名从origin重命名为origin2了
git remote rm origin
git并无提供直接的指令帮助咱们删除submodule,因此咱们就须要先执行git rm --cached mymodule
将submodule从暂存区中移除,再之心rm -rf mymodule
将文件夹完全删除,接着提交咱们的修改,同时.gitmodule这个文件也没有用了,因此也能够经过上述方法将其删除,这样咱们就成功的删除了submodule
咱们能够经过在本地fork的版本库中加入一个原做的远程分支,而后执行git fetch将原做的最新代码拉取下来,一般咱们会把这个分支名叫作upstream,接着咱们能够执行merge操做,将原做的upstream分支merge到咱们当前分支,而后提交推送,就跟上最新记录了。
这里要先说的一个概念是squash其实并非subtree这个指令专有的,git merge一样也能够指定这个选项而且做用是相同的
咱们能够经过另外创建一个相似叫no-squash的分支而后不加squash选项更新subtree,这样就保留了module的历史记录,没有烦人的反复冲突问题,而后再将no-squash分支合并到主分支,可是这里合并时用的是squash的方式git merge --squash
, 这样项目的主分支上只会体现一个commit,比直接git subtree add/pull --squash
还要简洁(本来是两个commit)。
固然这种作法也有缺点,首先新开的分支历史记录就会稍显混乱,另外就是每次新分支作subtree的操做就要记得merge一次回master,但我本身以为仍是在可以接受的范围内得。
其实常常会出现别人推送到远程后咱们又作了一些修改,这时候当咱们执行git pull时,会先执行git fetch,接着执行git merge,这时候其实不少时候会由于合并产生额外的commit,这时候咱们就能够经过执行git pull --rebase
来解决这个问题
这个算是比较冷门一点的问题,不过git也是有方法帮咱们来作的,咱们能够经过执行git format-patch -n -o path
这里n只的是最近的n条commit,而后咱们使用-o后面接一个路径,接着git就会在指定路径中建立每一个commit对应的一个patch文件,接着咱们能够把这些patch文件经过email等方式传给另一个host,而后在那个host的项目中执行git am path
,这里的path就是放传过来的这些文件,这样git就会帮咱们更新了