浅析git
浅析git
笔者在此整理了常见的git命令,git的重要性无需多言,与其再百度海中搜索git命令,不妨尝试收藏笔者的此篇作品。希望对你的学习有所帮助。
- 版本控制系统之git
Git:
(一)简介:
Git 是一款免费的、开源的、分布式的版本控制系统。旨在快速高效地处理无论规模大小的任何软件工程。
每一个 Git克隆 都是一个完整的文件库,含有全部历史记录和修订追踪能力,不依赖于网络连接或中心服务器。其最大特色就是“分支”及“合并”操作非常快速、简便。
(二)Git与svn的主要区别:
Git是分布式SCM,而SVN是基于服务器的,也就是说每个开发者本地都有一套git库,每个人维护自己的版本(或者合并其他人的版本),而SVN是每个人写完代码后都及时的checkin到服务器上,进行合并。
- Git的优势:
说到优势,那么自然是相对与SVN而言的
1.版本库本地化,支持离线提交,相对独立不影响协同开发。每个开发者都拥有自己的版本控制库,在自己的版本库上可以任意的执行提交代码、创建分支等行为。例如,开发者认为自己提交的代码有问题?没关系,因为版本库是自己的,回滚历史、反复提交、归并分支并不会影响到其他开发者。
2.更少的“仓库污染”。git对于每个工程只会产生一个.git目录,这个工程所有的版本控制信息都在这个目录中,不会像SVN那样在每个目录下都产生.svn目录。
3.把内容按元数据方式存储,完整克隆版本库。所有版本信息位于.git目录中,它是处于你的机器上的一个克隆版的版本库,它拥有中心版本库上所有的东西,例如标签、分支、版本记录等。
4.支持快速切换分支方便合并,比较合并性能好。在同一目录下即可切换不同的分支,方便合并,且合并文件速度比SVN快。
5.分布式版本库,无单点故障,内容完整性好。内容存储使用的是SHA-1哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。
- 本地创建git远程仓库:
我这里使用的是:centos7.2、并且在服务器中已经创建git账户;如果没有创建git账户的朋友,需要先创建git,并且在服务器端安装git
1.在本地建立一个空白的git仓库:
Git –bare init
注:--bare参数相当与只创建一个空白的仓库,只包含记录版本库历史记录的.git目录下面的文件,不会包含实际项目源文件的拷贝;
2.将本地创建的仓库添加到远程服务器,使用的linux上传命令;
Scp –r 文件夹 linux用户名@ip:/文件目录
如:scp –r gittest.git [email protected]:/data./git
(这里是将本地的gittest.git文件夹拷贝到服务器中 /data/git文件夹下)
3.此时可以直接将本地的gittest文件夹关联远程仓库,也可以在本地再次克隆git仓库;使用命令如下:
Git clone [email protected]:/data/git/gittest.git
4.可以直接创建一个文件,进行上传测试;如:
cd gittest 进入文件夹
vi app.js 然后随便输入字符,esc+wq!进行保存退出;
然后将操作添加至暂存区:
Git add .
创建本地仓库的版本:
Git commit –m “haha”
提交至远程服务器仓库:
Git push origin master;
注:如果此时出现此种错误:
remote: error: insufficient permission for adding an object to repository database ./objects
这时注意自己的服务器git账户是否有写的权限;一般是因为git账户对gittest.git文件夹权限不够;此时登陆自己的服务器,给git账户进行权限的赋予;命令为:
Chown –R git:git /data/git/gittest.git
权限操作完毕后,再次进行本地仓库与远程仓库的同步,一般此时不再会进行报错;
注:服务器git仓库的创建也可以直接在linux操作系统中进行;
- 本地git创建仓库与github的远程仓库相关联:
1.首先登陆github官网;进行注册、登录
2.创建新的仓库
3.在本地创建git仓库
mkdir githubtest
git init
vi app.js
注:https协议和ssh协议的区别就是每次远程操作都需要输入github的用户名和密码;
4.https协议:
Git remote add origin https://github.com/misterguan...
Git add .
Git commit –m “haha”
Git push –u origin master
此时需要输入github的用户名和密码
此时会报错: error: failed to push some refs to
(此种原因一般为在github的远程仓库有文件,在本地仓库没有,所以此时应该先将远程仓库合并到本地仓库,再进行提交)
git pull --rebase origin master
注意:这里的rebase和merge的区别,简单理解,rebase在log中无分叉,而merge有
再次提交,将会成功;
- ssh协议:
首先需要创建密钥
ssh-keygen –t rsa –C [email protected]
可以一路回车;
(Enter file in which to save the key (/c/Users/dream/.ssh/id_rsa):
这里默认就可以,这是存放ssh密钥的路径)
(Enter passphrase (empty for no passphrase):这里为ssh的密码,可以为空)
然后到C:Users用户 .ssh中找到id_rsa.pub,然后复制里面的密钥到github
例:C:Usersdream.ssh
在github的ssh and gpg keys中new ssh key
Title为此ssh的标记
Key为你本地的密钥
测试下:
ssh [email protected]
如果输出You've successfully authenticated;说明链接成功;
下面的操作跟https的一样:
- Git的分支管理:
查看本地分支:$ git branch
查看远程分支:$ git branch -r
创建本地分支:$ git branch [name] ----注意新分支创建后不会自动切换为当前分支
切换分支:$ git checkout [name]
创建新分支并立即切换到新分支:$ git checkout -b [name]
删除分支:$ git branch -d [name] ---- -d选项只能删除已经参与了合并的分支,对于未有合并的分支是无法删除的。如果想强制删除一个分支,可以使用-D选项
合并分支:$ git merge [name] ----将名称为[name]的分支与当前分支合并
创建远程分支(本地分支push到远程):$ git push origin [name]
删除远程分支:$ git push origin :heads/[name] 或 $ gitpush origin :[name]
项目开发的分支:
Master:用于发布版本的分支;(用于大版本号更新时使用)
Dev:开发时的主分支;
Feature:功能分支;(开发某个局部分支,从dev分支上分出来的)
Release:创建一个预发布分支(从dev分支上分出来,合并到master分支上,进行tag标注)
Bug:bug分支(从master分支上分出来,修改完毕后合并到master和dev分支)
分支合并
在git进行分支的合并时
如果使用默认的fast-farward merge
直接修改当前HEAD指针的指向然后再修改当前HEAD指针,说白了就是修改两个指针的指向,而并没有生成新的commit对象。
如果使用—no-ff 进行合并
这样会在master分支上创建一个版本;
- git如何解决代码冲突:
1.逻辑冲突
git自动处理(合并/应用补丁)成功,但是逻辑上是有问题的。
比如另外一个人修改了文件名,但我还使用老的文件名,这种情况下自动处理是能成功的,但实际上是有问题的。
又比如,函数返回值含义变化,但我还使用老的含义,这种情况自动处理成功,但可能隐藏着重大BUG。这种问题,主要通过自动化测试来保障。所以最好是能够写出比较完备的自动化测试用例。
这种冲突的解决,就是做一次BUG修正。不是真正解决git报告的冲突。
2.内容冲突
两个用户修改了同一个文件的同一块区域,git会报告内容冲突。我们常见的都是这种,后面的解决办法也主要针对这种冲突。如图中是:app.js冲突:
然后手动修改冲突的文件:
再次进行文件的提交,版本的创建;
3.树冲突
文件名修改造成的冲突,称为树冲突。
比如,a用户把app.js改名为master.js,b用户把app.js文件改名为test.js,那么b将这两个commit合并时,会产生冲突。
此时打开文件,把需要的删除的文件删除掉即可,如把master.js删除掉;
然后,再次重新创建版本即可:
- git常用操作命令:
1.查看、添加、提交、删除、找回,重置修改文件
git help <command> # 显示command的help
git show # 显示某次提交的内容 git show $id
git co -- <file> # 抛弃工作区修改
git co . # 抛弃工作区修改
git add <file> # 将工作文件修改提交到本地暂存区
git add . # 将所有修改过的工作文件提交暂存区
git rm <file> # 从版本库中删除文件
git rm <file> --cached # 从版本库中删除文件,但不删除文件
git reset <file> # 从暂存区恢复到工作文件
git reset -- . # 从暂存区恢复到工作文件
git reset --hard # 恢复最近一次提交过的状态,即放弃上次提交后的所有本次修改
git ci <file> git ci . git ci -a # 将git add, git rm和git ci等操作都合并在一起做 git ci -am "some comments"
git ci --amend # 修改最后一次提交记录
git revert <$id> # 恢复某次提交的状态,恢复动作本身也创建次提交对象
git revert HEAD # 恢复最后一次提交的状态
2.查看文件diff
git diff <file> # 比较当前文件和暂存区文件差异 git diff
git diff <id1><id1><id2> # 比较两次提交之间的差异
git diff <branch1>..<branch2> # 在两个分支之间比较
git diff --staged # 比较暂存区和版本库差异
git diff --cached # 比较暂存区和版本库差异
git diff --stat # 仅仅比较统计信息
3.查看提交记录
git log git log <file> # 查看该文件每次提交记录
git log -p <file> # 查看每次详细修改内容的diff
git log -p -2 # 查看最近两次详细修改内容的diff
git log --stat #查看提交统计信息
tig
Mac上可以使用tig代替diff和log,brew install tig
4.查看、切换、创建和删除分支
git br -r # 查看远程分支
git br <new_branch> # 创建新的分支
git br -v # 查看各个分支最后提交信息
git br --merged # 查看已经被合并到当前分支的分支
git br --no-merged # 查看尚未被合并到当前分支的分支
git co <branch> # 切换到某个分支
git co -b <new_branch> # 创建新的分支,并且切换过去
git co -b <new_branch> <branch> # 基于branch创建新的new_branch
git co $id # 把某次历史提交记录checkout出来,但无分支信息,切换到其他分支会自动删除
git co $id -b <new_branch> # 把某次历史提交记录checkout出来,创建成一个分支
git br -d <branch> # 删除某个分支
git br -D <branch> # 强制删除某个分支 (未被合并的分支被删除的时候需要强制)
5. 分支合并和rebase
git merge <branch> # 将branch分支合并到当前分支
git merge origin/master --no-ff # 不要Fast-Foward合并,这样可以生成merge提交
git rebase master <branch> # 将master rebase到branch,相当于: git co <branch> && git rebase master && git co master && git merge <branch>
6. Git补丁管理(方便在多台机器上开发同步时用)
git diff > ../sync.patch # 生成补丁
git apply ../sync.patch # 打补丁
git apply --check ../sync.patch #测试补丁能否成功
7. Git暂存管理
git stash # 暂存
git stash list # 列所有stash
git stash apply # 恢复暂存的内容
git stash drop # 删除暂存区
8.Git远程分支管理
git pull # 抓取远程仓库所有分支更新并合并到本地
git pull --no-ff # 抓取远程仓库所有分支更新并合并到本地,不要快进合并
git fetch origin # 抓取远程仓库更新
git merge origin/master # 将远程主分支合并到本地当前分支
git co --track origin/branch # 跟踪某个远程分支创建相应的本地分支
git co -b <local_branch> origin/<remote_branch> # 基于远程分支创建本地分支,功能同上
git push # push所有分支
git push origin master # 将本地主分支推到远程主分支
git push -u origin master # 将本地主分支推到远程(如无远程主分支则创建,用于初始化远程仓库)
git push origin <local_branch> # 创建远程分支, origin是远程仓库名
git push origin <local_branch>:<remote_branch> # 创建远程分支
git push origin :<remote_branch> #先删除本地分支(git br -d <branch>),然后再push删除远程分支
9.Git远程仓库管理
GitHub
git remote -v # 查看远程服务器地址和仓库名称
git remote show origin # 查看远程服务器仓库状态
git remote add origin git@ github:robbin/robbin_site.git # 添加远程仓库地址
git remote set-url origin git@ github.com:robbin/robbin_site.git # 设置远程仓库地址(用于修改远程仓库地址) git remote rm <repository> # 删除远程仓库
10.创建远程仓库
git clone --bare robbin_site robbin_site.git # 用带版本的项目创建纯版本仓库
scp -r my_project.git git@ git.csdn.net:~ # 将纯仓库上传到服务器上
mkdir robbin_site.git && cd robbin_site.git && git --bare init # 在服务器创建纯仓库
git remote add origin git@ github.com:robbin/robbin_site.git # 设置远程仓库地址
git push -u origin master # 客户端首次提交
git push -u origin develop # 首次将本地develop分支提交到远程develop分支,并且track
git remote set-head origin master # 设置远程仓库的HEAD指向master分支
也可以命令设置跟踪远程库和本地库
git branch --set-upstream master origin/master
git branch --set-upstream develop origin/develop
- 项目自动化:
现在的前端开发已经不再仅仅只是静态网页的开发了,日新月异的前端技术已经让前端代码的逻辑和交互效果越来越复杂,更加的不易于管理,模块化开发和预处理框架把项目分成若干个小模块,增加了最后发布的困难,没有一个统一的标准,让前端的项目结构千奇百怪。前端自动化构建在整个项目开发中越来越重要。
(一)工具化
在前端的技术栈发展过程中,出现了很多的工具,形成了工具化,能够用工具完成的绝不要手工完成,来帮助开发者提升效率。
- 前端工作流工具: Gulp,Grunt
- 前端js模块编译工具:Babel,Browserify,Webpack
- 包管理器: npm,bower
- 前端开发系列工具: livereload,数据mock,代码监控,代码检查。
(二)工程化
工程化是一个发展趋势,以工具化为基础。
工程的核心是流程自动化,又称为构建,这些包括了:代码质量检测,代码压缩,代码合并,代码优化,代码编译,单元测试等等部分。构建就是把这些以工作流程的方式组合起来,然后用一个命令行运行这整个流程。它有点像批处理,但是是程序开发中使用的特殊批处理。
(三)自动化
自动化是以工程化为基础,是在流程自动化上更进一步的自动化。
持续集成就是全自动化的一个终极体现。他的主要流程是:版本控制库 ->构建 ->测试 ->报告.
(四)代码规范
代码规范可以提高代码的可阅读性和避免一些低级错误。为了将代码规范的检查放到前端开发工程中,各种前端语言都有对应的hint或者lint工具。
(五)预处理
SASS
甚至为了避免这一点,引入了各种预编译语言,css的预编译less,现在流行的是sass,功能也更加强大,语法错误无法通过编译,来弥补css这种缺陷。
(六)ES6
js的预处理语言也有很多,只是为了让有其他语言经验的开发者更容易的上手js的编码。
因为浏览器的实现大多还是 ES5 的标准,为了使用最新的 ES6 语法,通常的做法是采用 Babel 将 ES6 编译为 ES5。
(七)js模块化
amd,cmd,common,es6
(八)文件处理
通常一个前端项目会分有一个 src 目录和 dist 目录, src 放置源码,dist 放置编译后的代码。所以在前端工程的流程中会涉及到文件的拷贝,删除,移动等流程。
(九)开发效率
通常的前端开发过程是,修改前端代码,调用命令编译代码,然后浏览器 F5 刷新。这个过程可以做到自动化,通过代码监控工具,指定要监控的目录和文件,如果对应文件有改变,调用编译工具编译源码,然后通过 livereload 自动刷新浏览器。 gulp-browserify也可以实现同样的功能。
(十)数据的mock
现代化前端项目开发大多是前后端分离的方式,也就是后端基本是提供 API 服务,在真实开发环境中,通常的问题是,后端 API 极其不稳定或者没开发,为了不阻碍前端的开发,通常的做法是,前后端先约定 API 接口定义,然后前端根据定义 mock 接口。
(十一)前端工作流
为了解决前端工程中复杂的流程,出现了很多开源前端流程处理工具。这些工作流工具不仅仅是其本身,都是一个流程生态体系,每个工具都涉及到对应的插件库,几乎我们能想到的前端工程问题都有对用的插件能够解决。
- Gulp:
Gulp 是基于node.js的一个前端自动化构建工具,开发这可以使用它构建自动化工作流程(前端集成开发环境)。
使用gulp你可以简化工作量,让你把重点放在功能的开发上,从而提高你的开发效率和工作质量。 gulpjs是一个前端构建工具,与gruntjs相比,gulpjs无需写一大堆繁杂的配置参数,API也非常简单,学习起来很容易,而且gulpjs使用的是nodejs中stream来读取和操作数据,其速度更快
(一)安装:
需要在全局或者项目中同时安装;
Cnpm install -g gulp
Cnpm install –save-dev gulp
(二)配置文件
使用gulpfile.js进行配置,基于gulp工作流的项目结构如下:
(三)Api:
1.gulp.src(globs[, options]) 输出(Emits)符合所提供的匹配模式(glob)或者匹配模式的数组(array of globs)的文件。 将返回一个 Vinyl files 的 stream 它可以被 piped 到别的插件中。文件的入口
A.路径匹配:
1)* :能匹配 a.js,x.y,abc,abc/,但不能匹配a/b.js
2). :能匹配 a.js,style.css,a.b,x.y
3)//*.js :能匹配 a/b/c.js,x/y/z.js,不能匹配a/b.js,a/b/c/d.js
4)** 能匹配 : abc,a/b.js,a/b/c.js,x/y/z,x/y/z/a.b,能用来匹配所有的目录和文件
5)*/.js :能匹配 foo.js,a/foo.js,a/b/foo.js,a/b/c/foo.js
6)a/**/z :能匹配 a/z,a/b/z,a/b/c/z,a/d/g/h/j/k/z
7)a/b/z :能匹配 a/b/z,a/sb/z,但不能匹配a/x/sb/z,因为只有单单独出现才能匹配多级目录
8)?.js :能匹配 a.js,b.js,c.js
9)a?? :能匹配 a.b,abc,但不能匹配ab/,因为它不会匹配路径分隔符
10)[xyz].js :只能匹配 x.js,y.js,z.js,不会匹配xy.js,xyz.js等,整个中括号只代表一个字符
11)1.js :能匹配 a.js,b.js,c.js等,不能匹配x.js,y.js,z.js
B.匹配多种文件
//使用数组的方式来匹配多种文件
gulp.src(['js/.js','css/.css','*.html'])
2.gulp.dest(path[, options]) 能被 pipe 进来,并且将会写文件。并且重新输出(emits)所有数据,因此你可以将它 pipe 到多个文件夹。如果某文件夹不存在,将会自动创建它。输出文件
根路径:如果在src中不设置base时,所有的默认根路径,都是从出现匹配符的地方开始截取:
如:
gulp.src('app/src/*/.css') //此时base的值为app/src,也就是说它的base路径为app/src
//设该模式匹配到了文件 app/src/css/normal.css .pipe(gulp.dest('dist')) //用dist替换掉base路径,最终得到 dist/css/normal.css
如果增加base基路径:
gulp.src(script/lib/*.js, {base:'script'}) //配置了base参数,此时base路径为script //假设匹配到的文件为script/lib/jquery.js .pipe(gulp.dest('build')) //此时生成的文件路径为 build/lib/jquery.js
3.gulp.task(name[, deps], fn) 定义一个使用 Orchestrator 实现的任务(task)。
1)参数:
Name:任务的名字,如果你需要在命令行中运行你的某些任务,那么,请不要在名字中使用空格。
Deps:类型: Array,一个包含任务列表的数组,这些任务会在你当前任务运行之前完成。
Fn:该函数定义任务所要执行的一些操作。
2)异步任务
A.使用callback:
var gulp = require('gulp');
// 返回一个 callback,因此系统可以知道它什么时候完成
gulp.task('one', function(cb) {
// 做一些事 -- 异步的或者其他的 cb(err); // 如果 err 不是 null 或 undefined,则会停止执行,且注意,这样代表执行失败了
});
// 定义一个所依赖的 task 必须在这个 task 执行之前完成
gulp.task('two', ['one'], function() {
// 'one' 完成后
});
gulp.task('default', ['two']);
B.使用promise
var gulp = require('gulp');
// 返回一个 callback,因此系统可以知道它什么时候完成
gulp.task('one', function(cb) {
// 做一些事 -- 异步的或者其他的 return new Promise((res, rej) => { setTimeout(() => { res(1344) }, 5000) })
});
// 定义一个所依赖的 task 必须在这个 task 执行之前完成
gulp.task('two', ['one'], function() {
// 'one' 完成后 console.log("two开始")
});
gulp.task('default', ['two']);
注意:5s后才开始执行的two任务
3)对于同步任务:
gulp.task('one',function(cb){ var stream = gulp.src('client/**/*.js') .pipe(dosomething()) //dosomething()中有某些异步操作 .pipe(gulp.dest('build')); return stream; }); gulp.task('two',['one'],function(){ console.log('two is done'); });
4.gulp.watch(glob[, opts], tasks)监视文件,并且可以在文件发生改动时做一些事情;
1)监听文件改变,并执行相应的task任务
var watcher = gulp.watch('js/*/.js', ['uglify', 'reload']);
watcher.on('change', function(event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});
2)监听文件改变,并执行回调函数
gulp.watch('js/*/.js', function(event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});
(四)第三方插件:
1.gulp-load-plugins:用来加载插件,避免我们再头部声明一堆插件,做到想用就用
2.less:用于编译 .less文件
3.autoprefixer:自动添加css前缀
4.babel:es6 编译成 es5
5.uglify:JS压缩
6.minify:CSS压缩
7.rename:重命名
8.sourcemaps:资源映射
9.concat:合并文件
10.del:删除文件、文件夹
11.inject:文件注入
12.notify:提示信息
13.browser-sync:热启动
14.http-proxy-middleware:配合browser-sync进行跨域
15.changed:只有发生了改变的文件才能进入流中
16.sequence:让task按顺序完成
17.rev:添加MD5
18.watch:监听文件变化
(五)实现项目自动化构建:
使用gulp构建项目,项目中支持:
1)js的模块化(可以实现模块化(common与require的切换));
2)sass的编译;
3)jslint的代码校验;
4)划分本地启动配置和线上打包配置;
5)实现文件的md5加密;
6)实现本地的开发服务;
7)实现mock数据
8)实现本地实时刷新
本篇文章内容还未补全,之后会做更多修改,以期待完成补全,不过对于初学git以及node的同学来说已然是够用了
- xyz ↩