Git实用指南

1. 什么是Git

Git是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。

2. Git命令

git init

代表初始化git仓库

git status

查看git状态

git add

把改动添加到一个暂存区

git commit

git commit -m 提交信息

git pull

git pull origin master 把本远程 master 分支最新的代码更新到本地。一般我们在push之前都会先pull,这样不容易冲突。

git push

git push origin develop 把本地代码推送到远程 develop 分支 git push origin master 把本地代码推送到远程 master 分支

git log

查看所有产生的commit记录

git branch

git branch 查看本地分支列表。git branch -r 查看远程分支列表。git branch -a 查看所有分支列表包括本地和远程分支

git branch -d

git branch -d develop 删除本地develop分支

git branch -D

如果a分支代码没合并到master,执行git branch -d a是删不了的,它会提示你a分支还有未合并的代码。 如果你非要非要删除,那就执行 git branch -D develop 就可以强制删除本地develop分支

git push origin :develop

删除远程develop 分支

git checkout develop origin/develop

如果远程分支有个develop,而本地没有,执行它把远程的develop分支迁到本地

git checkout -b develop origin/develop

把远程的develop 分支迁到本地并切换到该分支

git checkout

git checkout develop 切换到develop 分支。 git checkout -b featureA 新建并且自动切换到 featureA 分支。

git merge

合并分支

git tag

git tag v1.0 新建一个v1.0的标签。 git tag 可以查看历史tag 记录

git tag v1.1 f52c633 给历史提交的commit id 打标签

git push origin v1.0 推送指定的标签到远程仓库

git push origin --tags 推送所有的标签到远程仓库

git tag -d v1.0 删除本地没有推送到远程仓库的标签

git tag -d v1.0 git push origin :refs/tags/v1.0 删除已经推送到远程仓库的标签

git clone

git clone git@github.com:teachat8/test.git git-test 把test项目 clone 到了本地git-test文件夹下

git remote add origin git@github.com:teachat8/test.git

把本地项目与 GitHub 上的 test 项目进行关联。即添加一个远程仓库,它的地址是 git@github.com:teachat8/test.git ,而 origin 是给这个项目的远程仓库起的名字。

git remote -v

查看有哪些远程仓库

3. Git 进阶

1. diff

diff命令算是很常用了,使用场景是我们经常在做代码改动,但是有的时候2天前的代码了,做了哪些改动都忘记了,在提交之前需要确认下,这个时候就可以用diff来查看你到底做了哪些改动。值得一提的是直接输入 git diff 只能比较当前文件和缓存区文件差异,什么是缓存区?就是你还没有执行 git add 的文件。

git diff <$id1> <$id2>   # 比较两次提交之间的差异

git diff <branch1>..<branch2> # 在两个分支之间比较 

git diff --staged   # 比较暂存区和版本库差异

2. checkout

我们知道 checkout 一般用作切换分支使用,比如切换到 develop 分支,可以执行:

git checkout develop

但是 checkout 不只用作切换分支,他可以用来切换 tag,切换到某次 commit,如:

git checkout v1.0

git checkout ffd9f2dd68f1eb21d36cee50dbdd504e95d9c8f7 

# 后面的一长串是commit_id,是每次commit的SHA1值,可以根据 git log 看到。

除了有“切换”的意思,checkout 还有一个撤销的作用,举个例子,假设我们在一个分支开发一个小功能,刚写完一半,这时候需求变了,而且是大变化,之前写的代码完全用不了了,好在你刚写,甚至都没有 git add 进暂存区,这个时候很简单的一个操作就直接把原文件还原:

git checkout a.md

这里稍微提下,checkout 命令只能撤销还没有 add 进暂存区的文件。

3. stash

设想一个场景,假设我们正在一个新的分支做新的功能,这个时候突然有一个紧急的bug需要修复,而且修复完之后需要立即发布。当然你说我先把刚写的一点代码进行提交不就行了么?这样理论上当然是ok的,但是这会产品垃圾commit,原则上我们每次的commit都要有实际的意义,你的代码只是刚写了一半,还没有什么实际的意义是不建议就这样commit的,那么有没有一种比较好的办法,可以让我暂时切到别的分支,修复完bug再切回来,而且代码也能保留的呢?

这个时候 stash 命令就大有用处了,前提是我们的代码没有进行 commit ,哪怕你执行了 add 也没关系,我们先执行

git stash

什么意思呢?就是把当前分支所有没有 commit 的代码先暂存起来,这个时候你再执行 git status 你会发现当前分支很干净,几乎看不到任何改动,你的代码改动也看不见了,但其实是暂存起来了。执行

git stash list

你会发现此时暂存区已经有了一条记录。

这个时候你可以切换回其他分支,赶紧把bug修复好,然后发布。之后一切都解决了,你再切换回来继续做你之前没做完的功能,但是之前的代码怎么还原呢?

git stash apply

你会发现你之前的代码全部又回来了,就好像一切都没发生过一样,紧接着你最好需要把暂存区的这次 stash 记录删除,执行:

git stash drop

就把最近一条的 stash 记录删除了,是不是很方便?其实还有更方便的,你可以使用:

git stash pop

来代替 apply 命令,pop 跟 apply 的唯一区别就是 pop 不但会帮你把代码还原,还自动帮你把这条 stash 记录删除,省的自己再 drop 一次了,虽然更方便,但是使用起来也需要更加谨慎,为了验证你可以紧接着执行 git stash list 命令来确认是不是已经没有该记录了。

最后还有一个命令介绍下:

git stash clear

就是清空所有暂存区的记录,drop 是只删除一条,当然后面可以跟 stash_id 参数来删除指定的某条记录,不跟参数就是删除最近的,而 clear 是清空。

