「 工具 」git 操作及原理

最近全面整理下工作中遇到的git问题,了解了下git工作的原理。

常用操作

1
2
3
4
5
6
7
8
9
# 查看增删改文件状态
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这些概念后面再说

配置

1
2
3
4
5
6
7
8
9
10
11
12
# 列出所有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的方式,这样就免去了每次提交输密码

1
2
3
4
5
# 查看私钥
cd ~/.ssh

# 生成私钥
ssh-keygen -t rsa -C "eva@163.com"

新建repository

1
2
3
4
5
6
7
8
9
10
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查看命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 提交的主要有:新增,修改,删除

# 查看新增,修改,删除了哪些文件
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 "这里是注释"

回退

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 回退前都先查看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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 查看所有分支(包括远端和本地)
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来查看,会列出所有分支状态

1
2
3
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(陈旧的)

后面有提示

1
2
3
git remote prune origin
# 或
git fetch -p

Stash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 暂存
git stash

# 列出所有暂存
git stash list

# 列出上次暂存
git stash show

# 恢复暂存,删除list中的
git stash pop stash@{0}

# 只恢复暂存,list中不删除
git stash apply stash@{0}

checkout

1
2
3
4
5
6
7
8
9
10
11
# 在本地新建一个分支test并切换到此分支
git checkout -b test

# 切换已有的远端分支或本地分支
git checkout test

# 放弃工作区修改,删除的某文件
git checkout -- a.js

# 放弃所有工作区文件的删除,修改(不影响新增)
git checkout -- .

merge

1
2
3
4
5
6
7
8
9
10
11
12
13
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

1
2
3
4
5
6
7
8
# 只列出commit的log
git log

# 列出所有操作,包括pull,checkout
git reflog

# 查看每一行最后是谁修改的
git blame file

git的HEAD是当前活跃分支游标

使用小乌龟的log来查看历史

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 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

测试阶段

  1. 完成新功能后,dev 分支提交到 test 分支(release分支),进行测试
  2. 有bug:创建hotfix 分支(修复后合并到 test 分支)

发布上线

  1. release 分支合并进 master 和 develop
  2. 上线
  3. 线上环境小 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

两者都是用来合并分支,细节处理上有些不一样

[rebase](https://mp.weixin.qq.com/s?__biz=MzAwNDYwNzU2MQ==∣=400938481&idx=1&sn=f4d92674ebf00c0a208936e6467c3da1&scene=21#wechat_redirect\

git hook自动部署

ssh git@gitlab.com -T

git迁移

1
2
3
4
5
6
7
8
9
10
11
12
13
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

TODO概念解析

一些blob,tree,commit对象的内部概念

.git文件夹下的内容

git merge origin/other_branch时有时会自动merge
可以故意commit一个有冲突的再merge就可以显示merge的全部内容了

WIP:在进行中,避免被merge

git rebase

git cherry-pick

可以看到.git/HEAD中内容

当在master分支时,内容为ref: refs/heads/master

当在master分支时,内容为ref: refs/heads/master

Eva wechat
关注Eva的意如小馆,更方便的与我交流