Skip to content

cheatsheet - git

[[toc]]

链接

配置

##############################################################################
# 配置
##############################################################################
git config --global user.name "Your Name"         # 配置用户名
git config --global user.email "Email Address"     # 配置邮箱地址
git config --global core.autocrlf false # 禁止CR LF自动转换
git config --global color.ui true       # 配置颜色开启
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.br branch
git config --global alias.unstage 'reset HEAD' # 运行git reset HEAD 相当于将index区的提交回退到源码区
git config --global alias.last 'log -1'
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 config --global core.quotepath false     # 设置显示中文文件名
git config --global credential.helper store  # 每次都需要输入用户名和密码?

初始化

##############################################################################
# 初始化
##############################################################################
git init

提交修改

##############################################################################
# 提交修改
##############################################################################
git add <file>
git add -u 提交work directory中所有已track的文件至staging area
git commit -m "descriptions"
git commit --amend 对最近一次的提交做内容修改
git commit --amend --author "user_name <user_email>" 修改最近提交用户名和邮箱

查看状态、对比

##############################################################################
# 查看状态、比对
##############################################################################
git status
git status -s 文件状态缩略信息, 常见 A:新增; M:文件变更; ?:未track; D:删除
git diff <file>
git diff HEAD -- <file>  查看工作区和版本库里面最新版本的区别
git diff --check <file>     检查是否有空白错误(regex:' \{1,\}$')
git diff --cached <file>    查看已add的内容(绿M)

查看历史版本、历史操作

##############################################################################
# 查看历史版本、历史操作
##############################################################################
git log
git reflog
git log -n                  最近n条的提交历史
git log <branch_name> -n    分支branch_name最近n条的提交历史
git log --stat              历次commit的文件变化
git log --shortstat         对比--stat只显示最后的总文件和行数变化统计(n file changed, n insertions(+), n deletion(-))
git log --name-status       显示新增、修改、删除的文件清单
git log lhs_hash..rhs_hash  对比两次commit的变化(增删的主语为lhs, 如git log HEAD~2..HEAD == git log HEAD -3)
git log -p                  历次commit的内容增删
git log -p -W               历次commit的内容增删, 同时显示变更内容的上下文
git log origin/EI-1024 -1 --stat -p -W 查看远端分支EI-1024前一次修改的详细内容
git log origin/master..dev --stat -p -W 查看本地dev分支比远端master分支变化(修改)的详细内容