4. merge & rebase

我们知道 merge 分支是合并的意思,我们在一个 featureA 分支开发完了一个功能,这个时候需要合并到主分支 master 上去,我们只需要进行如下操作:

git checkout master
git merge featureA

其实 rebase 命令也是合并的意思,上面的需求我们一样可以如下操作:

git checkout master
git rebase featureA

merge 的含义:从两个 commit「分叉」的位置起,把目标 commit 的内容应用到当前 commit(HEAD 所指向的 commit),并生成一个新的 commit;

merge 的三种特殊情况: 冲突 原因:当前分支和目标分支修改了同一部分内容,Git 无法确定应该怎样合并; 应对方法:解决冲突后手动 commit。 HEAD 领先于目标 commit:Git 什么也不做,空操作; HEAD 落后于目标 commit:fast-forward。

Git 仓库回到 merge 前的状态:git merge --abort

5. 解决冲突

假设这样一个场景,A和B两位同学各自开了两个分支来开发不同的功能,大部分情况下都会尽量互不干扰的,但是有一个需求A需要改动一个基础库中的一个类的方法,不巧B这个时候由于业务需要也改动了基础库的这个方法,因为这种情况比较特殊,A和B都认为不会对别人造成影响,等两人各自把功能做完了,需要合并的到主分支 master 的时候,我们假设先合并A的分支,这个时候没问题的,之后再继续合并B的分支,这个时候想想也知道会有冲突了,因为A和B两个人同时更改了同一个地方,Git 本身他没法判断你们两个谁更改的对,但是这个时候他会智能的提示有 conflicts ,需要手动解决这个冲突之后再重新进行一次 commit 提交。

6. 查看改动内容

  1. 查看历史中的多个 commit:git log
    • 查看详细改动: git log -p
    • 查看大致改动:git log --stat
  2. 查看具体某个 commit:git show
    • 要看最新 commit ,直接输入 git show;要看指定 commit ,输入 git show commit的引用或SHA-1
    • 如果还要指定文件,在 git show 的最后加上文件名
    1. 查看未提交的内容:diff
    • 查看暂存区和上一条 commit 的区别:git diff --staged(或 --cached)
    • 查看工作目录和暂存区的区别:git diff 不加选项参数
    • 查看工作目录和上一条 commit 的区别:git diff HEAD

7. 提交的技巧

1. 修改写错的最新commit

生成一条新的commit替换了原commit,push 之后就不能用了 git commit --amend

2. 修改写错的倒数第二个commit

开启交互式 rebase git rebase -i

当 rebase UI界面打开时, 你能做3件事:

  • 调整提交记录的顺序(默认顺序旧的 commit 会排在上面,新的commit排在下面)
  • 删除你不想要的提交(通过切换 pick 的状态来完成,关闭就意味着你不想要这个提交记录)
  • 把多个提交记录合并成一个

每个 commit 默认的操作都是 pick (从rebase UI界面也可以看出),表示「直接应用这个 commit」。所以如果你现在直接退出编辑界面,那么结果仍然是空操作。

需要把它的操作指令从 pick 改成 edit 。 edit 的意思是「应用这个 commit,然后停下来等待继续修正」。

用 commit --amend 来把修正应用到当前最新的 commit git commit --amend

继续 rebase 过程,把后面的 commit 直接应用上去 git rebase --continue

在 Git 中,有两个「偏移符号」: ^ 和 ~。

^ 的用法:在 commit 的后面加一个或多个 ^ 号,可以把 commit 往回偏移,偏移的数量是 ^ 的数量。例如:master^ 表示 master 指向的 commit 之前的那个 commit; HEAD^^ 表示 HEAD 所指向的 commit 往前数两个 commit。

~ 的用法:在 commit 的后面加上 ~ 号和一个数,可以把 commit 往回偏移,偏移的数量是 ~ 号后面的数。例如:HEAD~5 表示 HEAD 指向的 commit往前数 5 个 commit。

git rebase -i HEAD^^^ 列出最近的三条提交记录

git rebase -i HEAD~3 列出最近的三条提交记录

3. 撤销commit

撤销最新的提交 git reset --hard

恢复到它的父 commit git reset --hard HEAD^

git reset --hard HEAD~2 将最近两次提交节点的提交记录全部清除

git reset --hard HEAD^^ 将最近两次提交节点的提交记录全部清除

reset的其他用法

git reset --hard: 重置位置的同时,清空工作目录和暂存区的所有内容; git reset --soft: 重置位置的同时,保留工作目录和暂存区的内容,并把重置 HEAD 的位置所导致的新的文件差异放进暂存区。 git reset --mix(默认): 重置位置的同时,保留工作目录的内容,把暂存区和重置 HEAD 的位置所导致的新的文件差异都放入工作目录。

4. 写错的commit被push,撤销commit

git revert HEAD^

5. 找回被删除的branch

reflog 是 "reference log" 的缩写,使用它可以查看 Git 仓库中的引用的移动记录。如果不指定引用,它会显示 HEAD 的移动记录。假如你误删了 branch1 这个 branch,那么你可以查看一下 HEAD 的移动历史:git reflog

可以切换回 git commit id,然后重新创建 branch1 :

git checkout c08de9a git checkout -b branch1

这样,你刚删除的 branch1 就找回来了。

注意:不再被引用直接或间接指向的 commits 会在一定时间后被 Git 回收,所以使用 reflog 来找回删除的 branch 的操作一定要及时, 不然有可能会由于 commit 被回收而再也找不回来。

Teachat's Blog | Share with you IT technology, to achieve progress together.