平时也多是使用 IDEA 自带的 Git 插件,简单又方便,不需要理解 Git 背后的技术,最近突然让我在 VsCode 上更新提交代码,发现又需要学习 VsCode 的插件使用,思量一番后,决定好好学习一下 Git 命令的使用。由于工作需要,对于 Git 的使用又有了新的要求,之前简单的 push、pull 命令已经无法满足平常的使用,因此专门找了个网站实操了一遍,同时将相关内容整理成文档,供后续翻看。
Git基本操作
以下操作来源:http://learngitbranching.js.org/?locale=zh_CN
commit提交
git commit
复制代码
Git 仓库中的提交记录保存的是你的目录下所有文件的快照,就像是把整个目录复制,然后再粘贴一样,但比复制粘贴优雅许多!
有时你提交过代码之后,发现一个地方改错了,你下次提交时不想保留上一次的记录;或者你上一次的 commit message 的描述有误,这时候你可以使用接下来的这个命令:git commit --amend。
查看如下案例:
$ git checkout master
$ git cherry-pick C2
$ git commit --amend
$ git cherry-pick C3
复制代码
branch创建分支
git branch newBranchName
复制代码
创建分支后,然后切换到当前新分支下。
git checkout newBranchName
复制代码
如果你想创建一个新的分支同时切换到新创建的分支的话,可以通过 git checkout -b
来实现。
git checkout -b newBranchName
复制代码
查看远程分支
git branch -r
复制代码
拉取远程分支并创建本地分支
git checkout -b 本地分支名x origin/远程分支名x
复制代码
使用该方式会在本地新建分支x,并自动切换到该本地分支x。采用此种方法建立的本地分支会和远程分支建立映射关系。
git fetch origin 远程分支名x:本地分支名x
复制代码
使用该方式会在本地新建分支x,但是不会自动切换到该本地分支x,需要手动 checkout。采用此种方法建立的本地分支不会和远程分支建立映射关系。
checkout检出/切换分支
将指定版本/tag/分支的代码检出,如果当前存在未提交的代码禁止做此操作。
merge合并
合并两个分支,存在冲突时,能自动合并的会自动合并,不能自动合并的会产生冲突,需要人工处 理。解决冲突的时候,只需要处理冲突的文件,当前工作目录会存在大量提交的文件,不要去还原这些文件,不要去还原这些文件,不要去还原这些文件。
// 创建bugFix新分支,并切换到该分支
git checkout -b bugFix;
//加入做了修改,然后提交
git commit
//切换到master分支,然后做修改并提交
git checkout master
git commit
//将bugFix分支的内容合并到master
git merge bugFix
复制代码
分离Head
目标: 从 bugFix
分支中分离出 HEAD 并让其指向一个提交记录。
执行下述命令:
git checkout master
git checkout C4
复制代码
移动
// 切换分支到C2的父节点C1
git checkout C2^
复制代码
上述 ^ 命令一次只能移动一个位置,比较缓慢。接下来我们学习一个新命令。
移动分支可以直接使用 -f
选项让分支指向另一个提交。例如:
git branch -f master HEAD~3
复制代码
上面的命令会将 master 分支强制指向 HEAD 的第 3 级父提交。
如何将下图中右边的分支修改成左边的样式:
$ git checkout HEAD^
$ git branch -f bugFix HEAD^
$ git branch -f master C6
复制代码
^ 和 ~ 的组合操作
上述三条命令可以简化为下面一条命令。
作业:
git branch bugWork HEAD~^2~
或者
git branch -f bugWork
复制代码
撤销变更
主要有两种方法用来撤销变更 —— 一是 git reset
,还有就是 git revert
。
git reset
通过把分支记录回退几个提交记录来实现撤销改动。你可以将这想象成“改写历史”。git reset
向上移动分支,原来指向的提交记录就跟从来没有提交过一样。
将HEAD 的代码重置到指定版本/tag/分支,重置分成三种,此操作非常危险,非特殊情况禁止此操作
- soft :软重置,重置位置,差异代码会暂存
- mixed :混合重置,重置位置,差异代码不会暂存
- hard :硬重置,重置位置和代码
git reset HEAD~1
复制代码
虽然在你的本地分支中使用 git reset
很方便,但是这种“改写历史”的方法对大家一起使用的远程分支是无效的哦!为了撤销更改并分享给别人,我们需要使用 git revert
。
作业:分别撤销 local
分支和 pushed
分支上的最近一次提交。共需要撤销两个提交(每个分支一个)。
记住 pushed
是远程分支,local
是本地分支 —— 这么说你应该知道用分别哪种方法了吧?
$ git reset local~
$ git checkout pushed
$ git revert pushed
复制代码
作业:
$ git reset HEAD~1
$ git checkout pushed
$ git revert HEAD
复制代码
作业:
// Git 中默认的是 --mixed。
git reset --hard o/master
git checkout -b feature C2
git push origin feature
复制代码
上述命令为标准答案,本人通过另一种方式实现的。
git checkout -b feature
git branch -f master C1
git push
复制代码
cherry-pick复制
作业:
$ git cherry-pick C3 C4 C7
复制代码
again:
$ git checkout one
$ git cherry-pick C4 C3 C2
$ git checkout two
$ git cherry-pick C5 C4 C3 C2
$ git branch -f three C2
复制代码
stash暂存
将当前未提交的代码存放到一个暂存区当中,如果你要做以下其他任何操作,但是当前存在未提交的代码
如果开发完成,则提交后做其他操作
如果没有开发完成,则暂存后进行其他操作
其他操作完毕后,将暂存区的代码恢复到当前工作目录。恢复的方式有两种,一种是应用(apply),仅恢复代码,不清除暂存区的内容。另一种是弹出(unstash),恢复代码并清空暂存区的代码。建议使用应用(apply**)**。
暂存区内可以暂存多次代码,恢复的时候不要选错。
pull拉取
从服务器拉取代码,不指定拉取的分支时会使用当前分支的上游分支(upstream)。如果当前代 码和服务器的代码存在差异,会自动进行一次合并,如果存在冲突并且不能自动处理,会有提示要 人工解决,因此即使本地不存在任何未提交的代码,但是还是会有可能提示冲突。
git pull
相当于 git fetch + git merge
git pull origin foo
相当于:
git fetch origin foo; git merge o/foo
复制代码
还有...
git pull origin bar~1:bugFix
相当于:
git fetch origin bar~1:bugFix; git merge bugFix
复制代码
作业:
git pull origin bar:foo
git pull origin master:side
复制代码
push推送
将当前代码推送到服务器上,不指定拉取的分支时会使用当前分支的上游分支(upstream)
git commit
git commit
git push
复制代码
git fetch;git rebase o/master;git push
复制代码
git fetch;git merge o/master;git push
复制代码
git pull
就是 fetch 和 merge 的简写,类似的 git pull --rebase
就是 fetch 和 rebase 的简写!
进阶
git push <remote> <place>
复制代码
git push origin master
,切到本地仓库中的“master”分支,获取所有的提交,再到远程仓库“origin”中找到“master”分支,将远程仓库中没有的提交记录都添加上去,搞定之后告诉我。
摆脱 git checkout
的束缚,用 git push
实现下面的目标。
$ git push origin master
$ git push origin foo
复制代码
要同时为源和目的地指定 的话,只需要用冒号 :
将二者连起来就可以了:
git push origin <source>:<destination>
复制代码
作业:
$ git push origin foo:master
$ git push origin C5:foo
复制代码
Git 有两种关于
git push origin :side
删除远程分支
fetch下载
从服务器上获取代码到本地,不进行合并。
在上文学习了 git push 的参数,很酷的 参数,还有用冒号分隔的 refspecs(
git fetch origin foo
复制代码
Git 会到远程仓库的 foo
分支上,然后获取所有本地不存在的提交,放到本地的 o/foo
上。
作业:
$ git fetch origin master~:foo
$ git fetch origin foo:master
$ git checkout foo
$ git merge C6
复制代码
git fetch origin :bugFix
在本地创建一个分支
fakeTeamwork模拟团队合作
使用 git fakeTeamwork 制造远程仓库的变更,“假装”你的同事、朋友、合作伙伴更新了远程仓库,有可能是某个特定的分支,或是几个提交记录。
克隆一个远程仓库(用 git clone
),再在刚创建的远程仓库中模拟一些修改,然后在你自己的本地分支上做一些提交,再拉取远程仓库的变更。
git clone
//假设成员做两次修改
git fakeTeamwork 2
//本地提交,生成 C4
git commit
//更新
git pull
复制代码
作业:
思路:
- 克隆你的仓库
- 模拟一次远程提交(fakeTeamwork)
- 完成一次本地提交
- 用 rebase 发布你的工作
git clone
git fakeTeamwork 1
git fetch;git rebase o/master;git push
复制代码
如果你是在一个大的合作团队中工作, 很可能是 master 被锁定了, 需要一些 Pull Request 流程来合并修改。如果你直接提交(commit)到本地 master, 然后试图推送(push)修改, 你将会收到这样类似的信息:
! [远程服务器拒绝] master -> master (TF402455: 不允许推送(push)这个分支; 你必须使用pull request来更新这个分支.)
复制代码
解决办法:新建一个分支 feature, 推送到远程服务器. 然后 reset 你的 master 分支和远程服务器保持一致, 否则下次你 pull 并且他人的提交和你冲突的时候就会有问题。
$ git reset HEAD~1
$ git checkout -b feature C2
$ git push
复制代码
rebase变基/衍合
作业:实现如下操作
git rebase -i HEAD~4
复制代码
再来一题:
// 首先将master移动到C4分支
$ git branch -f master C4
// 由bugFix切换到master上
$ git checkout master
//复制
$ git rebase -i HEAD~3
复制代码
二思后新的解题思路:
git rebase bugFix master
git rebase -i HEAD~3
复制代码
again:(不允许使用 cherry-pick)
//首先将master移动到C3上
$ git branch -f master C3
//检出master
$ git checkout master
$ git rebase -i HEAD~2
$ git rebase -i HEAD~1
$ git rebase -i HEAD~2
复制代码
另一种解题思路:
$ git rebase -i HEAD~2
$ git rebase -i HEAD~1
$ git rebase -i HEAD~2
$ git rebase caption master
复制代码
此外着重需要注意下述命令:
git rebase o/master side1
相当于
git checkout side1
git rebase o/master
复制代码
假设 HEAD 在 foo 分支节点上(foo*标识),如果想要将 master 移动到 foo 所在的节点上,并且检出,则可以通过一条命令来实现。
git rebase foo master
复制代码
tag
分支很容易被人为移动,并且当有新的提交时,它也会移动。分支很容易被改变,大部分分支还只是临时的,并且还一直在变。Tag 可以永远指向某个提交记录的标识呢,比如软件发布新的大版本,或者是修正一些重要的 Bug 或是增加了某些新特性。
它们并不会随着新的提交而移动。你也不能检出到某个标签上面进行修改提交,它就像是提交树上的一个锚点,标识了某个特定的位置。
$ git checkout master
$ git cherry-pick C2
$ git branch -f master C1
$ git cherry-pick C2 C3
复制代码
Describe
由于标签在代码库中起着“锚点”的作用,Git 还为此专门设计了一个命令用来描述离你最近的锚点(也就是标签),它就是 git describe
!
Git Describe 能帮你在提交历史中移动了多次以后找到方向;当你用 git bisect
(一个查找产生 Bug 的提交记录的指令)找到某个提交记录时,或者是当你坐在你那刚刚度假回来的同事的电脑前时, 可能会用到这个命令。
clone
git clone
复制代码
git clone
命令在真实的环境下的作用是在本地创建一个远程仓库的拷贝(比如从 github.com)。
如果你看到一个名为 o/master
的分支,那么这个分支就叫 master
,远程仓库的名称就是 o
。
大多数的开发人员会将它们主要的远程仓库命名为 origin
,并不是 o
。这是因为当你用 git clone
某个仓库时,Git 已经帮你把远程仓库的名称设置为 origin
了
不过 origin
对于我们的 UI 来说太长了,因此不得不使用简写 o
:) 但是要记住, 当你使用真正的 Git 时, 你的远程仓库默认为 origin
!
思路:在 master
分支上做一次提交;然后检出 o/master
,再做一提交。这有助于你理解远程分支的不同,他们的更新只是反映了远程的状态。
$ git commit
$ git checkout o/master
$ git commit
复制代码
克隆一个远程仓库(用 git clone
),再在刚创建的远程仓库中模拟一些修改,然后在你自己的本地分支上做一些提交,再拉取远程仓库的变更。
git clone
//假设成员做两次修改
git fakeTeamwork 2
//本地提交,生成 C4
git commit
//更新
git pull
复制代码
log
git log
复制代码
查看之前提交的 git 历史。
merge与rebase区别
优点:
- Rebase 使你的提交树变得很干净, 所有的提交都在一条线上
缺点:
- Rebase 修改了提交树的历史
比如, 提交 C1 可以被 rebase 到 C3 之后。这看起来 C1 中的工作是在 C3 之后进行的,但实际上是在 C3 之前。
推荐阅读:git rebase 使用详解
组合拳
que1:
思路提示:
- 这里共有三个特性分支 ——
side1
side2
和side3
- 我需要将这三分支按顺序推送到远程仓库
- 因为远程仓库已经被更新过了,所以我们还要把那些工作合并过来
$ git fetch
$ git rebase o/master side1
$ git rebase side1 side2
$ git rebase side2 side3
$ git rebase side3 master
$ git push
复制代码
que2:
标准答案:
$ git checkout master
// git pull 相当于 git fetch + git merge
$ git pull
$ git merge side1
$ git merge side2
$ git merge side3
$ git push
复制代码
que3:
当你克隆时, Git 会为远程仓库中的每个分支在本地仓库中创建一个远程分支(比如 o/master
)。然后再创建一个跟踪远程仓库中活动分支的本地分支,默认情况下这个本地分支会被命名为 master
。
- pull 操作时, 提交记录会被先下载到 o/master 上,之后再合并到本地的 master 分支。隐含的合并目标由这个关联确定的。
- push 操作时, 我们把工作从
master
推到远程仓库中的master
分支(同时会更新远程分支o/master
) 。这个推送的目的地也是由这种关联确定的!
拉取远程分支并创建本地分支
git checkout -b foo o/master
复制代码
使用该方式会在本地新建分支 foo,并自动切换到该本地分支 foo。采用此种方法建立的本地分支会和远程分支 o/master 建立映射关系。
另一种设置远程追踪分支的方法就是使用:git branch -u
命令,执行:
git branch -u o/master foo
复制代码
这样 foo
就会跟踪 o/master
了。如果当前就在 foo 分支上, 还可以省略 foo:
git branch -u o/master
复制代码
查看如下案例:
作业:
$ git checkout -b side o/master
$ git commit
$ git pull --rebase
$ git push
复制代码
查看远程仓库地址
到达指定项目目录下,该目录下包含.git 文件夹。然后打开 git,输入以下命令:
git remote -v
复制代码
查看/修改用户名和邮箱地址
查看用户名和邮箱地址:
$ git config user.name
$ git config user.email
复制代码
修改用户名和邮箱地址:
$ git config --global user.name "username"
$ git config --global user.email "email"
复制代码