git log <branch_name> --oneline   对提交历史单行排列
git log <branch_name> --graph     对提交历史图形化排列
git log <branch_name> --decorate  对提交历史关联相关引用, 如tag, 本地远程分支等
git log <branch_name> --oneline --graph --decorate 拼接一下, 树形化显示历史
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen%ai(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit 同上, 建议alais保存

git log --pretty=format 常用的选项(摘自progit_v2.1.9)
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 --date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明

git log --since --after     显示时间之后的提交
git log --until --before    显示时间之前的提交
git --author                显示指定作者的提交
git --committer             显示指定committer的提交(注:committer不一定是author)
git log -S [keyword]        仅显示添加或移除了某个关键字的提交(某些场景比单独git log -p | grep [keyword] 好用很多)
git log origin/b3.3/master --author=yx-ren --since="2019-10-01" --before="2019-11-01" 查看某作者在某发布版本最近一个月的提交, 常见于线上背锅
git log origin/b3.0/master --author=some_leave --since="1 month ago" 查看某刚离职同事过去一个月的提交, 常见于背锅
git log --since=1.weeks     过去一周的提交(写周报的时候可以看看我这一周干了啥)
git log --since=1.days      过去一天的提交(下班的时候可以看看我这一天干了啥)
git log --since="1 weeks 2 days 3 hours 40 minutes 50 seconds ago" 过去1周2天3小时40分50秒之内的提交

版本回退、前进

##############################################################################
# 版本回退、前进
##############################################################################
git reset --hard HEAD^  回退到上1版本
git reset --hard HEAD~5  回退到上5个版本
git reset --hard id  回退到指定版本

撤销修改

##############################################################################
# 撤销修改
##############################################################################
git checkout -- <file>  撤销修改:误修改工作区文件,未git add/commit
git restore <file>  撤销修改:误修改工作区文件,未git add/commit
git reset HEAD <file>  撤销git add:误将文件加入暂存区(git add),未git commit
git reset --hard HEAD^  撤销git commit:误将文件提交(一旦提交,只能通过版本回退进行撤销)

删除与恢复

##############################################################################
# 删除与恢复
##############################################################################
git rm/add <file>
git commit -m "remove <file>" 删除版本库中的<file>:删除工作区文件后,继续删除版本库中相应的文件
git checkout .         撤销修改过的,但是对于被git rm的文件不适用
git checkout -- <file>  根据版本库中的<file>恢复工作区<file>
git checkout <branch> -- <file> 适用于被git rm的文件

清理工作区

##############################################################################
# 清理工作区未track也未ignore的文件或文件夹(如各种临时.swp, .patch文件等)
##############################################################################
git clean -i    #交互式清理, 不常用
git clean -n    #查看清理文件列表(不包括文件夹), 不执行实际清理动作
git clean -n -d #查看清理文件列表(包括文件夹), 不执行实际清理动作
git clean -f    #清理所有未track文件
git clean -fd   #清理所有未track文件和文件夹, 常用, 但使用前确保新增加的文件或文件夹已add, 否则新创建的文件或者文件夹也会被强制删除
git clean -fdx  #增加x选项是删除所有.gitignore忽略的文件,请谨慎使用

关联Github远程仓库

##############################################################################
# 关联GitHub远程仓库(本地到远程)
##############################################################################
git remote rename origin upstream  将远程分支名从origin改成upstream
git branch --set-upstream-to=origin/branch-name 将当前分支关联远程分支
git fetch --all --prune 重新从远程拉去仓库,这个操作会删除当前跟远程仓库无关的分支,谨慎执行,确保当前的仓库分支都已经保存了
git remote add origin <remote address> 在本地工作区目录下按照 GitHub 提示进行关联
git remote rm origin   解除错误关联
git push -u origin master  第一次将本地仓库推送至远程仓库(每次在本地提交后进行操作)
git push origin master   以后每次将本地仓库推送至远程仓库(每次在本地提交后进行操作)
<remote address>:
 git@github.com:<username>/<repository>.git
 https://github.com/<username>/<repository>.git

克隆Github远程仓库

##############################################################################
# 克隆GitHub远程仓库(远程到本地)
##############################################################################
git clone <remote address> git协议速度更快但通常公司内网不允许,https协议速度慢

分支管理

##############################################################################
# 分支管理:创建、切换、查看、合并、删除
##############################################################################
git branch <branch name> 创建<branch name>分支
git checkout <branch name> 切换至<branch name>分支
git switch <branch name> 切换至<branch name>分支
git checkout -b <branch name> 创建并切换至<branch name>分支
git switch -c <branch name> 创建并切换至<branch name>分支
git branch   查看已有分支(* 表示当前分支)
git merge <branch name>  合并<branch name>到当前分支(通常在master分支下操作)
git branch -d <branch name> 删除分支
##############################################################################
# 分支管理:合并后删除分支也在 log 中保留分支记录
##############################################################################
git merge --no-ff -m "descriptions" <branch name>

解决合并冲突

##############################################################################
# 解决合并冲突
##############################################################################
合并时报错“分支发生冲突”,首先vim相应文件,修改冲突位置,然后按照git add/commit重新提交,最后删除多余分支即可。
git log --graph --pretty=oneline --abbrev-commit
git log --graph

开发流程

##############################################################################
# 开发流程:
##############################################################################
master分支  发布稳定版本
dev分支   发布开发版本
<developer name>分支 个人开发分支(个人开发完成将该分支并入dev,同时保留该分支,继续开发)
git flow 使用git-flow工具管理开发流程

Bug分支管理(建立单独分支进行bug修复)

##############################################################################
# Bug分支管理(建立单独分支进行bug修复)
##############################################################################
软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
git stash   保存当前工作现场(在dev未完成开发,但master有bug需要修复)
git stash pop   回到dev分支后恢复工作现场(list中的现场会同时被删除)
git stash list   查看当前存储的工作现场
git stash apply stash@{#} 回到指定工作现场(list中的现场不会被删除,需要用git stash drop)
git stash drop stash@{#} 删除指定工作现场
git cherry-pick <id>  在master修复好bug后,在dev复制一遍bug修复流程

Feature分支管理(建立单独分支添加新功能)

##############################################################################
# Feature分支管理(建立单独分支添加新功能)
##############################################################################
软件开发中,总有无穷无尽的新的功能要不断添加进来。添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。
git branch -D <branch name> 强制删除分支(丢弃未合并分支)

协作与分支推送

##############################################################################
# 协作与分支推送
##############################################################################
User 1:
git remote [-v]      查看远程库信息(-v 查看详细信息)
git push origin [master/dev/...]   推送指定分支到远程
User 2:
git clone <remote address>    克隆到本地(只能克隆master)
git checkout -b dev origin/dev    本地新建分支并关联远程
git add/commit/push     添加、提交、推送更新
User 1:
git add/commit/push     推送时报错(与user 2推送的更新冲突)
git pull <remote> <branch>
git branch --set-upstream-to=origin/<branch> <branch> 本地与远程关联
git pull      拉取远程文件(并解决冲突)
git commit/push      重新提交并推送

标签管理

##############################################################################
# 标签管理(常用于版本管理):查看、创建、操作
##############################################################################
git tag        查看标签
git show <tag name>      查看指定标签
git log --pretty=oneline --abbrev-commit --decorate=full 在log中显示标签
git tag <tag name>      为上次commit位置打标签
git tag <tag name> <commit id>     为指定commit位置打标签
git tag -a <tag name> -m "descriptions" <commit id>  为指定commit打标并添加描述
git tag -d <tag name>      删除本地标签
git push origin <tag name>     推送指定标签到远程
git push origin --tags      推送所有本地标签到远程
git push origin :refs/tags/<tag name>    删除远程标签(先删除本地标签)

子模块操作

##############################################################################
# submodule子模块操作
##############################################################################
git submodule add <remote address> <submodule_dir> 增加子模块
git submodule update --init --recursive # 循环更新子模块
git submodule foreach {git command} # 循环子模块执行git指令

rebase

git rebase branch一个参数

git rebase branch是对你当前所在的分支(也就是HEAD指向的分支)rebase到branch对应的分支上 ,这是最常用的用法;其中branch可以是本地的分支,也可以是上游的远程分支,比如 git rebase master / git rebase origin/master

 # 执行 git rebase branch,分支的变化如下   
          Before                           After
    A---B---C---F---G (branch)        A---B---C---F---G (branch)
             \                                         \
              D---E (HEAD)                              D---E (HEAD)
 # 本质含义是本来D到E(也就是HEAD指向的分支)的祖宗(parent)是C,运行之后祖宗变成了branch的最新commit G
 # 这个在执行merge request到组织的仓库之前必须要做的操作

git rebase --onto两个参数

git rebase --onto 允许你在rebase时,可以精确到部分commit id上

 # 执行 git rebase --onto F D,分支会出现如下的变化
           Before                           After
    A---B---C---F---G (branch)        A---B---C---F---G (branch)
             \                                     \
              D---E---H---I (HEAD)                  E---H---I (HEAD)
 # 执行 git rebase --onto F D, 跟执行 git rebase --onto F D HEAD (三个参数的情况)是一样的,HEAD是被省略了,其中HEAD指向的是当前分支,所以运行 前先要切换到自己想要rebase的分支上,防止执行的结果不是自己想要的
 # 意思就是把D到HEAD之间的提交(不包含D),挂载到F上
 # D及D之前分支(不在branch上的提交)的提交都会被踢掉
# 当只有一个分支的情况下,执行git rebase --onto B E, 分支变化如下
          Before                       After
    A---B---C---E---F (HEAD)        A---B---F (HEAD)
# 相当于执行git rebase --onto B E HEAD 
# 相当于删除了中间过的C和E,这个在你想删除该commit id的情况下十分实用,当然你也可以使用交互式的rebase来完成删除commit的操作git rebase -i {commit_id}

git rebase --onto 三个参数

相比于两个参数的情况下,这个更加的精确

 # 执行 git rebase --onto F D H后,分支的变化如下
           Before                                     After
    A---B---C---F---G (branch)                A---B---C---F---G (branch)
             \                                             \
              D---E---H---I (HEAD)                          E---H (HEAD)
# 相当于把D到H之间的提交(包括H,但不包括D)挂载在F之后
##############################################################################
# rebase(换基)
##############################################################################
# rebase 在日常中常用功能主要是两个, 多人协同开发定期rebase master以及压缩某分支多个commit
git rebase master 常见于多人开发, 每个开发人员从master checkout出自己的分支, 开发一段时间后提交至master之前最好rebase一下, 防止冲突,
              就算真有冲突在本地解决好过强制提交, 开发流程中尽量保证master的干净整洁

举个例子:
master分支上有三个提交C1, C2, C3
某一时刻usr1在C3的master分支上checkout出新的分支, 用于开发服务端支持ipv6新特性, 并提交了C4, C5
git checkout -b ipv6_support
......
git commit -m C4
......
git commit -m C5
此时提交状态如下所示
      (origin/master branch)
             |
C1 <- C2 <- C3
             \
              \
               \
                C4 <- C5
                       |
                (ipv6_support branch)

某同事usr2修改了master上的内存泄漏错误, 并提交了C6, C7, C8三个commit, 然后直接推送origin/master(假设这个期间无其他人推新内容到master)
此时提交状态如下所示
                    (origin/usr2/fix_mem_leak branch)
                               |
C1 <- C2 <- C3 <- C6 <- C7 <- C8
             \                 |
              \         (origin/master branch)
               \
                C4 <- C5
                       |
                (ipv6_support branch)

如果此时usr1希望将ipv6的新特性提交至master, 那么在其直接push origin master时会提示master需要合并分支ipv6_support
虽然C4, C5的改动内容完全独立于C6, C7, C8的改动
但git仍会抓取C5和C8的提交并产生一个新的C9 commit(因两者分支的base不同), 如下图所示
C1 <- C2 <- C3 <- C6 <- C7 <- C8
             \                 \
              \                 \
               \                 \
                C4 <- C5 <------ C9

如果是为了保证master提交记录的"干净完整"
或者是某分支不着急提交, 仍需要更多的测试与开发, 但又不想分支开发周期结束后"偏离"当初checkout的master分支太久远(容易造成更多的冲突)
可以考虑(定期)利用rebase来进行变基
即上面提到过的多人协同开发, 定期rebase master是个好习惯
git checkout ipv6_support
git rebase master
结果提交状态如下所示
            (origin/master origin/usr2/fix_mem_leak branch)
                               |
C1 <- C2 <- C3 <- C6 <- C7 <- C8
                                \
                                 \
                                  \
                                   C4' <- C5'
                                           |
                                    (ipv6_support branch)
这种rebase在功能上类似将某分支所有的改动做成多个patch并依次打在指定的新base上
此时再提交master就不会产生抓取效果, 会将C4'和C5'直接提交至master, 即can be fast-forwarded, 同时也保证了master提交记录的整洁性
(注: 虽然C4'和C5'的内容和C4, C5完全一致, 但两者base不同, commit hash code也完全不同)

git rebase -i HEAD~n 压缩当前分支的n个commit并合并为1个commit, 常见第一行为pick, 剩下的n-1行为squash(注意:可以看修改历史那一章节)

git rebase --abort # rebase过程中发生错误, 可以利用该命令终止整个rebase过程
git rebase --continue # rebase过程中发生冲突, 在解决冲突后可以利用该命令进行后续过程
git rebase -X {theirs/ours} {{branch_name}} # 自动解决冲突,比较危险的操作,除非你知道冲突的是什么内容

修改历史

amend

git commit --amend # 修改最后一次commit的信息,小心使用这个,会改变commit id,如果已经提交到远程仓库上,需要强制重推才能更新远程仓库

rebase

git rebase -i {commit_id} # 使用rebase工具交互式地修改从{commit_id}到HEAD指向区间的历史提交记录
# 运行这个命令后,会弹出一个编辑器,内容大概如下:

##########编辑器内容开始###########################################################
pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick <commit> = use commit(无任何改动)
# r, reword <commit> = use commit, but edit the commit message(仅仅用来修改commit message,提交的内容无改变)
# e, edit <commit> = use commit, but stop for amending(除了可以修改commit message,也可以用来修改内容)
# s, squash <commit> = use commit, but meld into previous commit(压缩多个commit变成一个单独的commit message)
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.(如果直接删除哪行,那么commit message和相应的内容都会丢掉)
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
##########编辑器内容结束#############################################################


# 需要重点注意的是相对于正常使用的 log 命令,这些提交显示的顺序是相反的。 运行一次 log 命令,会看到类似这样的东西:
git log --pretty=format:"%h %s" {command_id}..HEAD
# a5f4a0d added cat-file
# 310154e updated README formatting and added blame
# f7f3f6d changed my name a bit
# 将每一个commit前面的关键字(默认为pick)改成上面提示的那些,就可以完成修改历史commit

++++++++++++++++++++++++++++++++例子1(edit)+++++++++++++++++++++++++++++++++++++++++++++++
# (edit) 例如你在一个commit中修改了一个bug,但是后面测试发现那个bug没有修复彻底,想直接改动那个commit的内容,那么就可以使用edit
# 那么就可以改成
edit f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
# 当保存并退出编辑器时,Git 将你带回到列表中的最后一次提交,把你送回命令行并提示以下信息:

$ git rebase -i {command_id}
Stopped at f7f3f6d... changed my name a bit
You can amend the commit now, with

       git commit --amend

Once you're satisfied with your changes, run

       git rebase --continue
# 这些指令准确地告诉你该做什么。 输入
$ git commit --amend
# 修改提交信息,然后退出编辑器。 然后,运行
$ git rebase --continue
# 这个命令将会自动地应用另外两个提交,然后就完成了。 如果需要将不止一处的 pick 改为 edit,需要在每一个修改为 edit 的提交上重复这些步骤。 每一次,Git 将会停止,让你修正提交,然后继续直到完成。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

++++++++++++++++++++++++++++++++例子2(重新排序提交)+++++++++++++++++++++++++++++++++++++++++++++++
# 也可以使用交互式rebase来重新排序或完全移除提交。 如果想要移除 “added cat-file” 提交然后修改另外两个提交引入的顺序,可以将变基脚本从这样:
pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
# 改成这样
pick 310154e updated README formatting and added blame
pick f7f3f6d changed my name a bit
# 当保存并退出编辑器时,Git 将你的分支带回这些提交的父提交,应用 310154e 然后应用 f7f3f6d,最后停止。 事实修改了那些提交的顺序并完全地移除了 “added cat-file” 提交
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

++++++++++++++++++++++++++++++++例子3(压缩提交)++++++++++++++++++++++++++++++++++++++++++++++++++
# (squash) 通过交互式rebase工具,也可以将一连串提交压缩成一个单独的提交。 在变基信息中脚本给出了有用的指令:
# 如果,指定 “squash” 而不是 “pick” 或 “edit”,Git 将应用两者的修改并合并提交信息在一起。 所以,如果想要这三次提交变为一个提交,可以这样修改脚本:
pick f7f3f6d changed my name a bit
squash 310154e updated README formatting and added blame
squash a5f4a0d added cat-file
# 当保存并退出编辑器时,Git 应用所有的三次修改然后将你放到编辑器中来合并三次提交信息:

# This is a combination of 3 commits.
# The first commit's message is:
changed my name a bit

# This is the 2nd commit message:

updated README formatting and added blame

# This is the 3rd commit message:

added cat-file

# 当你保存之后,你就拥有了一个包含前三次提交的全部变更的提交。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

++++++++++++++++++++++++++++++++例子4(拆分提交)++++++++++++++++++++++++++++++++++++++++++++++++++
# 跟例子1一样使用edit命令,但是这次的应用场景是将一个commit拆成两个commit来提交,拆分一个提交会撤消这个提交,然后多次地部分地暂存与提交直到完成你所需次数的提交。 例如,假设想要拆分三次提交的中间那次提交。 想要将它拆分为两次提交:第一个 “updated README formatting”,第二个 “added blame” 来代替原来的 “updated README formatting and added blame”。 可以通过修改 rebase -i 的脚本来做到这点,将要拆分的提交的指令修改为 “edit”:
pick f7f3f6d changed my name a bit
edit 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
# 然后,当脚本带你进入到命令行时,重置那个提交,拿到被重置的修改,从中创建几次提交。 当保存并退出编辑器时,Git 带你到列表中第一个提交的父提交,应用第一个提交(f7f3f6d), 应用第二个提交(310154e),然后让你进入命令行。 那里,可以通过 git reset HEAD^ 做一次针对那个提交的混合重置,实际上将会撤消那次提交并将修改的文件取消暂存。 现在可以暂存并提交文件直到有几个提交,然后当完成时运行 git rebase --continue:
$ git reset HEAD^
$ git add README
$ git commit -m 'updated README formatting'
$ git add lib/simplegit.rb
$ git commit -m 'added blame'
$ git rebase --continue
# Git 在脚本中应用最后一次提交(a5f4a0d),历史记录看起来像这样:
$ git log -4 --pretty=format:"%h %s"
1c002dd added cat-file
9b29157 added blame
35cfb2b updated README formatting
f3cc40e changed my name a bit

filter-branch

git filter-branch 有很多陷阱,不再推荐使用它来重写历史。 请考虑使用 git-filter-repo,它是一个 Python 脚本,相比大多数使用 filter-branch 的应用来说,它做得要更好。它的文档和源码可访问 https://github.com/newren/git-filter-repo 获取。

# 有另一个历史改写的选项,如果想要通过脚本的方式改写大量提交的话可以使用它——例如,全局修改你的邮箱地址或从每一个提交中移除一个文件。 这个命令是 filter-branch,它可以改写历史中大量的提交,除非你的项目还没有公开并且其他人没有基于要改写的工作的提交做的工作,否则你不应当使用它。 然而,它可以很有用。 你将会学习到几个常用的用途,这样就得到了它适合使用地方的想法。

# 从每一个提交中移除一个文件
# 这经常发生。 有人粗心地通过 git add . 提交了一个巨大的二进制文件,你想要从所有地方删除。 可能偶然地提交了一个包括一个密码的文件,然而你想要开源项目。 filter-branch 是一个可能会用来擦洗整个提交历史的工具。 为了从整个提交历史中移除一个叫做 passwords.txt 的文件,可以使用 --tree-filter 选项给 filter-branch:

git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21)
Ref 'refs/heads/master' was rewritten

# 全局修改邮箱地址
# 另一个常见的情形是在你开始工作时忘记运行 git config 来设置你的名字与邮箱地址, 或者你想要开源一个项目并且修改所有你的工作邮箱地址为你的个人邮箱地址。 任何情形下,你也可以通过 filter-branch 来一次性修改多个提交中的邮箱地址。 需要小心的是只修改你自己的邮箱地址,所以你使用 --commit-filter:

$ git filter-branch --commit-filter '
        if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ];
        then
                GIT_AUTHOR_NAME="Scott Chacon";
                GIT_AUTHOR_EMAIL="schacon@example.com";
                git commit-tree "$@";
        else
                git commit-tree "$@";
        fi' HEAD
# 这会遍历并重写每一个提交来包含你的新邮箱地址。 因为提交包含了它们父提交的 SHA-1 校验和,这个命令会修改你的历史中的每一个提交的 SHA-1 校验和, 而不仅仅只是那些匹配邮箱地址的提交。

git-filter-repo

https://github.com/newren/git-filter-repo

https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html

# 彻底地将文件从历史中删除掉(该删除是再也无法从历史commit中找回,用于删除敏感数据或者无用数据)

1. 使用前,请备份好你的仓库,防止误操作删除了不改删除的文件
2. git-filter-repo --path PATH_TO_DELETE --invert-paths
  2.1 --path会匹配目录和文件名,为了防止删除整个目录,可以加上--use-base-name来仅仅精确匹配文件名
  2.2 如果去掉--invert-paths参数就是保留该目录,刚好是相反的操作
  2.3 以上都是删除某些文件或者保留某些文件的操作,其目录结构仍然会保留原始仓库的结构,但我们需要的是仅保留某个目录下的所有文件,并将其中的内容移动到根目录下。针对这种场景git-filter-repo提供了一个叫做--subdirectory-filter的选项
3. git push origin --force(强制将删除后的结果推上仓库)

# 全局修改用户名和邮箱

1. 在git项目的根目录下创建一个名为.mailmap的文件
2. 在.mailmap文件中写入你要修改的用户名和邮箱的信息
  2.1 mailmap的详细语法请看https://git-scm.com/docs/gitmailmap
  2.2 例子1,如果你仅仅想改用户名,可以写成
      你想改成的名字 <git里面使用的邮箱>
  2.3 例子2,如果你既想改用户名又想改邮箱,那么可以写成
      你想改成的名字 <你想改成的邮箱> <git里面不再想使用的邮箱>
3. git-filter-repo --use-mailmap
  3.1 或者如果文件名不是.mailmap可以使用这个来指定 git-filter-repo --mailmap {mailmap文件名}
4. 防止后面的名字和邮箱,请配置好本地的邮箱和用户名,可以使用vi ~/.gitconfig检查邮箱和用户名有没有错误

打patch (补丁)

##############################################################################
# 打patch(补丁)
##############################################################################
# 生成diff patch文件(git可以识别diff文件)
git <branch> log -n -p > diff.patch # 生成某分支过去n个commit的文件diff信息至单个diff文件
git diff <--cached> diff.patch # 针对当前缓存区的内容生成diff文件

# 利用apply打patch
git apply --check diff.patch    #检查是否可以正常应用, 无回显证明无冲突
git apply --stat diff.patch     #查看应用diff文件后的文件变化
git apply diff.patch            #打patch, 仅仅改变文件信息, 无commit信息, 仍然需要add, commit

# 利用--format-patch生成patch, 带commit信息
git format-patch <branch> -n   #生成分支<branch>最近的n次commit的patch
git format-patch <r1>..<r2>     #生成两个commit间的修改的patch(包含两个commit. <r1>和<r2>都是具体的commit号)
git format-patch -1 <r1>        #生成单个commit的patch
git format-patch <r1>           #生成某commit以来的修改patch(不包含该commit)
git format-patch --root <r1>  #生成从根到r1提交的所有patch

# 利用am打patch
git apply --check 0001-update-bash.sh.patch #检查patch是否冲突可用
git apply --stat 0001-update-bash.sh.patch  #检查patch文件变更情况, 无回显证明无冲突
git am 0001-update-bash.sh.patch            #将该patch打上到当前分支, 带commit信息
git am ./*.patch                            #将当前路径下的所有patch按照先后顺序打上
git am --abort                              #终止整个打patch的过程, 类似rebase --abort
git am --resolved                           #解决冲突后, 可以执行该命令进行后续的patch, 类似rebase --continue

bundle (打包)

##############################################################################
# bundle(打包)
##############################################################################
# 该命令会将git工程打包, 默认情况下会打包所有commit记录和track的文件
# 不同于简单粗暴tar.gz打包整个文件夹, bundle只打包那些push过的记录
# 如某git工程下存在.build构建后的目录, 而.gitignore又忽略了该文件夹
# 如果利用tar.gz打包则会将那些忽略的文件文件夹一并打包, 可能会造成压缩包极大的臃肿
# 而又不想仅仅为了打个包就删除整个build目录(如重新build时间成本太大)
# 那么就可以使用bundle进行打包, 该命令只打包track过的文件
# 并且像url那样直接调用git clone来重建
git bundle create awesome-cheatsheets.bundle HEAD master #打包重建master分支的所有数据
git clone awesome-cheatsheets.bundle # 重建工程

# bundle也可以打包指定的区间, 至于提交区间有多种表示方式
git bundle create awesome-cheatsheets.bundle HEAD~10
git bundle create awesome-cheatsheets.bundle HEAD~10..HEAD
git bundle create awesome-cheatsheets.bundle lhs_commit_md5..rhs_commit_md5
git bundle create awesome-cheatsheets.bundle origin/master..master
git bundle create awesome-cheatsheets.bundle master ^origin/master

配置.gitignore文件

##############################################################################
# 配置.gitignore文件
##############################################################################
/<dir name>/   忽略文件夹
*.zip    忽略.zip文件
/<dir name>/<file name>  忽略指定文件

配置.gitattributes文件

*           -text         # 所有的文件不视为文本,防止git 的autocrlf
*           text=auto     # 对任何文件,设置text=auto,表示文件的行尾自动转换。如果是文本文件,则在文件入Git库时,行尾自动转换为LF。如果已经在入Git库中的文件的行尾为CRLF,则该文件在入Git库时,不再转换为LF。
*.txt  text          # 对于txt文件,标记为文本文件,并进行行尾规范化
*.jpg  -text         # 对于jpg文件,标记为非文本文件,不进行任何的行尾转换
*.vcproj text eol=crlf # 对于vcproj文件,标记为文本文件,在文件入Git库时进行规范化,即行尾为LF。但是在检出到工作目录时,行尾自动转换为CRLF
*.sh  text eol=lf   # 对于sh文件,标记为文本文件,在文件入Git库时进行规范化,即行尾为LF。在检出到工作目录时,行尾也不会转换为CRLF(即保持LF)
*.py  eol=lf        # 对于py文件,只针对工作目录中的文件,行尾为LF

其他配置

##############################################################################
# 配置别名
##############################################################################
git config [--global] alias.<alias> '<original command>' 为所有工作区/当前工作区配置别名
.git/config   当前工作区的配置文件
~/.gitconfig   当前用户的配置文件
##############################################################################
# 配置颜色
##############################################################################
git config --global color.ui true 显示颜色