- Git命令详解
- 为所有在当前分支上但不在指定分支上的commit创建补丁
- 一个patch文件代表一个commit, 有多少个commit就会生成多少个patch文件
- 例如
- 也可以为指定commit创建patch文件
- 将仓库打包存档,移除了.git目录,只保留最新的代码
- 示例:打包成zip
- 打包成tar
- 将一个分支打包成单独的文件,包含了完整的commit历史
- 示例
- 将打包的文件还原成仓库并创建一条分支(不创建分支看不到历史commit)
- 示例
- 创建.gitignore文件,用于排除不用track的文件
- 在.gitignore中使用通配符*
- 查看2个commit之间的差异
- 也可以是两个分支名
- 查看工作区和暂存区的差异
- 查看暂存区和最新commit的差异
- 查看指定commit的内容
- 等价于用git diff比较这个commit和它上一个commit的差异
- 创建命令缩写
- 示例
- 本地仓库的配置文件在.git/config
- git全局配置文件在~/.gitconfig
Git命令详解
基本概念
Git工作流由四大元素组成:
- 工作区(working directory) : 实际的代码和文件
- 快照(snapshot) :snapshot用于记录代码在当前时间节点相对上一次commit时间节点的改动(diff);暂存snapshots的地方可以称为staging area
- 项目历史(project history): snapshots只有执行commit操作才会被真正记录下来,形成project history
- 分支(branch): 分支代表一条独立的开发线(an independent line of development), 分支本质上是指向某个特定commit的指针
基础操作
# 查看git版本
git --version
# 查看帮助文档,在命令后面加-h,
# 例如: git status -h
# 将当前目录初始化为Git仓库
git init
# 新建一个仓库目录
git init <repo-name>
# 查看每个文件的状态
git status
# 查看每个文件的状态(简单模式)
git status -sb
# 为文件创建snapshot, 添加到暂存区, 用于下次commit, 这个操作叫staging
git add <file>
# stage当前目录所有文件
git add .
# stage除了.开头的所有文件
git add *
# 将snapshots写入项目历史
git commit
# 提交的同时附带信息
git commit -m <message>
# git add和git commit -m合并, -a表示所有tracked文件,对未track的文件无效
git commit -a -m <message>
git commit -am <message>
# verbose冗长模式,可回顾刚刚做了什么
git commit -v
HEAD
HEAD象征的是当前checkout的commit
HEAD通常指向分支的名字,仿佛总是藏在分支名字指针的下面
当HEAD由指向分支名字改为指向某个commit时(git checkout <commitID>
),称为detached HEADHEAD
表示当前commitHEAD^
和HEAD~
都表示上一个commitHEAD~n
表示往上数第n个commit
如果一个commit有多个父节点,则用^n
表示父节点中的第几个(按commit的提交顺序)
~和^可以连在一起使用
比如HEAD~^2~3
表示当前节点往上数1个commit(~1的数字1可被省略), 再往上数多个父节点中的第2个commit,再往上数3个commit
查看commit历史
# 查看commit历史
git log
# 每个commit显示一行
git log --oneline
# 显示额外的信息
git log --stat
# 显示diff信息
git log -p
# 查看since分支头部到until分支头部之间的commit
git log <since>..<until>
# 查看指定的一段commit
git log HEAD~<n>..HEAD
# 查看当前分支最近的n个节点
git log -n 3
git log -n 3 --oneline
# 也可以去掉n直接写数字
git log -3
# 查看之前的commit
git checkout <commit-id>
标签
tag用于永久地标记一个重大历史节点,就像是里程碑
# 给commit添加tag
git tag <tag-name> <commitID>
# 省略<commitID>, 则是给当前所在位置(HEAD)添加tag
git tag <tag-name>
# 添加tag时还可以附带描述信息, -a 表示注释(annotate)
git tag -a <tag-name> -m "<description>"
# 查看所有tag
git tag
# 通过tag查看commit
git checkout <tag-name>
# 显示离指定位置往上最近的tag信息
# <place>可以是<commitID> <branch> <HEAD~n>
git describe <place>
删除文件
# 从工作区删除已经commit的指定文件
git rm <file>
# 不再track指定文件,但保留在工作区
git rm --cached <file>
# 从工作区强制删除add的文件
git rm -f <file>
撤销更改
# 将分支指针回退n个commit, 不改变工作区内容,暂存区add的内容会变成未add
git reset --mixed HEAD~<n>
# 将分支指针回退n个commit, 工作区内容重置为回退到的commit, 暂存区add的内容会丢失
git reset --hard HEAD~<n>
# 将分支指针回退n个commit, 工作区和暂存区都不改变, 类似checkout, 区别是HEAD不是detached
git reset --soft HEAD~<n>
# git reset相当于重写了历史(被撤销的commit变成dangled状态,log查看不到),适合用于本地分支
# 如果是更改和他人共享的远程分支,则不推荐用reset,应该用revert
# revert通过产生一个新的commit的方式,回退某个指定commitID,但仍然保留后面新增的commit
git revert <commitID>
# 示例: 撤销最近一次commit
git revert HEAD
# 例如有下面 3 个 commit
a64e0b0a2 new2
87f874f36 new1
25580dcef 要回退的
# 回退倒数第 3 个 commit, 保留倒数 1 和 2 个 commit
git revert 25580dcef
# 将tracked的文件重置为最近一次的commit,未track的文件不受影响
# 工作区最新未add的修改和暂存区已add的修改都会丢失
# 这里其实是省略了参数HEAD
git reset --hard
# 删除所有未track的文件
git clean -f
# 这两个命令一起用于撤销所有最近未commit的修改
git reset --hard
git clean -f
# 将一个指定文件移出暂存区(unstage),不改变工作区内容
git reset HEAD <file>
# 将指定文件还原到指定的commit状态
git checkout <commit-id> <file>
# 示例: 撤销某个文件的更改
git checkout HEAD index.html
# 撤销整个目录的更改(HEAD可省略)
git checkout .
恢复撤销的commit
# 按时间顺序显示本地操作历史
git reflog
# 如何恢复撤销的commit?
# git reflog查看被撤销的commitID
# git checkout <commitID> 把HEAD移到这个commitID
# git checkout -b <branch-name> 产生一个新的分支,被撤销的commit就在这个分支上了
分支
# 列出所有本地分支
git branch
# 列出远程分支
git branch -r
# 列出本地分支和远程分支
git branch -a
# 创建新的分支,实际上是创建了一个指向当前commit的指针
# 每次给项目添加新特性时都应该创建新的分支
# 不要给分支取无意义的名字
git branch <branch-name>
# 在指定位置创建分支
git branch <branch-name> <commitID>
# 示例
git branch bugFix HEAD~^2~3
# 将分支指针移动到指定的commit
git branch -f <branch-name> <commitID>
# 切换到指定的分支
git checkout <branch-name>
### 注意:未track的文件不属于任何分支
# 创建新的分支并切换到该分支
git checkout -b <branch-name>
# 重命名分支
git branch -m <old-name> <new-name>
# 将指定分支与当前所在分支合并,
# 未分叉的分支合并称为fast-forward merge
# 分叉的分支合并会产生新的commit, 并将当前分支的头部指向新的commit, 称为3-way merge
git merge <branch-name>
# 3-way merge时,如果两个分支都修改了同一个文件,则会产生冲突
# 打开冲突的文件,修改冲突的内容,再git add <path> 和 git commit
# git commit 不需要-m <message>, git会自己添加merge信息
# --no-ff表示拒绝使用fast-forward方式合并,合并后产生新的commit
git merge --no-ff <branch-name>
# --squash表示合并前把分支上需要被合并的commits压缩为一个,合并后产生新的commit
git merge --squash <branch-name>
# 撤销合并
git merge --abort
# 删除指定分支(已经被合并过)
git branch -d <branch-name>
# 删除指定分支(有commit未被合并过)
git branch -D <branch-name>
Rebase
rebase本质上是拷贝一些指定的commit, 放到指定的位置
rebase能让本来是平行的commit序列看起来是线性的
rebase可用于合并分支和修改历史commit
# 合并分支: 将当前分支不在指定分支上的的commits移动到对方头部节点的下面
# <new-base>可以是<commitID>和<branch-name>
git rebase <new-base>
# 如果rebase的过程出现冲突,修改冲突的文件,git add <path>, git rebase --continue
# fast-forward合并同样可以使用git rebase
# rebase还可以添加第二个参数指定要操作的分支,省略则表示当前分支
git rebase <new-base> <branch-name>
# 交互式rebase的原理是回退到指定位置,重新回顾和处理该位置后面的commits
# 可进行的处理有pick(保留), squash(合并), edit(修改), drop(丢弃)等等
# 还可以移动commitID所在的行交换commit的位置
# 最终处理过的commit都会拥有新的commitID
git rebase -i <new-base>
# 示例: 修改当前分支指针到master分支指针之间的commits
git rebase -i master
# 修改最近一次的commit, 而不是产生新的commit
git commit --amend
# 在修改一个commit后继续rebase
git rebase --continue
# 取消交互式rebase,回到之前的状态
git rebase --abort
# 在交互式rebase时如果想删掉中间某个commit, 添加2个新的commit
# 则可以在edit这个commit时
# 用git reset --mixed HEAD~1回退一个commit, 然后提交新的commit,再git rebase --continue
Cherry-pick
# 复制任意个任意位置上的commit放在当前位置(HEAD)的下面
git cherry-pick <Commit1> <Commit2> <...>
# 场景:为了找出bug, 在一个分支上产生很多个commit, 如何只把最后一个commit合并到主分支?
# 方法一: 使用cherry-pick摘取最后一个commit
git checkout master
git cherry-pick <last-commitID>
# 方法二: 先在分支上使用交互式rebase压缩中间的commit, 再合并分支
git rebase -i master # squash中间的commits
git checkout master
git rebase <branch-name>
Stash
stash(贮存)可以将未commit的改动都保存在一个栈上,在任何需要的时候再释放出来
应用场景:
当你发现在一个错误的分支上做了修改,可以先git stash
, 切换到正确的分支后,再git stash pop
# 贮存修改
git stash
# 释放修改
git stash pop/apply
远程仓库
简单来说,只要是与你的仓库相互独立的其他仓库都可以是远程仓库,它可以是在公司的局域网里,在互联网上,甚至是在你电脑的文件系统里。
远程仓库可用于代码备份,团队合作,开源项目等等。
# 拷贝远程仓库
git clone <remote-path>
# 拷贝远程仓库到指定目录
git clone <remote-path> <repo-name>
# 配置当前仓库的信息,让别人知道你是谁
git config user.name <name>
git config user.email <email>
# 列出远程仓库
git remote
# 列出远程仓库的完整路径
git remote -v
## git clone时,git会自动给新克隆的仓库添加origin,指向源仓库(远程仓库)
# 添加远程仓库
git remote add <remote-name> <remote-path>
# 远程仓库重命名
git remote rename <old-name> <new-name>
# 删除本地的远程仓库连接
git remote rm <remote>
# 下载指定远程仓库的所有分支到本地远程分支
git fetch <remote>
# 下载所有远程仓库的所有分支
git fetch --all
# 下载远程仓库的某个分支,或者只下载一部分,<place>可以是分支名也可以是表示某个commit的索引
git fetch <remote> <place>
# fetch只会更新本地远程分支,不会对本地分支做任何事
# fetch之后应当先检查本地分支和远程分支的差异,确认没问题再做合并
# 列出远程仓库的分支
git branch -r
# 查看远程仓库的分支,此时HEAD为detached
# 远程分支是无法被本地修改的,只能通过fetch获取更新
git checkout <remote>/<branch-name>
# 查看本地分支和远程仓库分支的差异
# 正反都比较一下,就能知道本地分支和远程分支是否出现了分叉
git log <branch-name>..<remote>/<branch-name> --stat
git log <remote>/<branch-name>..<branch-name> --stat
# git fetch之后可以通过下面任意一种方式将远程分支与本地分支合并
git cherry-pick <remote>/<branch-name>
git rebase <remote>/<branch-name>
git merge --no-ff <remote>/<branch-name>
# git pull是git fetch和git merge的组合
# 如果不带参数, 则下载所有远程分支, 并将当前checkout的分支与关联的远程分支合并
git pull
# 如果当前checkout的分支没有关联的远程分支,则必须指明下载的远程分支
# 下载指定的远程分支后,会与当前checkout的分支合并(git pull只会与当前分支合并)
git pull <remote> <branch-name>
# git fetch和git rebase的组合
git pull --rebase
git pull <remote> <branch-name> --rebase
# git push将本地分支上新的commits推送到远程仓库上的同名分支
# 如果远程分支不存在则创建分支
git push <remote> <branch-name>
# 如果是两个人合作,各自的仓库互为远程仓库
# 将本地分支推送到远程仓库, 会成为远程仓库的本地分支
# 此时不应该使用git push, 因为你总不希望自己的分支无故被覆盖或冒出多余的分支
# 还可以将指定位置的commit推送到指定的远程分支
# 如果指定的远程分支不存在,则会创建该分支
git push <remote> <local-source>:<remote-destination>
# source可以是分支名, HEAD~n等能标记commit位置的索引
# 指定commit往上的不在远程分支上的commit也会被一起push
# 示例
git push origin foo^:master
# git push <remote> <branch-name>其实是上面指令当分支名相同时的省略
# 如果不传source参数则会在远程仓库中删掉指定的分支
git push <remote> :<remote-destination>
# 示例: 删掉远程仓库中的foo分支
git push origin :foo
# git fetch也有像git push类似的操作,只不过方向相反
# 直接把远程分支上的指定commit下载到指定的本地分支上
# 如果指定的本地分支不存在则会创建分支
# 该指令不会更新本地的远程分支
git fetch <remote> <remote-place>:<local-branch>
# 如果什么都不fetch,则会在本地创建一个新分支
git fetch <remote> :<local-branch>
# git pull同样可以指定<source>和<destination>
# 除了把远程分支上的指定commit下载到指定的本地分支上, 还将指定的分支与当前checkout的分支合并
git pull <remote> <remote-place>:<local-branch>
# 将标签推送到远程仓库(标签不会随分支推送,需另外推送)
git push <remote> <tag-name>
# 删除远程仓库的分支
git push <remote> --delete <branch-name>
# 如果对方仓库的分支处于checkout状态,则不能被删除
本地分支与远程分支关联
# git clone时,不论远程仓库有多少分支,生成的本地仓库只有一条master分支,
# 且master分支自动与origin/master分支关联(track)
# 建立关联后,远程分支可以称为本地分支的upstream branch
# 当git push不传参数时, 默认将当前checkout的分支推送给与其关联的远程分支
# 当git pull不传参数时, 默认下载所有远程分支,并将当前checkout的分支与关联的远程分支合并
# 注意:当git配置push.default的值为upstream, 才可以git push给不同名的关联的远程分支
# 如果值为simple则只允许git push给同名的远程分支
# 远程分支可与任意多条本地分支关联
# 新建一条分支并与指定的远程分支关联
git checkout -b <localBranchName> <remote>/<branch-name>
# 示例
git checkout -b totallyNotMaster origin/master
# 另一种方式建立本地分支与远程分支关联
git branch -u <remote>/<branch-name> <localBranchName>
# 如果这条本地分支已经checkout,参数可以省略
git branch -u <remote>/<branch-name>
# git push时也可通过-u参数将本地分支与远程分支关联
git push -u <remote> <branch>
集中式工作流(Centralized Workflows)
如果只有两个人相互协作,可以各自将对方的仓库设为远程仓库,但这样也会存在被对方随意push分支甚至覆盖自己的分支的危险。
如果有多个人相互协作,则应该使用一个公共的仓库作为远程仓库
# 创建一个裸仓库
# 这个仓库没有工作区,仅有存储功能,适合作为中心仓库
git init --bare <repo-name>
- 刚开始这个仓库什么都没有
- 其中一个人将这个仓库添加为远程仓库,并将他的master分支push到这个中心仓库
- 其他人可以先clone这个中心仓库,然后各自开始工作
- A新增了一个commit, 使用
git push origin master
将本地分支推送到中心仓库,git会自动进行fast-forward merge合并 - B也新增了一个commit, 他将master分支push到中心仓库时,因为中心仓库的master分支有了A新增的commit, 而B推送上来的是在旧的分支上修改的(与中心仓库的分支产生了分叉),中心仓库会拒绝B的push
此时B要先git fetch中心仓库,将他本地的修改与最新的中心仓库进行对比,合并,再重新push到中心仓库
# 下载中心仓库
git fetch origin
# 对比
git log master..origin/master --stat
git log origin/master..master --stat
# 合并
git rebase origin/master
# 上传更新
git push origin master
所有人都遵从这个流程,即为Centralized Workflows
- 注意:如果你的本地commit已经分享到了中心仓库,就绝对不要再用rebase命令修改这些commit, 这将给其他人的工作带来混乱,你只能在push到中心仓库前修改commit
分布式工作流(Distributed Workflows)
集中式工作流对于团队合作开发非常有用,前提是开发人员要有权限访问中心仓库。在开源项目中,如果有陌生人发现你的bug, 想帮助你修改代码,你肯定不希望任何人都有权限访问你的仓库并直接修改你的代码,此时就需要用到分布式工作流。
- 在GitHub或Bitbucket创建一个公开仓库(public),相当于执行了
git init --bare
命令 - 你将这个公开仓库添加为远程仓库,并将本地仓库的代码push到这个公开仓库
- 当有人想帮你修改代码时,执行以下流程:
- 他将你公开仓库的代码clone到本地,创建一个新的分支,修改代码,
- 在他自己的账户下创建一个公开仓库
- 将包含修改代码的分支push到他的公开仓库,告诉你他的公开仓库的地址
- 你把这个地址添加为远程仓库,并fetch到本地
- 你查看他的分支代码,确认他的修改没问题后,合并到你的本地代码,push到你的公开仓库。
- 如果是在GitHub上还可以这样操作:
- 他在GitHub上fork你的公开仓库
- 他将fork后的仓库clone到本地
- 创建新的分支,修改代码,push到他的仓库
- 在他的仓库将分支pull request
- 你在公开仓库查看pull requests
- 你确定修改的代码没问题后,merge pull request
- 在整个过程中,其他人都无法直接修改你的代码,你是代码的整合者(integrator)。
补丁工作流(Patch Workflows)
集中式工作流和分布式工作流都是建立在分支基础上的
而patch是commit级别的, 一个patch文件就是一个可以应用到任何分支上的commit
合作者之间可以通过发送patch文件完成项目修改
当有人想通过patch方式帮你修改代码时,具体流程如下:
- 他先要获取到你的代码(通过邮件或远程仓库)
- 创建一个新的分支(每次修改他人代码或添加新特性都应该这么做)
- 修改代码,提交commit
- 用下面的命令将新的commit生成patch文件
```bash
为所有在当前分支上但不在指定分支上的commit创建补丁
一个patch文件代表一个commit, 有多少个commit就会生成多少个patch文件
git format-patch例如
git format-patch master
也可以为指定commit创建patch文件
git format-patch
- 打开生成的patch文件,会发现文件格式其实是一封邮件,里面包含了**diff**信息
```diff
From e6650616044215c633907e5f8dd1ee29e645fd9a Mon Sep 17 00:00:00 2001
From: jack <jack@gmail.com>
Date: Fri, 17 Dec 2021 12:54:57 +0800
Subject: [PATCH] add pink color css
---
style.css | 2 +-
1 files changed, 1 insertions(+), 1 deletion(-)
diff --git a/style.css b/style.css
index 31c4610..392f7c5 100644
--- a/style.css
+++ b/style.css
@@ -3,5 +3,5 @@
}
p {
- color: green;
+ color: pink;
}
\ No newline at end of file
--
2.26.2.windows.1
- 他将patch文件通过邮件等方式发送给你
- 你将收到的patch文件放到本地仓库根目录
- 新建一个分支(千万不要直接在master分支应用patch)
在分支上使用下面的命令应用patch
# 将patch文件应用到当前分支
git am < <patch-file>
# 例如
git am < 0001-Change-pink-to-a-manly-color.patch
测试没有问题后,合并到master分支
- 删掉patch文件
Tips & Tricks
```bash将仓库打包存档,移除了.git目录,只保留最新的代码
git archive—format=zip —output= 示例:打包成zip
git archive master —format=zip —output=../website-12-10-2012.zip打包成tar
git archive master —format=tar —output=../website-12-10-2012.tar
将一个分支打包成单独的文件,包含了完整的commit历史
git bundle create
示例
git bundle create ../repo.bundle master
将打包的文件还原成仓库并创建一条分支(不创建分支看不到历史commit)
git clone
示例
git clone repo.bundle repo-copy -b master
创建.gitignore文件,用于排除不用track的文件
touch .gitignore
在.gitignore中使用通配符*
.exe .o
查看2个commit之间的差异
也可以是两个分支名
git diff
查看工作区和暂存区的差异
git diff
查看暂存区和最新commit的差异
git diff —cached
查看指定commit的内容
等价于用git diff比较这个commit和它上一个commit的差异
git show
创建命令缩写
git config —global alias.
示例
git config —global alias.co checkout git config —global alias.ci commit git config —global alias.br branch
本地仓库的配置文件在.git/config
git全局配置文件在~/.gitconfig
<a name="hOgLl"></a>
#### Q&A
1. github的https方式现在需要用token登录:[文档](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)
2. 使用https方式时报错
> fatal: unable to access 'https://github.com/xxx': OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443
解决办法:给git设置代理, 将端口号修改为你的代理端口,推荐用clash
```bash
git config --global http.proxy 127.0.0.1:7890
git config --global https.proxy 127.0.0.1:7890
# 取消代理
git config --global --unset http.proxy
git config --global --unset https.proxy
- 不小心把不该commit的文件commit并push到了远程仓库怎么办?
答: 推送到远程仓库的是你的分支上的所有commits,就是git log能查看到的那些commits
如果你想撤回最近一次的commit, 可以用**get reset --mixed HEAD~1**
回退一个版本,重新commit,再**git push -f**
就能覆盖掉远程仓库上的记录
如果你想撤回更早以前的commit, 当然也可以 **get reset --mixed <HEAD~n>**
回退n个版本,再重新commit
也可以使用**git rebase -i <HEAD~n>**
进行交互式rebase, edit那个要修改的commit, 最后再**git push -f**
(以上方法仅适用个人开发,如果是团队开发,请绝对不要轻易修改已经分享到公开仓库的commits)
Git配置
SSH
本地仓库和远程仓库通信推荐用SSH协议; HTTPS每次都要输入密码,很麻烦
原理:私钥在你电脑上,公钥在GitHub上,公钥和私钥可相互解密
生成密钥:
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
cat ~/.ssh/id_rsa.pub
~/ssh目录生成两个文件
id_rsa 私钥(不能给任何人看)
id_rsa.pub 公钥(复制到github上)
测试是否配对成功:ssh -T git@github.com
删除key
rm -f ~/.ssh/id_rsa
rm -f ~/.ssh/id_rsa.pub
本地配置
git config --global user.name 你的英文名
git config --global user.email 你的邮箱
# 默认push当前分支到它的upstream分支上,且本地分支必须与远程分支同名
git config --global push.default simple
# 防止中文乱码
git config --global core.quotepath false
# 配置默认编辑器
git config --global core.editor "code --wait"
# commit时把CRLF转换成LF,checkout时不转换(Unix配置)
git config --global core.autocrlf input
# commit时把CRLF转换成LF,checkout时LF转成CRLF(Windows配置)
git config --global core.autocrlf true
# 配置 git pull 的合并方式,推荐 rebase
git config pull.rebase false # merge (the default strategy)
git config pull.rebase true # rebase
git config pull.ff only # fast-forward only
# 配置以更优雅的方式显示git log
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
# 使用
git lg
# 查看所有配置
git config -l
# 查看全局配置
git config --global -l
# 查看本地仓库配置
git config --local -l
# 查看全局某个配置
git config --global <item-name>
# 例如
git config --global user.name
# 查看本地某个配置
git config <item-name>
# 删除
git config --global --unset user.name
git config --global --unset user.email
bash命令简写
touch ~/.bashrc # 新建bash配置文件
echo 'alias ga="git add"'>> ~/.bashrc
echo 'alias gc="git commit -v"'>> ~/.bashrc
echo 'alias gl="git pull"'>> ~/.bashrc
echo 'alias gp="git push"'>> ~/.bashrc
echo 'alias gco="git checkout"'>> ~/.bashrc
echo 'alias gst="git status -sb"'>> ~/.bashrc
source ~/.bashrc # 每次更改都要source一下
# 美化git log
# 在~/.bashrc里添加
alias glog="git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit -- | less"
# 之后source ~/.bashrc