跳到主要内容

git Tips

00 Git配置

全局配置 ~/.gitconfig文件

项目配置 项目下的.git/config文件

别名配置

git配置文件中,增加alias部分,就可以使用诸如git last,git co等别名了

同时配置的其他全局变量如name和email也在这里

[alias]
co = checkout
ci = commit
br = branch
st = status
unstage = reset HEAD
last = log -1
lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
[user]
name = user
email = [[email protected]]

Git使用

00 revert restore reset区别

git revert <commit>... 通过一个新的反向提交,来回退指定的commit提交(如果有冲突,需要先解决冲突)。

git-revert is about making a new commit that reverts the changes made by other commits.

git restore 从索引(默认)或者另外一个提交(--source commmitid)中还原工作区的文件。

git-restore is about restoring files in the working tree from either the index or another commit. ...

git reset 回退分支,移动HEAD位置,会影响提交记录。

git-reset is about updating your branch, moving the tip in order to add or remove commits from the branch.

01 暂存区=>还原=>工作区

提交到暂存区的文件,怎么还原到工作区(分批commit之类的场景)

  • git restore --staged <file>...

    git add命令之后,会提示,可以用这个命令,从暂存区中恢复到工作区,可以使用git restore --staged -p来选择哪些文件从暂存区恢复,哪些不恢复

  • git rm --cached <file>...

    很直白的删除命令,删除暂存区中的文件,恢复到工作区

    git rm命令的作用是从工作区和暂存区删除文件(不会实际删除文件)

  • git reset HEAD <file>...

    相当于用版本库里的文件,覆盖掉暂存区和工作区的文件,默认的mixed模式(覆盖暂存区和工作区)

    由于暂存区已经存在修改,所以回退就覆盖了暂存区的数据

02 版本库=>覆盖=>工作区

版本库中的文件,修改或者删除后,还未add,怎么干掉修改(开发了一部分代码,彻底不想要了)

  • git restore <file>...

    git status会提示,可以使用这个方法,抹去工作区中的修改

  • git checkout -- <file>...

    直接从版本库检出文件,覆盖工作区

  • git reset --hard HEAD

    不能指定目录,把整个工作区修改都删掉,不能针对单个文件

03 抹掉commit

最后一次,或者某一次commit之后的提交,我们都不想要了(可能因为出错了或者其他什么原因)

  • git reset <mode> [commit]/HEAD^类似表达

    取消commit,把代码库HEAD指向commit或者HEAD^类似表达指定的那次commit上,之后的commit都取消掉。

    mode有三种取值(说明被取消的commit怎么处理):

    --soft 所有被取消的commit,都恢复到add之后的状态,即版本库的提交,恢复到暂存区里,工作区没变化

    --mixed 默认模式,所有被取消的版本库的提交恢复到工作区,要提交,需要重新add和commit

    --hard 直接删除掉所有被取消的commit,无法再找回

04 强制覆盖远程分支

一般我们提交代码并推送到远程之后,如果我们本地再次修改,这时候如果继续用git push推送,会发现推送有问题

这时候直接用git push -f强制使用本地分支覆盖远程分支

05 时光机

在各个commit之间穿梭(指定HEAD位置,即当前版本),使用命令git reset --hard commit_id

往历史穿越,先用git log查看提交历史,确定要回退到的commit

往未来穿越,先用git reflog查看历史命令,找到要回到未来的哪个版本

06 merge体现

通常情况下,我们从master拉取一个feat分支,开发完成之后,如果使用merge方式,即在master分支上执行 git merge feat,如果没有冲突,会执行fast-forward,feat分支上的内容,直接接在原master节点后面,变成 原master -> commitid(feat,master)

如果我们删除掉了feat分支,那么除了看commit的comment,就没法知道原来的feat分支都有哪些提交,做了什么事情

但是如果我们使用git merge --no-ff -m 'merge feat 2 master' feat,就会生成一个新的commit,来体现我们从dev4合并到develop的过程。 相当于这个新的commit压缩了本分支的所有commit到一个commit里,并且接在了原master之后。

image20211103151653550

这样,在我么删除掉dev4之后,我们仍然能通过这新生成的一个commit知道该分支都干了什么。

07 git pull的两种模式

git pull = git fetch + git merge git pull --rebase = git fetch + git rebase

最佳实践:

1。通过git pull --rebase来保持当前分支拉到远程的更新(git fetch)之后,是通过rebase(git rebase)把本地的修改接在远程更新的commit之后,保持整个提交线图是一条直线,避免菱形(菱形是因为远程更新fetch到之后,执行了merge,在本地的commit之后产生了一个新的提交)

2。通过git mrege --no-ff -m 'br2 merge to br1' br1命令,人为的制造出一个分叉出来,表明这一系列提交都是为了实现同一个目的,刻意把提交内容弄成一次提交线图分叉。

08 git tag

  • git tag <taganme> -m "blablabla..." [commit id]用于新建一个标签,默认为HEAD,也可以指定一个commitid,-m刻意指定标签信息
  • git tag可以查看所有标签
  • git tag -d <tagname>可以删除一个本地标签 git push origin :refs/tags/<tagname>删除远程标签
  • git push origin <tagname>/--tags可以推送一个或者全部未推送的标签到远程

09 git branch

创建分支

git branch <branch-name> [<start-point>] 创建分支,start-point可选,如果不写,默认就是HEAD,如果写了,就是在指定的位置,比如HEAD3,HEAD^2~2(~HEAD的上一个节点,^2两个parent里的第二个,~2上两个节点)等。

