Git实操小课堂

语言: CN / TW / HK

平时也多是使用 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 或 git fetch 时不指定任何 source,方法就是仅保留冒号和 destination 部分,source 部分留空。

  • git push origin :side删除远程分支

fetch下载

从服务器上获取代码到本地,不进行合并。

在上文学习了 git push 的参数,很酷的 参数,还有用冒号分隔的 refspecs( : )。 这些参数也适用于 git fetch 。他们的概念是相同的,只是方向相反罢了(fetch 是下载,而 push 是上传)

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 side2side3
  • 我需要将这三分支按顺序推送到远程仓库
  • 因为远程仓库已经被更新过了,所以我们还要把那些工作合并过来
$ 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"
复制代码