《Git权威指南》读书笔记
常用设置
git config --global core.quotepath false # 若 git status 不能正确显示中文文件名
git config --global user.name USERNAME # --global 对当前用户有效
git config --global user.email MAIL@HOST # 若不加 --system 和 --global,只对当前项目生效
git config --system color.ui true # 颜色高亮显示,--system 对所有用户有效
git config --global merge.tool vimdiff # 设置图形化合并工具
git config receive.denyNonFastForwards true # 当前项目禁止非快进提交,提交时 ParentId 必须是上游的最新提交
git clone git@gitee.com:空间名/项目名.git # gitee 项目使用 ssh 协议访问方式
在git命令中指定版本库位置
git --git-dir=/path/to/repo config user.name USER2
git status
git status -s # 简洁显示
git add
git add -u # 添加已跟踪文件中的更新
git add -A # 添加本地变化的文件(新增、删除、更新)
git add -i # 交互式添加
删除文件 git rm
git rm <paths> # 删除工作区的<paths>,并将该操作add到暂存区
git rm --cached <paths> # 只删除暂存区的<paths>,保存工作区的<paths>
git diff
工作区-- git diff -->暂存区-- git diff --cached -->版本库
└── git diff HEAD ──┘
git diff --word-diff # 单词级别比较
git diff A B # 比较 tag A 和 B
git diff A # 比较工作区和 A
git diff # 比较工作区和暂存区
git diff --cached A # 比较暂存区和A
git diff --cached # 比较暂存区和HEAD
git diff HEAD # 比较工作区和HEAD
git diff <commit1> <commit2> -- <paths> # 比较两个提交中的指定文件
git diff <path1> <path2> # git diff 也可以对非 Git 目录/文件进行比较
git log
git log --stat # 显示文件变动统计
git log --decorate # 显示 tag 和分支信息
git log -<n> # 最近 n 条提交
git log B..D # 显示 D 的祖先提交,把 B 及其祖先排除在外
git log B...D # 显示 B 和 D 的祖先提交,把 B 和 D 都能访问的排除在外
git log -p -1 # 显示更改信息(patch)
git log -p -m -1 # 在 -p 或 --stat 的基础上,-m 显示合并提交的详细信息
git log --pretty=raw # 显示提交的原始数据
git log --pretty=oneline # 以单行显示提交历史
列出版本库目录树
git ls-tree -l HEAD
git ls-files --with-tree=HEAD^
列出暂存区目录树
#无法显示文件大小
git ls-files -s
#也可以用以下命令先得到目录树ID
git write-tree
git ls-tree -l 目录树ID
#将上面两行命令整合
git write-tree | xargs git ls-tree -l -r -t
清除本地未跟踪文件
# -f 强制删除 -d 包含子目录 -n 模拟操作
git clean -fdn
git cat-file
git cat-file -t <引用> #显示引用的对象类型
git cat-file -p <引用> #显示引用的具体内容
#不用 -p 选项也可以显示内容
git cat-file commit HEAD
git cat-file blob HEAD:readme.txt
git cat-file tree HEAD:{tree}
计算HASH
(printf "commit 234\0";git cat-file commit HEAD) | sha1sum
对象指代方法
HEAD^ #父对象
HEAD^2 # 第2个父对象
HEAD~n # 第 n 级父对象
HASH^{tree} # 对应的树对象
HASH:path/to/file # 提交中的文件
:path/to/file # 暂存区中的文件
取得HASH
git rev-parse 引用名称
显示版本历史
git reflog show master
cat .git/logs/refs/heads/master
git reset
若处于头指针分离状态,则只移动HEAD;若HEAD指向分支,则同时移动HEAD和分支。
#使用 commit 提交中的 paths 替换暂存区中的同名文件,不改变工作区
git reset [commit] [--] paths # 如未指定 commit,则使用 HEAD
git reset --soft <commit> #执行1,用于重新提交 1、改变HEAD指向
git reset --mixed <commit> #执行1、2 2、替换暂存区
git reset --hard <commit> #执行1、2、3 3、替换工作区
git checkout
checkout 只移动HEAD,若目标为 commit,则导致头指针分离;若目标为分支,则使HEAD指向(refer)分支
#用指定提交中的文件覆盖暂存区、工作区,如省略 commit,则用暂存区文件覆盖工作区
git checkout [commit] [--] paths
#检出分支,并重置暂存区、工作区
git checkout <branch>
#新建并检出到分支
git checkout -b <new_branch> <start_point>
git stash 快照
git stash [save "message..."] # 建立快照
git stash list # 列出快照
git stash pop --index [<stashID>] #恢复快照并删除快照,--index 选项同步恢复暂存区
git stash apply [<stashID>] #恢复快照并保留快照
git stash drop [<stashID>] #删除快照
git stash clear #清除快照
git reflog show refs/stash #查看 stash 历史
#查看 stash 保存内容,stash@{1}为 reflog 格式的引用
git log -3 --graph --pretty=raw stash@{1}
.gitignore 文件忽略
/path # 强调只忽略本文件夹,子文件夹中的不忽略
path/ # 忽略此文件夹下的全部文件
!file # 取消对 file 的忽略,强制关注
git status --ignored # 显示被忽略的文件
git add -f paths # 强制添加被忽略的文件到暂存区
git config --global core.excludesfile /home/.gitignore # 设置全局 .gitignore 文件
文件归档 git archive
git archive -o latest.zip HEAD
图形版本历史浏览 gitk
gitk --since="2 weeks ago" & # 使用&可以在启动gitk后不挂起终端
gitk v2.6.. include/scsi drivers/scsi # 显示某tag以来,针对特定目录、文件的提交
显示详细信息 git rev-parse
git rev-parse --symbolic --tags # 列出 tags
git rev-parse --symbolic --glob=refs/* # 列出所有引用
git rev-parse TAG^0 TAG^{} TAG^{commit} # 显示 TAG 指向的提交HASH
git rev-parse TAG^{tree} TAG: # 显示 TAG 指向提交的目录树ID
git rev-parse TAG^{tree}:path/to/file TAG:path/to/file # 显示 TAG 指向提交的目录树中的文件ID
git rev-parse :path/to/file # 显示暂存区文件ID
git rev-parse :/"keyword" # 在提交日志中查找
列出所有历史提交 git rev-list
git rev-list TAG # 不附加选项,只列出提交ID
git rev-list --oneline A B # 列出 A、B 的并集
git rev-list --oneline ^B D # 并集,排除 B 及其历史版本
git rev-list --oneline B..D # 等价于 ^B D
逐行追溯 git blame
git blame -L <linenum>,<+n> <paths> # 逐行显示指定文件的修改来源,指定行范围
正则查找行 git grep
git grep -n <keyword> # 搜索当前目录下的文件,显示与正则表达式匹配的行。-n 显示行号
检出文件的历史版本到新文件
git show <commit>:<file> >> newfile
修补提交
# 修改上次提交的文件或备注
git commit --amend
# 合并最近多次提交
git reset --soft HEAD^^
git commit
git cherry-pick 检取提交到当前分支
git cherry-pick <commit> # 执行 cherry-pick 时要求当前工作树无修改(clean)
git cherry-pick <commitA>..<commitB> # 检取 <commitA>..<commitB> 到当前分支,注意不包括 <commitA> 本身
git rebase 变基
git rebase [--onto <newbase>] [<upstream> [<branch>]]
如果指定了 <branch>,则先执行 git switch <branch>,检出为当前分支。作用范围是当前分支(旁枝)中所有提交,且未包含在 <upstream> 分支(主枝)中的部分。如果 <upstream> 和 <branch> 是同一分支上的提交,则作用范围是 <upstream>..<branch>(排除 <upstream> 及其历史提交)。
范围内的提交最后将检出到 <upstream> 分支。
git rebase master dev # 将 dev 分支上的新提交转捡出到 master 分支
# 下面两行操作与上条等价
git checkout dev # 先检出至要移动的分支
git rebase master # 将未包含在 master 分支中中的提交移动至 master 分支
git rebase -i # 交互式变基
git rebase <since>
git rebase <since> <till>
git rebase --onto <newbase> <since>
git rebase --onto <newbase> <since> <till>
如果
git checkout master
git reset --hard HEAD@{1}
丢弃历史,新建根提交 hash-object
git cat-file commit TAG_A^0 > tmp # 以 TAG_A 新建根提交,A 以前的提交将被丢弃
vim tmp # 编辑 tmp,将其中 parent 一行删去
git hash-object -t commit -w -- tmp # 创建新的根提交,得到 HASH_ID
git rebase --onto HASH_ID A master # 将 A 以后(不含A)的提交检出到 A' 上
反转提交 git revert
git revert <commit>
克隆库 git clone
git clone <repository> <directory> # A->B,只能在B端pull,不能在A端push
# 裸版本库,习惯以 .git 结尾,A->B,可以在A端push
git clone --bare <repository> <directory.git>
git clone --mirror <repository> <directory.git>
git init --bare repos.git # 也可以在初始化裸版本库
查看上游版本库注册信息 git remote
git remote -v # 注册信息保存在 .git/config 中
显示远程版本库的引用 git ls-remote
git ls-remote origin
git ls-remote origin my* # 过滤
清理版本库 git gc
git reflog expire --expire=now --all # 将所有 reflog 置为过期
git fsck # 检查未被关联的松散对象
git gc --prune=now # 清理未关联对象
du -sh <目录名> # 显示目录占用空间
分支合并
git merge <commit> # 将 <commit> 合并到当前分支,当前分支为第一父提交,<commit>为第二父提交
git merge --no-ff <commit> # 禁止快进式合并,各分支保持各自的主线
# 自动拉取上游更新并合并到本地,git pull = git fetch + git merge
git pull
# 手工与上游代码合并
git fetch # 取得上游更新
git merge origin/master # 将上游更新合并到本地
git merge refs/remotes/origin/master # 与上一行等效
冲突解决
- 查看可能引发冲突的提交
git rev-list master...dev1 # 排除 master、dev1 两分支共有的历史提交
- 冲突解决可以手工编辑冲突文件,删除其中的冲突标志即可;也可以使用工具解决:
git mergetool # 打开合并工具;也可以手工编辑冲突文件,删除冲突标志
git add -u # 解决冲突后需要将结果添加到暂存区
git commit # 提交后即可完成冲突解决
- 解决冲突时可用
git status查看冲突状态,也可用git ls-files -s列出冲突文件及所在暂存区,可用使用git show :[1|2|3]:path/to/file查看暂存区中的文件 - 合并成功仍然可能存在逻辑冲突,如一个分支中修改了文件名,而另一分支中仍然在代码中引用原文件名;或一个分支中修改了函数返回值的含义,另一分支中未做相应改变。好的解决方法是引入单元测试及自动化集成。
里程碑 tag
列出里程碑
git tag # 列出现在里程碑
git tag -n<num> # 最多显示 <num> 行里程碑说明
git tag -l V2* # 使用通配符过滤 tag 名称
创建里程碑
git tag <tagname> [<commit>] # 轻量tag,直接指向commit,不建立tag对象,无法被 describe 命令直接引用
git tag -m <msg> <tagname> [<commit>] # 标准tag,带注释信息
git tag -s <tagname> [<commit>] # 带GPG签名的tag
普通tag(非轻量)属于tag对象,指向一个提交。在git log命令中,可直接使用tag名称指代相对应的提交,其他场合如需取得tag对象指向的提交,可使用 TAG_A^{} TAG_A^{commit} TAG_A^0
删除里程碑
git tag -d <TAG_NAME>
tag无法重命名,只能先删除,记下对应的 commitID 后立即对其重建新命名的 tag,或使用 -f 参数新建 tag,覆盖原有的同名 tag。
推送里程碑
执行 git push 不会向上游推送 tag,必须显式推送指定 tag
git push origin tagName # 指定 tag 名称
git push origin refs/tags/* # 使用通配符指定多个 tag
git ls-remote origin my* #查看上游版本库中的 tag
拉取里程碑
git pull可直接拉取到上游新增的 tag,但如果拉取到的 tag,其后被他人更改并重新推送到上游,那么再次 pull 无法取得更新后的 tag,除非显式地拉取指定 tag
git pull origin refs/tags/mytag:refs/tags/mytag # 冒号前后分别为本地和远程tag
删除远程 tag
git push origin :mytag # 用空值覆盖远程 tag
命名规则
tag 命名时不要以 - 开头(与选项区分),不要以 . 开头或结尾,不要使用易与 git 命令混淆的符号,如 ~、^、:、?、*、空格 等。
版本描述 git describe、name-rev
git describe [<commit>] # 显示最近的一个 tab 作为版本描述
git name-rev <commit> # 与 describe 类似,为 <commit> 显示一个名称,优先使用分支名称,除非显示使用 --tags
git log --pretty=oneline origin/master | git name-rev --tags --stdin # name-rev 可以接受标准输入
分支管理
常用命令
git branch # 显示本地分支
git branch -r # 显示远程分支
git branch <branchName> # 基于HEAD创建分支,实质是在 .git/refs/heads 下创建引用
git branch <branchName> <commit> # 基于指定提交创建分支
git branch -d <branchName> # 删除分支,若未合并到其他分支则拒绝删除
git branch -m <oldBranch> <newBrandn> # 重命名分支
git checkout -b <newBranch> [<commit>] # 创建并切换到新分支
分支合并
将 bugfix 分支合并到 dev 分支
git checkout dev # 切换到 dev 分支
git merge --no-ff bugfix # 合并bugfix分支,禁止快进合并,保持各自主线
git tag -m "v1.1 bugfix" v1.1 # 合并后最好打个 tag
查看远程分支
git status # 可显示本地分支与远程分支的跟踪关系(如果已关联好的话)
git cherry # 显示本地尚未推送到上游跟踪分支的提交
git ls-remote --head <url> # 显示上游版本库中的引用,--head 只显示分支
git show-ref # 与上一行相对应,显示本地的全部引用
推送本地分支到上游
git push origin <本地分支>:[<远程分支>] # 将本地分支推送到远程**指定分支**
git push origin hello-1.x # 将本地分支推送到远程的**同名分支**
git push # 将本地**当前分支**推送到**同名的远程分支**上(需先关联远程分支)
git push --set-upstream origin <本地分支名> # 关联本地分支与远程同名分支
git push -u origin <本地分支名> # 与上行等效的短参数
获取远程分支 git fetch
git fetch [<远程版本库引用名>]
# 回显:
# *[new branch] hello-1.2 -> origin/hello-1.2
本地引用 origin/hello-1.2 称为远程分支(全称为 refs/remotes/origin/hello-1.2),不能直接检出,需要先基于该远程分支创建本地分支:
git checkout -b hello-1.2 origin/hello-1.2 # 本地分支创建后将自动关联远程分支
# git 1.6.6 以后版本可以使用下面命令创建本地分支
git checkout hello-1.2
远程分支的跟踪信息保存在 config 文件中
检取远程分支
git pull origin <远程分支名>:<本地分支名> # 将**远程指定分支**拉取到**本地指定分支**上
git pull origin <远程分支名> # 将远程指定分支拉取到本地**当前分支**上
git pull # 将与本地**当前分支**同名的远程分支拉取到本地当前分支上(需先关联远程分支,只需关联一次)
删除远程分支
git push origin :dev # 用空值覆盖远程分支即可删除
远程版本库管理 git remote
git remote add <ref_name> <url> # 注册远程版本库到 config 中,ref_name 默认为 origin
git remote -v # 显示已注册的远程版本库
git remote rename <old_ref_name> <new_ref_name> # 更改已注册的远程版本库注册名
git remote update # 获取所有远程版本库的更新
git remote rm <ref_name> # 删除注册的远程版本库
补丁交互 patch
git format-patch -s HEAD~3..HEAD # 创建补丁文件,自动编号,-s 参数为保留作者姓名、邮箱信息
git send-email *.patch # 以邮件正文的形式逐条发送补丁文件
# git send-email 可以预先配置以下参数(以QQ邮箱为例)
git config --global sendemail.smtpencryption ssl
git config --global sendemail.smtpserver smtp.qq.com
git config --global sendemail.smtpuser ******@qq.com
git config --global sendemail.smtpserverport 465
git config --global sendemail.smtppass ***
# 使用 linux 的邮件客户端收取补丁
$ mail # 进入 mail 环境,提示符变成 &
& s 1-3 user1-mail-archive # 将 1-3 号邮件合并保存为 mbox 格式文件
& q
# am 的意思是 apply email,应用补丁并提交
git am user1-mail-archive # 将 mbox 文件中的补丁全部应用在当前分支
# 也可以通过U盘或邮件附件复制 patch 文件
cat *.patch | git am # 或者以管道形式应用 patch 文件

浙公网安备 33010602011771号