git branch aaa 在当前节点处创建aaa分支

git checkout aaa切换到aaa分支,HEAD指向aaa

git checkout -b aaa在当前节点创建aaa分支,并且切换到aaa分支,即HEAD指向aaa,相当于一次执行了git branch aaa + git checkout aaa,一条命令相当于上面的两条。

强制修改分支位置

git branch -f main HEAD~3 把main分支强制指向他的3级parent节点

git branch -f 分支 xxx把名为【分支】的分支,指向xxx,这里的xxx可以是一个节点,也可以是分支名,也可以是HEAD等代表的相对路径

10 分离HEAD

HEAD 是一个对当前所在分支的符号引用 —— 也就是指向你正在其基础上进行工作的提交记录。

HEAD 总是指向当前分支上最近一次提交记录

HEAD 通常情况下是指向分支名的(如 bugFix)

分离的HEAD,就是让其指向某个具体的提交记录,而不是分支名,比如本来是

HEAD => main => C1 (HEAD指向main,main指向C1)

执行git checkout C1之后,HEAD指向了C1

所以git checkout xxx,就是把HEAD移动到某个地方

  • 如果xxx是具体节点,就是把HEAD移动到该节点上

  • 如果xxx是分支,就是把HEAD指向这个分支

另外,这里的xxx,可以用相对引用,比如HEAD^ bugFix~3,这样通过git checkout命令,可以让HEAD随意在整个链上移动,可以通过HEAD本身相对定位,也可以通过分支名相对定位,也可以通过节点hash值绝对或者相对定位。

所以,不管是HEAD自己,还是节点hash值,还是分支名,只要有个锚点,就可以直接移动HEAD到指定的节点上去(可以通过^或者~n相对定位,也可以通过hash或者分支名绝对定位)。

在git命令里,所有需要指定节点或者分支的地方,本质都是需要节点,而节点可以通过上面说的相对位置或者绝对位置来指定

11 rebase重新认识

  • 交互式rebase里,可以调整commit顺序

git rebase -i HEAD~4 交互式合并,把HEAD往前的4个节点,列出来,可以调整每个commit的顺序,调整到HEAD位置的commit,还可以用git commit --amend来修改,然后可以再用git rebase -i HEAD~4调整回原来的顺序,相当于对一系列提交中的某个节点进行修改,并移回原位。

  • rebase可以支持两个参数

git rebase master feature,相当于get checkout feature + git rebase master

如果只写成git rebase master,相当于git rebase master current-branch

所以相当于 git rebase master [feature],如果feature是当前分支,可以省略,如果不是,相当于先checkout出feature,再执行rebase master操作。

12 git 远程

git fetch完成了两个动作

  • 从远程仓库下载本地仓库中缺失的提交记录

  • 更新远程分支指针(如origin/master)

实际上就是将本地仓库中的远程分支,更新成了远程仓库相应分支最新的状态。但是他并不会改变你本地分支的状态,不会更新本地的master分支。只是单纯的下载操作,对齐本地的远程分支和远程仓库的对应分支。

只下载远程仓库中的某个分支,可以用git fetch origin foo,仅仅只会下载foo,更新本地的远程分支指针o/foo

origin/master这类远程分支保存在本地,所以可以用如cherry-pick rebase merge等命令操作远程分支。

git pull = git fetch + git merge origin/master

git pull --rebase = git fetch + git rebase origin/master

本地和远程的分支名可以不一样,通过下面两个命令都可以绑定

  • git checkout -b foo origin/main 把本地的foo分支和远程的origin/main分支绑定

  • git branch -u origin/main foo

git push origin master 切到本地仓库中的“main”分支,获取所有的提交,再到远程仓库origin中找到master分支,将远程仓库中没有的提交记录都添加上去,搞定之后告诉我。

git push origin main

git pull origin main

这两个命令,由于指定了远程分支,所以不管当前的HEAD在什么位置,都能对main分支进行本地和远程的同步。如果没有指定后面的origin main,那就是当前在哪个分支上,就拉取或者推送哪个分支。

所以日常在某个分支foo上,要拉取或者推送代码,直接用git pull或者git push就可以了,可以认为默认加了[origin foo]

实际上看起来,fetch和push是一对命令,而pull只是对fetch和merge/rebase的组合

在push和fetch里,都可以使用类似git push/fetch origin <source>:<destination>的语法

如果是push,source是指本地位置,destination是远程位置

如果是fetch,source是指远程位置,destinatiom是指本地位置

git push origin <source>:<destinaiton> 把本地的source,推送到远程的destination。

示例:

git push origin foo^:main 把本地的foo分支上一个提交,和远程的main进行同步。本地的o/main分支,指向了foo^节点。

比较特殊的用法,在fetch或者push时,source为空,即git push/fetch origin :foo

push:push空到远程,实际是删除远程的foo分支

fetch:fetch空到本地,实际是在当前HEAD创建了一个foo分支

git pull参数

git pull origin foo = git fetch origin foo + git merge origin/foo

git pull origin bar~1:bugFix = git fetch origin bar~1:bugFix + git merge bugFix

当前在bar上,执行git pull origin master,等于执行git fetch origin master+git merge origin/master,即拉取master远程分支,并且merge到本地的bar上。

当前在bar上,执行git pull origin master:foo,假设本地没有foo,会先在main对应的节点上创建分支foo,然后同步远程的master->foo,然后再把foo merge到当前的bar上