本文已参与「新人创作礼」活动,一起开启掘金创作之路。
git是目前世界上最先进的分布式版本控制系统(git-scm.com/doc)
git概念
工作区
进入监视
git clone / git init / git add / git fetch / git pull(push) / git checkout / git log(reflog) / git config / git status
缓存区stage
半控制
git reset / git checkout / git stash & apply / git rm / git status
远程仓库
全控制
git branch / git merge / git diff / git remote / git rebase / git reset
git常见使用场景
-
git 如何把master分支代码合并到自己的分支
1,先切换到主分支 git checkout master 2, 使用git pull把领先的主分支代码拉下来 git pull 3, 切换到自己的分支 git checkout dev-01 4, 把主分支的代码合并到自己的分支 git merge master -
commit到本地仓库的代码,追加代码修改,最后一起push到远程仓库
git add . git commit --amend
版本格式
主版本.次版本.修订号-beta(先行版本).1 + meta(元数据)
- alpha - 内部测试版本,除非是内部测试人员,否则不推荐使用,有很多bug
- beta - 公测版本,消除了严重错误,还是会有缺陷,这个阶段还会持续加入新功能
- rc - release candidate,发行候选版本,这个版本不会加入新功能,主要是排错,修改bug
全局变量设置
git config --global user.name 'your name'
git config --global user.email 'your email'
配置的时候,加上--global是针对当前用户起作用的,如果不加,就只针对当前的仓库起作用
每个仓库的git配置文件都在.git/config文件中
当前用户的git配置文件放在用户主目录的隐藏文件.gitconfig中
密钥
ssh密钥
git使用https密钥,每次pull、push都要输入密码
使用git协议,然后使用ssh密钥,这样可以省去每次都输密码。需要三个步骤
- 本地生成密钥对
- 设置gitlab上的公钥
- 修改git的remote url为git协议
生成密钥
大多数git服务器都会选择使用SSH公钥来进行授权,系统中的每个用户都必须提供一个公钥用于授权。首先确认一下是否已经有一个公钥了,SSH公钥默认储存在账户的主目录下的~/.ssh目录,查看该目录下是否有id_rsa(密钥)和id_rsa.pub(公钥),如果没有,可以用ssh-keygen创建
添加公钥到git服务器
ssh -T <git@github.com>验证是否添加成功
修改本地的ssh remote url,改用git协议
git remote -v查看当前的remote url
登录git服务器,找到git协议的地址
使用git remote set-url origin git协议的地址
设置完成后,可以用git remote -v查看,已经切换为git协议了,不必再每次输入密码
本地多个SSH密钥文件
有时候可能多个云平台都要用到ssh key来认证,如果每次都覆盖原来的id_rsa文件,那么之前的认证就会失效。这时候我们可以通过在~/.ssh目录下增加config文件来解决
-
配置git用户名和邮箱
git config user.name 'your name' git config user.email 'your email' -
生成ssh key时同时指定保存的文件名
ssh-keygen -t rsa -f ~/.ssh/id_rsa.company -C "email"id_rsa.company就是指定的文件名
这时~/.ssh目录下就会多出id_rsa.company和id_rsa.company.pub两个文件,id_rsa.company.pub里保存的就是我们要使用的key
-
如果config文件不存在,先添加,存在则直接修改
touch ~/.ssh/config在config文件里添加如下内容
Host 域名或者IP IdentityFile ~/.ssh/id_rsa.company User 用户名 -
上传key到云平台后台
-
测试ssh key是否配置成功
ssh -T git@域名 -p 端口号 -
这样便可以配置多个ssh key了
git 命令
git push
第一次使用push之前,需要对git push进行配置
-
simple方式
git config --global push.default.simple
simple方式的push只会push已经从远程仓库pull过的分支,意思就是假如曾经pull了分支dev,那么当使用缺省git push时,当前分支为dev,远程分支dev就会收到commit
-
matching方式
git config --global push.default.matching
matching方式的push会把所有本地的分支push到远程仓库中对应匹配的分支
-
使用
git push [远程仓库][本地分支]
创建版本库
git init创建一个空的版本库,.git目录用来跟踪管理版本库
git status
git diff
获取远程仓库代码
-
git clone
<版本库的网址> <本地目录名> -
git remote add
[远程仓库名][远程仓库地址],比如git remote add origin 仓库地址git要求每个远程主机都必须指定一个主机名,git remote命令就用于管理主机名
git remote列出所有远程主机
git remote -v可以查看远程主机的网址
克隆版本库的时候,所使用的远程主机自动被git命名为origin,如果想用其他的主机名,需要用git clone命令的 -o选项指定
-
拉取代码;如果已经被git管理的项目,可以使用git pull和git fetch来管理代码的拉取和更新;
-
使用git pull拉取远程代码的HEAD头标记,即最新的代码
git pull <远程主机名> <远程分支名>:<本地分支名>
-
git add
把所有的文件更改提交到暂存区
git commit
把所有暂存区的代码写入日志并提交到本地仓库
git push
把所有本地仓库的提交,更新到远程仓库
如果省略远程分支名,则表示将本地分支推送到与之存在追踪关系的远程分支(通常两者同名),如果该远程分支不存在,则会被新建;
git push <远程主机名> <本地分支名>:<远程分支名>
<来源地>:<目的地>
git pull 是<远程分支>:<本地分支>
git push 是<本地分支>:<远程分支>
如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支
git push origin :master
等同于
git push origin --delete master
如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略;如果当前分支只有一个追踪分支,那么主机名都可以省略,只用git push就可以了
如果当前分支与多个主机存在追踪关系,则可以用-u选项指定一个默认主机,这样后面就可以不加任何参数使用git push,不带任何参数的git push,默认只推送当前分支,即simple方式
将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以直接用git push
git push -u origin master
另外一种matching方式,会推送所有有对应的远程分支的本地分支
还有一种情况,就是不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机,这种情况要添加--all选项
git push --all origin
如果远程主机的版本比本地版本新,推送git会报错,要先在本地git pull合并差异,然后再推送到远程主机,如果添加--force选项,会导致远程主机上更新的版本被覆盖,应尽量避免使用--force选项。
git push --force origin
git push不会推送标签tag,除非使用--tags选项
git push origin --tags
git fetch
一旦远程主机的版本库有了新的commit,需要将这些更新取回本地,这时就可以用git fetch
git fetch <远程主机名>
上述命令可以将某个远程主机的更新,全部取回本地
默认情况下,git fetch取回所有分支的更新,如果只想取回特定分支的更新,可以指定分支名
git fetch <远程主机名><分支名>
比如git fetch origin master是取回origin主机的master分支
可以使用git merge或者git rebase命令,在本地分支上合并远程分支
git merge origin/master
git rebase origin/master
上述命令表示在当前分支上,合并origin/master
git pull
取回远程主机某个分支的更新,再与本地的指定分支合并
git pull <远程主机名> <远程分支名>:<本地分支名>
比如取回origin主机的next分支,与本地的master分支合并,写法为:
git pull origin next:master
如果远程分支是与当前分支合并,则冒号后面的分支可以省略
git pull origin next #取回origin/next分支,再与当前分支合并,等同于git fetch origin+ git merge origin/next
git追踪关系tracking
有时候,git会自动在本地分支与远程分支之间,建立一种追踪关系,比如在git clone的时候,所有本地分支默认与远程主机的同名分支,建立追踪关系,即本地的master分支自动追踪origin/master分支。如果当前分支与远程分支存在追踪关系,git pull就可以省略远程分支名;如果当前分支只有一个追踪分支,连远程主机名都可以省略。
如果合并需要采用rebase模式,可以使用--rebase选项
git pull --rebase <远程主机名> <远程分支名>:<本地分支名>
如果远程主机删除了某个分支,默认情况下,git pull不会在拉取远程分支的时候,删除对应的本地分支。可以通过添加-p参数改变这个默认行为
git pull -p
等同于
git fetch --prune origin
git fetch -p
git时光机
git log
查看每次修改的日志文件;git log是顺着当前分支前去查找提交记录;git reflog是作为本地提交记录的清单
git reset
在git中,HEAD表示当前版本,上一个版本就是HEAD^,上100个版本HEAD~100
git reset --hard HEAD^ 回退add命令,提交到缓存区的文件撤销掉,重新放回工作区
git reset HEAD file 提交到缓存区的文件撤销掉,重新放回工作区
git checkout
丢弃缓存区文件的修改,把文件恢复到git add之前的状态
标签管理
发布一个版本时,通常现在版本库中打一个标签,唯一确定打标签时刻的版本,标签属于版本库的一个快照。
实际就是指向某个commit的指针,但标签不可以移动,所以创建和删除标签都是瞬间完成的
注意:标签不是按照时间顺序列出,而是按照字母排序的
可以使用git show [tag name]查看标签信息
创建标签
首先,切换到需要打标签的分支上,使用git tag打一个新标签,也可以使用这个命令查看所有标签
git tag v1.0 <commit id>
默认标签是打在最新提交的commit上的,如果忘记给某次的commit打标签了,可以找到历史提交的commit id,然后打上标签
git tag -a [tag name] -m [tag info]
标签的删除与推送
git push origin可以推送一个本地标签
git push origin --tags可以推送全部未推送的本地标签
git push origin :refs/tags/可以删除一个远程标签,要先用git tag -d [tag name]删除本地标签,再用push删除远程标签
.gitignore忽略文件
一些必须放在工作目录中,但又不能提交的文件,比如数据库密码的配置文件等。在git工作区的目录下创建一个.gitignore文件,然后把要忽略的文件写进去,git提交的时候就会自动忽略这些文件。
忽略文件的原则是:
- 忽略操作系统自动生成的文件,比如缩略图等
- 忽略编译生成的中间文件、可执行文件等
- 忽略带有敏感信息的配置文件
.gitignore规则不生效的解决办法
git rm -r --cached .
git add .
git commit -m 'update .gitignore'
配置别名
git config --global alias.lg "log --color --graph --pretty=format:'%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
执行git lg
Git分支管理
一开始的时候,HEAD头指针指向的是主分支,即master分支。而HEAD指向的是当前分支,master指向的是提交。如果,在master分支上新建一个分支dev,此时HEAD指向了dev,这时对工作区的修改和提交就是针对dev分支的了,比如新提交一次后,dev指针往前移动一步,而master指针不变。
git branch [分支名] #创建分支
git branch #查看所有分支
git checkout [分支名] #切换到对应的分支
git branch -d [分支名] #删除分支
冲突解决
当两个分支中修改相同的文件并提交(add>commit),合并(merge)这两个分支的时候,会产生冲突。
通常,合并分支时,如果可能,git会用fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。如果要强制禁用fast forward模式,git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
-
从master分支切换出一个新的分支
-
在新的分支上修改代码
-
切换回master分支,准备合并dev分支,使用--no--ff模式禁用fast forward
git merge --no--ff -m'merge with no-ff' dev
分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理
- master分支应该是非常稳定的,也就是仅仅用来发布新版本,平时不能在上面修改代码
- 修改代码应该都在dev分支上,即dev分支是不稳定的,等到功能都开发完成了,比如1.0版本发布的时候,再把dev分支合并到master上,在master分支发布1.0版本
- 开发的时候可以每个开发有自己的分支,然后经常往dev分支上合并就可以了
团队合作的分支
每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。比如修复一个代号为101的bug任务,可以新建一个issue-101分支来修复。如果这时在dev开发代码无法提交,可以先用git stash把现场的代码储藏起来,等把bug修复之后,再执行git stash pop把代码还原,继续进行dev的开发工作
如果要在master分支上修复bug,就从master创建临时分支,修复完成后,切换到master分支,并完成合并,最后删除issue-101分支
git checkout master
git checkout -b issue-101
#修改代码
git add [修改的文件名]
git commit -m'fix bug 101'
git checkout master
git merge --no--ff -m'merged bug fix 101' issue-101
git branch -d issue-101
#bug修复完成后,继续回到dev进行开发
git checkout dev
git stash pop #恢复代码的同时把stash里的内容也删除了
git stash apply恢复代码时,stash里的内容并不删除,需要用git stash drop来删除stash
git stash list 查看stash 列表
git stash apply stash@{0}恢复指定的stash代码
删除分支
每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并。最后,删除该feature分支。feature分支与bug分支一般情况下是相似的。
git checkout -b feature-new
git add .
git commit
git checkout dev
git merge --no--ff freature-new
如果突然要取消新开发的功能,需要立刻销毁新分支,分两种情况
- 如果没有合并之前,可以简单的使用git branch -d [分支名]来删除分支(使用-D命令,强制删除分支)
- 如果已经合并,除了上面的需要删除,还需要使用git reset --hard HEAD^来退回到上一个版本
多人协作
首先,可以试图用git push origin branch-name推送自己的修改;如果推送失败,则因为远程分支比本地的更新,需要先用git pull 试图合并,如果合并有冲突,就要先解决冲突,并在本地提交;没有冲突或解决掉冲突之后,再用git push origin branch-name推送就可以了。
如果git pull提示“no tracking information”则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream branch-name origin/branch-name
所有工作流建立再已经建立了个人账户,并添加了SSH key的个人文档中。
Git恢复流程
当中心仓库由于不可抗力因素跨了之后,项目的恢复流程有
恢复指定分支
-
注册账号 > 输入SSH keys > 新建项目
-
在原项目文件夹下,使用git remote -v命令查看;使用git remote remove origin删除原有仓库地址
-
使用新的仓库地址,git remote add origin [ssh仓库地址]
-
添加文件,commit提交,最后push上远程指定分支
git add . git commit -m'add my repo' git push origin master #这条命令会把当前分支,推送到远程的master git push origin dev:dev #把本地的dev分支,推送到远程的dev分支git分支提交的问题:
git push origin dev:dev为什么dev后还要加一个冒号和dev呢git push <远程主机名> <本地分支名>:<远程分支名>前面的是本地分支名,后面的是远程分支名,同名可以省略冒号部分
git push origin dev:dev和git push origin dev是等效的
恢复项目所有分支
git remote remove origin
git remote add origin [新的SSH仓库地址]
git push --mirror [旧的仓库地址]
\