基本操作
注:命令中被<>括起来的都是参数,实际输入指令时不需要带<>
git clone <git repository address>
:从远程克隆一个本地库- git分为工作区(本地)、暂存区、分支(版本库)
- 一个完整的提交流程应该是:将本地文件通过
git add <file>
命令添加到暂存区,再通过git commit -m "comments"
命令将暂存区的所有文件随注释一起提交到版本库。 git add <file>
:把文件添加到暂存区git commit -m "comments"
:将暂存区的文件随comments
注释一起提交到版本库。每次提交都会通过SHA1计算生成一个唯一的16进制commit id
用于唯一标识版本。但它不方便人阅读区分,所以我们用comments
与版本号锚定,来说明每次提交的更新内容,方便使用者知晓每次提交的意义。git log
:查看所有版本更新的历史记录。如果嫌输出信息太多,令人眼花缭乱,可以试试加上--pretty=oneline
参数git status
:查看当前工作区、暂存区状态
版本回退/穿梭
-
git checkout -- <file>
: 丢弃工作区的修改,回退到最后一次add到暂存区的版本。如果暂存区无文件,则回退到最后一次commit至版本库的版本。本质上是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”
注意--
与<file>
之间需要有1个空格 -
HEAD
可以理解成指向当前版本的指针,参见What is HEAD in Git?上一个版本是HEAD^
,上上个版本是HEAD^^
,往上100个版本是HEAD^100
。还有一种写法是把^
写成~
,如HEAD~
,HEAD~~
,HEAD~100
-
git reset --soft HEAD^
:它执行的操作是:
- 移动HEAD指针使其指向上一个版本快照
soft模式下reset
指令只对版本库有影响, 不改变当前工作区、暂存区的内容。效果相当于撤销上一次commit
操作 -
git reset --mixed HEAD^
:reset
指令默认的参数就是mixed,所以该指令可以简写为git reset HEAD^
。它实际上执行以下操作:
- 移动HEAD指针使其指向上一个版本快照
- 将HEAD移动后所指的快照回滚到暂存区
效果:将暂存区的内容恢复到上一个版本,但不改变当前工作区的内容。 -
git reset --hard HEAD^
:它实际上执行以下操作:
- 移动HEAD指针使其指向上一个版本快照
- 将HEAD移动后所指的快照回滚到暂存区
- 将暂存区的文件还原到工作区
效果:丢弃当前工作区的内容,使工作区、暂存区、版本库同时回退到上一个版本
.git reset --hard <commit id>
:回退到某一版本号对应的版本 -
关于版本回退,Git的
reset
指令允许我们在版本的历史之间穿梭。使用git reset --hard 版本快照的ID号
将指定版本的内容恢复到工作区。一般使用版本号的前5位来指定某个版本快照的ID号
- 若要回退至过去,可以先用git log
查看提交历史(从上至下按时间由近及远排列),以便确定要回退到哪个版本
- 若要重返到未来,可以用git reflog
查看命令历史,以便确定要回到未来的哪个版本 -
git reset 版本快照 路径/文件
:回滚个别文件/目录。其中版本快照可以用HEAD或者版本号来指定。另外需要注意的是:只回滚个别文件,无需移动HEAD指针 -
git reset HEAD <file>
:可以把暂存区的修改撤销掉(unstage),重新放回工作区。<file>
用来指定要从暂存区拿掉文件。如果不指定,git reset HEAD
表示将所有提交至暂存区的文件都拿掉(清空暂存区) -
git rm <file>
:删除文件,之后应使用git commit -m "comments"
将本次操作同步至版本库并追加注释
分支管理
-
对于所有分支而言,工作区和暂存区是公共的。git切换分支时会把未add或未commit的内容带过去
-
分支修改后没有提交,是无法切换到另一分支的
-
当2个人都修改了同一个文件、或两个待合并分支上同一份文件被做了不同的修改,合并时会引发冲突。事实上,在共同开发过程中,由于大家进行的不是相同的开发任务,只要约定某些公用的文件不去提交本地的修改,大多数情况下合并都不会有冲突。
-
git merge <branch name>
:合并指定分支到当前分支。特别的:假设我们要在master分支上使用git merge feature
命令将feature分支合并到mater:
- 若不存在冲突,合并完成之后,master分支改变为合并之后的样子,但是此时的feature分支依然保持自身的样子,并不会因为合并而产生变化。
- 同理,若存在冲突,手动处理冲突、完成合并操作之后,feature分支也依然保持原来的状态没有改变。 -
git merge --no-ff dev
:将dev分支以非快速合并的方式(普通合并)合并至当前分支。--no-ff
参数,表示使用普通合并模式,禁用Fast forward合并。通过普通合并合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。 -
git branch
:查看当前所有的分支 -
git branch <name>
:创建分支 -
git branch -d <name>
:删除分支 -
git checkout <name>
:切换分支 -
git checkout -b <name>
:创建+切换分支 -
git switch <branch>
:切换到已有分支(比git checkout <branch>
更科学) -
git switch -c <branch>
:创建并切换分支(c - creat) -
当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。用
git log --graph
命令可以看到分支合并图。(常用git log --graph --pretty=oneline --abbrev-commit
) -
分支策略:在实际开发中,我们应该按照几个基本原则进行分支管理:
- 首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活
- 那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
- 你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
Bug分支
当你正在dev分支上进行工作,工作尚未完成时,有一个急需修复的bug需要提交。此时,你不能将修复的bug和未完成的工作一起提交,未完成的工作可能会影响到同事。但你也不想舍弃当下工作区和暂存区的的进度。
那么可以通过git stash
功能来暂时保存当前工作进度,去新建一个临时分支来修复bug并提交。
git stash
:将当前工作现场储存起来,日后恢复并继续工作(你可以通过这条命令来储存多个工作现场)
一般使用完git stash
后,git status
查看工作区就是干净的,你需要通过git chekout <another branch>
切换到另一个分支,在那个分支上通过git checkout -b <bug branch>
新建一个分支,完成急需完成并提交的工作(例如修复紧急bug)
然后切换回another branch,删除bug branch
接下来通过git checkout <dev>
回到dev分支继续工作。此时git status
会显示工作区是空的。
使用git stash list
命令查看所有原先保存的工作现场。
恢复原先的工作现场有两种方法:
- 用
git stash apply
恢复,但是恢复后,stash list里内容并不删除,你需要用git stash drop
来删除; - 用
git stash pop
恢复,恢复的同时把stash list里内容也删除
在master分支上修复的bug,想要合并到当前dev分支,可以用git cherry-pick <commit>
命令,把bug提交的修改“复制”到当前分支,避免重复劳动
Feature分支
开发一个新feature,最好新建一个分支;
当你开发完这个feature分支、被添加、提交后但尚未合并时,接到要求需要删掉这个feature分支(在不合并的前提下),git branch -d <feature branch>
会报错:error: The branch 'feature-vulcan' is not fully merged.
此时就要用到以下指令:
git branch -D feature-vulcan
:-D参数代表强行删除未被合并的分支
多人协作
多人协作的工作模式通常是这样:
- 首先,可以试图用
git push origin <branch-name>
推送自己的修改 - 如果推送失败,则因为远程分支比你的本地更新,需要先用
git pull
试图合并 - 如果合并有冲突,则解决冲突,并在本地提交
- 没有冲突或者解决掉冲突后,再用
git push origin <branch-name>
推送就能成功
如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
这就是多人协作的工作模式,一旦熟悉了,就非常简单
小结
git remote -v
:查看远程库信息。注意本地新建的分支如果不推送到远程,对其他人就是不可见的;
git push origin branch-name
:从本地推送分支。\
如果推送失败,先用git pull
抓取远程的新提交
git checkout -b branch-name origin/branch-name,
:在本地创建和远程分支对应的分支,本地和远程分支的名称最好一致;
git branch --set-upstream branch-name origin/branch-name
:建立本地分支和远程分支的关联。注意使用git pull
从远程抓取分支时,如果有冲突,要先处理冲突。
Rebase变基
还没变过,变后来答
标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的。
所以,tag就是一个让人容易记住的有意义的名字(别名),它跟某个commit绑在一起。commit与tag的关系,就类似于IP和域名的关系。
创建标签
创建标签只要先切换到需要打标签的分支上git switch <to be tagged>
再使用git tag <tag name>
打标签即可
git tag <tag name> <commit id>
:给指定的commit打标签,一般与git log
(之后)配合使用
git tag
:查看所有的标签,注意标签不是按时间顺序列出,而是按字母排序的
git tag -a <tag name> -m "instructions" <commit id>
:创建带有说明的标签,用-a指定标签名,-m指定说明文字:
git show <tagname>
:查看标签详细信息、说明文字
注意: 标签总是和某个commit挂钩。如果这个commit既出现在master分支,又出现在dev分支,那么在这两个分支上都可以看到这个标签
操作标签
推送标签:
git push origin <tag name>
:推送某个标签到远程
git push origin --tags
:一次性推送全部尚未推送到远程的本地标签
删除标签:
git tag -d <tag name>
:删除本地标签。因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除
如果标签已经推送到远程,要删除远程标签就麻烦一点:
先用git tag -d <tag name>
从本地删除,然后从远程用push命令删除:git push origin :refs/tags/<tag name>