最近全面整理下工作中遇到的git问题,了解了下git工作的原理。
常用操作
# 查看增删改文件状态
git status
# 增删改进入暂存区
git add .
# 提交待push,staged area
git commit -am 'comment'
# 从staged area回到暂存区
git reset HEAD .
git push
提示up-to-date是最新的
文件生命周期
工作区(working tree,未add的,untracked)=>暂存区(已add的,tracked,staged)=>index(staging area)(commit未push的,staged)=>远端仓库(repository)

HEAD,origin,remote这些概念后面再说
配置
# 列出所有config
git config --list
# 配置
git config --global user.name 'Eva'
git config --global user.email 'eva@163.com'
# 换行符转为crlf
git config --global core.autocrlf ture
# 配置别名:git last为显示上次提交信息,git last -p
git config --global alias.last 'log -1 HEAD'
生成私钥,添加在setting里
有时候提交时一直要求输入密码,我们需要在git clone时选择ssh的方式,这样就免去了每次提交输密码
# 查看私钥
cd ~/.ssh
# 生成私钥
ssh-keygen -t rsa -C "eva@163.com"
新建repository
cd d/workspace
mkdir demo
git init
git remote add origin 'https://github.com/EvaLotus/EvaLotus.github.io'
# 第一次需要-u,用来set upstream
git push -u origin/master
commit
git command -h查看命令
# 提交的主要有:新增,修改,删除
# 查看新增,修改,删除了哪些文件
git status
# 查看具体新增,修改,删除
git diff
# 具体文件修改了啥
git diff a.txt
# 丢弃单文件修改,删除
git checkout -- a.txt
# 丢弃所有修改,删除
git checkout -- .
# 添加单文件修改到暂存区下管理
git add file.txt
# 新增,修改,删除都添加到暂存区
git add .
# 强制add,gitignore里的也会添加
git add -f
# rm都是对暂存区的文件来说的,工作区的直接rm就可以了
# rm必须是在远端有的文件
git rm
# 从暂存区中移除,从tracked变成untracked
git rm --cached
# 直接删除文件
git rm -f
# 没有添加到暂存区的,还在工作区的直接
rm a.txt
# 只要git add .过的,就已经在暂存区了,checkout -- .就不管用了。只能git reset HEAD 了
# 将所有新增,修改,删除都从commit中返回
git reset HEAD .
# 暂存
git stash
# 恢复暂存
git stash pop
git commit -m "my 这里是注释"
# 上次漏提交了文件,可以使用amend命令来修改上次的提交,使log更好看
git commit -amend
# 多个提交时用-a
git commit -am "这里是注释"
回退
# 回退前都先查看log
git log
# reset
# 几个~就是回退几个版本,此处是本地回退
git reset --hard HEAD~~
# 回退到上个版本
git reset –hard HEAD^
# 回退到上100个版本
git reset -hard HEAD~100
# revert
git push -f origin master就是强制push到远端
# log和reflog的区别
Branch
# 查看所有分支(包括远端和本地)
git branch -a
# 查看本地分支
git branch
# 查看所有远端分支
git branch -r
# 查看本地已merged的分支
git branch --merged
# 查看本地未merged的分支
git branch --no-merged
# 新建分支,但是不切换到此分支
git branch mybranch
# 从当前分支新建并切换到此分支
git checkout -b mybranch
# 删除本地分支,可删除多个
git branch -d mybranch1 mybranch2
# 强制删除本地未merge分支
git branch -D mybranch
# 删除远端分支
git push origin :mybranch
# 或
git push origin --delete mybranch
关于删除远端分支注意:
删除远端分支后另一个用户并不能获取到分支被删除了,git branch -r也依然能获取到被删除的分支
这说明,remotes/origin/* 这些远程跟踪分支,仅仅是远程分支的一个缓存,并且,不能通过git fetch 命令获取到分支删除的更新
可以通过git remote show origin来查看,会列出所有分支状态
branchA tracked
refs/remotes/origin/deletedBranchA stale (use 'git remote prune' to remove)
refs/remotes/origin/deletedBranchB stale (use 'git remote prune' to remove)
被删除的分支refs/remotes/origin/deletedBranch状态是stale(陈旧的)
后面有提示
git remote prune origin
# 或
git fetch -p
Stash
# 暂存
git stash
# 列出所有暂存
git stash list
# 列出上次暂存
git stash show
# 恢复暂存,删除list中的
git stash pop stash@{0}
# 只恢复暂存,list中不删除
git stash apply stash@{0}
checkout
# 在本地新建一个分支test并切换到此分支
git checkout -b test
# 切换已有的远端分支或本地分支
git checkout test
# 放弃工作区修改,删除的某文件
git checkout -- a.js
# 放弃所有工作区文件的删除,修改(不影响新增)
git checkout -- .
merge
git checkout dev
# 把master merge到 dev
git merge origin/master
# 在远程仓库也新建一个分支test
git push origin test
# cherry-pick
git cherry-pick commitId
# 和rebase的区别是?rebase的commit history会更干净一点
git rebase
Reflog和log
# 只列出commit的log
git log
# 列出所有操作,包括pull,checkout
git reflog
# 查看每一行最后是谁修改的
git blame file
git的HEAD是当前活跃分支游标
使用小乌龟的log来查看历史
# fetch和pull的区别,fetch不会自动merge更安全点
git fetch
# 在英文状态下按Q退出日志模式
git log
git rm
# origin算是仓库名,可以命名为其他的
git pull origin
# git的origin指向的是本地的代码库托管在github的版本
# 列出对应的都是仓库
git remote -v
# git remote存在的远端分支 origin
# 创建多个远端仓库
git remote add
# 删除远端仓库
git remote rm repositoryName
# 重命名远端仓库
git remote rename eva origin
# 在本地仓库添加一个远程仓库,并将master跟踪到远程分支
git remote add https://github.com/EvaLotus/test.git
# 把我的分支push到远端哪个分支上
git push origin mybranch:master
git push origin mybranch:staging
# 带*的表示当前分支
# 回退到merge前
git reset --merge
- sourceTree请使用英文版的,不然太难用啦!!
gitlab中的权限管理
只有owner在setting里可以邀请member,给member设置master的权限,否则是protected状态
gitlab网站上发出merge request
svn和git的区别
-
SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,如果在局域网还可以,带宽够大,速度够快,如果在互联网下,如果网速慢的话,就纳闷了。
-
Git是分布式版本控制系统,那么它就没有中央服务器,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要联网了,因为版本都是在自己的电脑上。既然每个人的电脑都有一个完整的版本库,那多个人如何协作呢?比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们两之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。
基于 Git Flow 的开发流程
Git Flow 分支模型
-
master 分支 => 正式环境(最为稳定功能最为完整)
-
test 分支(release分支) => 测试环境(发布定期要上线的功能)
-
dev 分支 => 团队协作的发开环境(功能最新最全的分支)
-
hotfix 分支 => 修复线上代码的 bug
-
feature分支 => 某个功能点正在开发阶段
-
开发阶段
-
开发 on your branch
-
gitlab上发起merge request
-
code review,accept merge request,delete branch
测试阶段
- 完成新功能后,dev 分支提交到 test 分支(release分支),进行测试
- 有bug:创建hotfix 分支(修复后合并到 test 分支)
发布上线
- release 分支合并进 master 和 develop
- 上线
- 线上环境小 bug:创建 hotfix 分支进行修改,大bug:版本回滚
分支命名
参考:
- feature——按照功能点(而不是需求)命名;
- test(release)——用发布时间命名,可以加上适当的前缀;
- hotfix——GitLab 的 issue 编号或 bug 性质等。
Commit Message格式
star多的项目都有完善的文档体系和高覆盖的测试用例
type
-
feat:新功能(feature)
-
fix:修补bug
-
docs:文档(documentation)
-
style: 格式(不影响代码运行的变动)
-
refactor:重构(即不是新增功能,也不是修改bug的代码变动)
-
test:增加测试
-
chore:构建过程或辅助工具的变动
Scope
用来说明本次Commit影响的范围,即简要说明修改会涉及的部分。这个本来是选填项,但从AngularJS实际项目中可以看出基本上也成了必填项了。
用来简要描述本次改动,概述就好了,因为后面还会在Body里给出具体信息。并且最好遵循下面三条:
Subject
-
以动词开头,使用第一人称现在时,比如change,而不是changed或changes
-
首字母不要大写
-
结尾不用句号(.)
Body
<body>里的内容是对上面subject里内容的展开,在此做更加详尽的描述,内容里应该包含修改动机和修改前后的对比。
Revert
此外如果需要撤销之前的Commit,那么本次Commit Message中必须以revert:开头,后面紧跟前面描述的Header部分,格式不变。并且,Body部分的格式也是固定的,必须要记录撤销前Commit的SHA值。
git pull = git fetch +git merge
在git merge时会自动生成个merge的时间节点在commit记录里
rebase和merge
两者都是用来合并分支,细节处理上有些不一样
git hook自动部署
ssh git@gitlab.com -T
git迁移
git checkout master
git pull origin master --all
for branch in `git branch -a | grep remotes | grep -v HEAD | grep -v master `; do
git branch --track ${branch#remotes/origin/} $branch
done
export new_repo=$1
git remote set-url origin $new_repo
git remote set-url --push origin $new_repo
git remote -v
git push -u origin --all
git push -u origin --tags
一些补充概念
一些blob,tree,commit对象的内部概念
.git文件夹下的内容
git merge origin/other_branch时有时会自动merge 可以故意commit一个有冲突的再merge就可以显示merge的全部内容了
WIP:在进行中,避免被merge
git rebase
git cherry-pick
HEAD
可以看到.git/HEAD中内容
当在master分支时,内容为ref: refs/heads/master
当在master分支时,内容为ref: refs/heads/master