Git常用命令
摘要
- 一起来了解一些git常用命令吧
- 推荐一个学习git命令的网站:https://learngitbranching.js.org/?locale=zh_CN
基本概念
Git的5种工作区域
-
工作目录:用于新增、修改、删除文件,实际我们用于编写代码的目录
-
暂存区:执行
add
命令可以将工作目录对应的文件提交到暂存区,只有加入到暂存区的文件才会参与版本控制,其实际为一堆索引文件,保存在.git/objects
目录下,记录每个文件的快照(hash) -
本地版本库:执行
commit
命令可以将暂存区的文件提交到本地版本库,每一次提交都会记录版本日志,其实际保存位置也是.git/objects
目录下的快照文件,每一次commit
如果文件发生变化都会生成新的快照,.git/refs/heads/
下记录每个分支的最新一次commit的版本号 -
远程跟踪区:
.git/refs/remotes/origin/
下记录每个分支的最新一次更新后的远程版本号,执行fetch\pull\push
时都会更新为最新的远程版本号。如果只执行fetch
仅仅会更新远程跟踪区,并不会更新本地目录,执行pull
命令会同时更新远程跟踪区和本地目录 -
远程版本库:例如github
文件的状态
-
Untracked:未跟踪的文件,尚未加入过暂存区的文件
1 | ➜ git:(release) touch a.txt |
-
要提交的变更[新增],加入暂存区
1 | ➜ git:(release) ✗ git add . |
-
已提交版本库待发布到远端,将暂存区的文件加入本地版本库
1 | ➜ git:(release) ✗ git commit -m 'add a.txt' |
-
尚未暂存以备提交的变更,加入过暂存区的文件发生修改
1 | ➜ git:(release) echo "hello" >> a.txt |
-
要提交的变更[修改],加入过暂存区的文件重新加入暂存区
1 | ➜ git:(release) ✗ git add . |
切换分支时值得注意的地方
-
只要文件没有被commit,无论是新增还是修改,切换分支时,文件的状态都会被带到切换后的分支
-
所以切换分支前,一定要执行commit
HEAD指针和分支指针
-
我们在查看git的log时会看到类似于
* 6d93a15 (HEAD -> master) message
这样的信息,6d93a15就是commit时的版本号,master是分支名称,HEAD就是HEAD指针
,实际上这里的master也是一个指针,他就是分支指针
-
分支指针
永远指向当前分支最新的一次提交版本,分支指针对应版本号保存在.git/refs/heads/
目录下对应的分支文件中 -
HEAD指针
表示我们当前的工作目录是基于哪个版本checkout出来的,通常情况下HEAD指针
指向分支指针
,但当我们通过命令git checkout <commit号>
切换到某个版本时,HEAD指针
就不再指向分支指针
,这个情况有个名字叫作detached HEAD(头分离)
,HEAD指针
对应的版本号保存在.git/HEAD
文件中,这也是为什么我们每次进入项目,git都知道我们当前所在分支或版本号是什么。
问题与方法
1.别人在远程仓库中创建了新的branch,我本地执行git branch -a
却看不到,如何才能看到并checkout呢?
1 | # 1.先要获取远端全部信息 |
2.如何查看本地分支与远程分支的区别?
1 | # 1.先要获取远端全部信息 |
3.如何查看本地两个分支之间的区别?
1 | # 1.比较任意两个分支的区别 |
4.如何查看本地的发生了哪些更改?
1 | # 当前工作目录的索引和上次提交索引之间的差异,只有已经被commit过的文件才会被比较,如果是新增的文件则看不到 |
5.如何提交本地仓库?
1 | # 本地无论是新增文件或修改文件,都要add后才能commit |
6.git reset
–如何回滚到指定版本或分支?
-
git reset的作用是修改HEAD的位置,即将HEAD指向的位置改变为之前存在的某个版本,如果想恢复到之前某个提交的版本,且那个版本之后提交的版本我们都不要了,就可以用这种方法。
-
注意,如果reset包含了已经发布(
git push
)的的版本,此时如果用git push
会报错,因为我们本地库HEAD指向的版本比远程库的要旧,需要执行命令git push -f
强制更新远程 -
注意参数
--hard
有和没有的区别,有–hard,则完全回退到上一版本,丢弃所有其它修改,清空暂存区,同步工作目录到指定版本。没有–hard,则该版本之后的变化会变为Modified状态保留在工作目录,只清空暂存区。
1 | # 1.回滚到上一个版本 |
7..gitignore
–在git中如果想忽略掉某个文件,不让这个文件提交到版本库中,要怎么做呢?
1 | # 在工作目录下创建 .gitignore 文件 |
8.git commit --amend
–commit后发现有内容要修改或者注释写错了,但是不想创建新的一次commit,要怎么办呢?
-
以下命令如果直接合并到已经push过的版本,再次git push时会提示"更新被拒绝,因为您当前分支的最新提交落后于其对应的远程分支",此时可以执行
git push -f
强行发布即可。
1 | # 覆盖上一次提交,这样不会产生新的提交 |
9.git log
–如何查看提交日志?
1 | # 显示版本历史,如果有用git reset --hard xxxxx回退操作,则只会显示到xxx之前的历史 |
10.如何创建本地仓库并绑定到远程仓库?
1 | # 1.首先要在对应的git服务器创建一个新的仓库,一般的git服务器创建新仓库后都会提示你如何绑定该仓库的 |
11.远程仓库地址变更后如何更新?
1 | # 1.命令行修改 |
12.如何将本地仓库同时绑定到多个远程仓库?
1 | # 1.按照`10.如何创建本地仓库并绑定到远程仓库?`中的步骤完成第一个仓库的绑定 |
13.如何在git pull
时不用每次都输入密码?
1 | # 进入项目目录 |
14.如果文件已经git add
到暂存区,但是尚未commit,此时如何将文件从暂存区中移除?
1 |
|
15.git config
–如何设置和查看git配置信息?
1 | # 使用git config命令进行设置和查看 |
16.提交的文件太大(默认是1M),导致push失败怎么办?
1 | # 加大缓冲区大小(http.postBuffer的参数) |
17.clone代码最简单的方式是通过https的形式,不过一般这样做需要输入用户名和密码,如果不想输入用户名和密码可以使用git@xxxx
的形式,如何实现呢?
1 | # 这种方式就是基于ssh的密钥证书来实现 |
18.git branch
–如何创建分支\切换分支\查看分支\删除分支\发布分支?
1 | # 从当前分支创建release分支,但是不切换 |
19.merge还是rebase? 如何合并分支?
一般开发流程:
-
更新主分支 git pull
-
从主分支创建一个开发分支 git checkout -b dev,每个开发人员都会创建一个自己的开发分支
-
开发分支完成测试后合并回主分支,这里推荐使用git rebase,开发分支的log会被移动到主分支的顶端,这样主分支始终是一条线
-
这样主分支在保持干净的同时还保留了每一次commit的log,便于开发人员追溯历史
-
也可以使用
git merge --no-ff
,其也会保留每次的log到主分支上
一般发布流程:
-
将主分支测试完成后合并到发布分支,一般都是由一个专门的人员进行合并,此时推荐使用git merge,这样每次一发布的版本都会产生一个新的节点,而主分支上的多次commit的log不会被记录到发布分支上
-
这样发布分支看起来就是每一次大的发布才会合并一次并记录log,log中可以编辑本次发布的内容。
示例准备,初始化一个待合并的目录
1 | ➜ mkdir git_test |
merge
将dev分支merge到当前分支
1 | git merge dev |
-
merge会将dev分支和当前分支合并后创建一个新的节点放到当前分支最顶端,所以解决完冲突,需要执行如下命令创建一个新的commit
1 | git add . && git commit -m 'merge dev' |
-
merge的log会按照时间顺序显示
-
merge后如果删除了dev分支,则在log中就看不到这个分支信息了,但是日志内容还在
merge示例
1 | ➜ git:(master) ✗ git merge dev |
删除dev分支后
rebase
将dev分支rebase到当前分支
1 | git rebase dev |
-
rebase会将dev分支的每一个节点转移到当前分支最顶端,而不会产生一个新的节点,这样rebase后的分支节点看起来就是一条线
-
解决完冲突,执行
git add .
和git rebase --continue
,不会产生额外的commit -
rebase后如果删除了dev分支,则在log中就看不到这个分支信息了,但是日志内容还在,这样看起来就是主分支自己的节点,没有产生过新的分支
-
如果要放弃本次合并,可以运行
git rebase --abort
rebase示例
1 | ➜ git:(master) git rebase dev |
删除dev分支后
20.git rebase -i
–我commit了很多次,发现都是干的一件事,此时如果直接rebase到主分支会有很多没必要的log,是否可以合并这些commit为一个呢?
1 | # git rebase -i 即可以合并多次提交,也可以合并分支,-i表示可以编辑提交过程,编辑过程中将后面的提交命令修改为s即可,保存后会提示你是否重新编辑log内容,按需编写即可。 |
-
如果修改的提交节点距离结束的提交节点中间有多个节点,而上一次和下一次都需要合并文件,这个过程就要进行多次。个人感觉这个过程虽然可以重新整理提交节点,使节点更准确清晰,但是如果修改的点比较远,文件内容变化复杂,这个多次合并的过程还是比较痛苦的,不推荐在这种情况下使用。
-
推荐的使用场景为,针对同一个功能进行修改,在commit后尚未进行pushsa时发现有些内容需要变化,如果此时没有进行commit,可以执行
git commit --amend -am "注释"
,如果已经执行了commit,则可以执行git rebase -i HEAD~2
。
示例
1 | git rebase -i HEAD~3 |
-
注意这里会进入两次编辑页面,一次是多个提交的处理方式,按提示进行修改即可,一般保留第一行的命令pick,后面的行命令修改为s,然后wq保存。
-
注意合并多个分支时如果包含已经发布过的分支,就要调整顺序,将已经发布的最后一个分支放到第一行,否则再次发布时会提示需要先执行
git pull
,这是因为按照上面的逻辑只有第一行的是提交操作,后面的不会产生新的提交,所以版本号就会落后于远端分支,但是即便调整顺序,也会提示版本偏离,还是要先执行git pull
,待手工合并冲突后再次提交一个新的commit,所以,最好的方法就是不要包含已经发布过的分支,只针对未发布的分支进行合并。
编辑后的:
-
第二个页面是要求你编辑本次合并的log说明,此时每次的log都会显示在这个页面,去掉不需要的,重新编辑一下保存即可。
编辑后的:
查看日志:git log --oneline
21.git revert
–如何撤销指定的提交呢,就是把这次提交回滚到其前一次提交的状态,但是又不影响其之后的提交?
-
git revert
是回滚某个commit ,不是回滚“到”某个 -
git revert
是用一次新的commit来回滚之前的commit,git reset
是直接删除指定的commit。 -
git reset
撤销到某次提交,git revert
撤销某次提交,撤销并不意味着删除本次提交,其log里仍然会有这次提交,只不过revert后会产生一个新的节点用于提交,而reset是会删除之前的log。 -
需要回滚到上一个版本时,可以通过
git reset HEAD~1
实现,也可以通过执行git revert HEAD
实现。区别就是reset后再次发布需要git push -f
,而revert不需要-f参数,因为revert时会在顶端产生一个新的节点,其版本号一定比远端新的。 -
还有一点需要注意,例如dev是从 master checkout出来的分支,然后针对dev执行reset时,如果reset的版本master里也包含,此时再将dev merge 回 master时,则master里会保留本应该被reset的内容,而执行revert则不会出现这个问题,因为revert是用一次逆向的commit“中和”之前的提交。
1 | # 撤销并commit一次新的提交 |
22.如何打tag
1 | git tag -a "prod_x.x.x" -m "message" #打标签, -a是标签名 -m注释 |
23.git pull
= git fetch
+ git merge
这种说法对吗?
-
先说答案,
git pull
!=git fetch
+git merge
,但是确实很像。 -
其实更好的比较是
git pull --rebase
与git fetch
+git merge
的区别,其相当于git fetch
+git rebase
为了说清楚这个问题,我们需要先明白git的三个仓库:
-
本地仓库:如master,修改代码,提交暂存区,然后commit到的就是本地仓库
-
本地远程跟踪仓库:如orign/master,这个仓库不能用来直接提交代码,其用来保存上一次从远程仓库拉取到的最新版本,如果不主动执行git fetch,git pull,git push等,是不会更新这个版本的。
-
远程仓库:git服务器,如github。执行git push时会将本地仓库的版本发布到远程仓库。
-
git fetch
的作用是将远程仓库(如github)的版本与本地远程跟踪仓库(如orign/master)的版本保持一致。之后我们在master分支下执行git merge orign/master
就会将远程仓库的代码合并到本地仓库,所以合并后会产出一个新的版本,我们可以发布这个版本到远程仓库。 -
git pull
的作用很像git fetch
+git merge
,区别就是,git pull
时如果没有冲突,就不会产生新的版本。
24.如何确定开发–>测试–>发布流程?
开发A模型:基于master的主分支开发模型
-
使用git rebase orign将远程分支内容合并到当前master主分支,保证本地master始终是一条线
-
模型结构简单,始终记住使用
git fetch
+git rebase
或者git pull --rebase
更新主分支版本
开发B模型:基于dev的子分支开发模型
-
增加了一个dev子分支,合并回master主分支时可以通过
git rebase -i
的方式合并多个commit为一个commit,减少版本号数量 -
每次开发时要创建新的分支,开发完成后删除该分支,虽略显反锁,但也更加灵活,比如上一次的开发任务还没有完成,有一个紧急的任务需要马上处理,此时只需要从master分支checkout出一个子分支进行开发即可。
-
使用
git pull --rebase
更新远程版本,使用git rebase dev
合并子分支,总的目标依旧是保证本地master始终是一条线
测试模型:基于A\B模型的master的主分支
-
开发完成后,从master分支中checkout出一个release分支用于测试
-
测试过程中如果有bug,则由开发人员通过开发模型进行修复,修复后merge到release中
-
测试通过后,将release分支内容merge到prod分支中(prod表示生产分支,第一次时从release中checkout创建)
-
release分支仅仅为了本次发布内容而创建的测试分支,所以发布后可以删除release分支
发布模型
-
release分支测试通过够会merge到prod生产分支,此时代码可以发布上线,上线前需要为prod打tag
-
只有打了tag的代码才能部署上线,如果线上代码发现bug,则从对应的tag中checkout一个hotfix分支,用于bug修复
-
bug修复后从hotfix分支checkout出hotfix_release分支进行测试,测试过程按照
测试模型
进行 -
测试通过后按照
发布模型
进行 -
发布后需要将hotfix_release分支merge到master开发主分支,之后可以删除本次hotfix分支和hotfix_release分支
说明
-
本地和远程始终存在的分支是master开发主分支和prod发布主分支
-
每次上线前一定要打tag,tag对应的就是当前生产环境
-
测试分支和修复分支根据需要进行创建,用后可以删除。