NOTE:
use git merge-base A base
to find the common ancestor:
- if output is another different commit, A is not merged into base
- if output is base and A is not base, then A is not merged into base, and base haven't changed since A's move.
- if output is A, then A is merged into base.
in conclusion, if merge-base is not A, then A is not merged.
1.find all branches
2.find each branch's merge-base
3.mark the merged flag, when merged, we want to know A's last non-merging commit.
If not merged, always compare with the newest base
If merged, compare with the merge commit, that is, A's last two commits.
List commits of a branch:
master: log --first-parent only merge points are shown
dev branch: log --first-parent only merge or commits directly made to the branch
git最核心的命令,用于遍历git提交
# 展示所有提交
$ git rev-list --all|head -n10
a7e44e9cd88e64cb733443a3c8fd81ec3b180776
f5caa49c52f1f58b9e9b4d992381ea992bd9f863
d45c5e4ddfb708ab66e90da1a1f95592c222647d
cb24751cfc363ebabc78570a7d3fe60bf9c40ad4
788bc23b6381ecb117bacb4aba57144b5dcbefc8
fa3216e06c1ca3e89123b1cb3e5104b05d5a0927
a4df69d564597578a21c13253317d72ea21ba563
97d74965105da0d70ce5e7f3e98870cd07dfcce5
6cb159cf4050bf70da9bb43e68b62653692bf885
$ git rev-list --all|wc -l
3913
添加过滤条件:
遍历所有的引用(分支, tag):
# 显示所有远程分支
$ git for-each-ref --sort=-committerdate refs/remotes|head -n10
6d3b66401f716a7f5bb180c91c3bdcdeff468aa5 commit refs/remotes/origin/release-wy-v0.3
a7e44e9cd88e64cb733443a3c8fd81ec3b180776 commit refs/remotes/origin/dev-1.15.0
f5caa49c52f1f58b9e9b4d992381ea992bd9f863 commit refs/remotes/origin/dev-1.14.0-sg-ui
f5caa49c52f1f58b9e9b4d992381ea992bd9f863 commit refs/remotes/origin/release-1.14.0
d45c5e4ddfb708ab66e90da1a1f95592c222647d commit refs/remotes/origin/1.14.0-csc-mr-use
d45c5e4ddfb708ab66e90da1a1f95592c222647d commit refs/remotes/origin/release-v1.14.0-csc
97d74965105da0d70ce5e7f3e98870cd07dfcce5 commit refs/remotes/origin/HEAD
97d74965105da0d70ce5e7f3e98870cd07dfcce5 commit refs/remotes/origin/master
65938df22c3bedaaf177339b355ccc4e0ddbbb85 commit refs/remotes/origin/v1.14.0-mysspl-from-master_my
014552dbcf040382176783bd1f8ee5d2fafca43a commit refs/remotes/origin/release-v1.13.0
$ git for-each-ref refs/remotes|wc -l
368
使用format:
#lstrip=3表示去掉3个path前缀
$ git for-each-ref --format='%(objectname) %(refname:lstrip=3)' refs/remotes/origin
ba3e2ab996a202c99f814c58384bcd65792c9015 1.11.0-cr-xf
d45c5e4ddfb708ab66e90da1a1f95592c222647d 1.14.0-csc-mr-use
展示一个名称的具体指向,比如master, HEAD:
$ git show-ref master
10062f0be851bae0830768483d7c506c746a43ca refs/heads/master
97d74965105da0d70ce5e7f3e98870cd07dfcce5 refs/remotes/origin/master
$ git show-ref HEAD
97d74965105da0d70ce5e7f3e98870cd07dfcce5 refs/remotes/origin/HEAD
# the following command errors
$ git show-ref HEAD~1
$ git show-ref HEAD{1}
$ git show-ref HEAD@{1}
用来处理git目录相关的操作,如果只有一个参数,就会显示这个分支的CommitID
git rev-parse HEAD # 730fd0265e818fc24bf8455a1d4845431bfa743c
git rev-prase 730fd0265e818fc24bf8455a1d4845431bfa743c # 730fd0265e818fc24bf8455a1d4845431bfa743c
git rev-parse --short HEAD # 730fd02
与文件相关的
git rev-parse --local-env-vars #显示所有GIT_*的变量,仅仅对该工程程序
git rev-parse --is-inside-git-dir # 是否在 .git目录及其自目录下,true|false
git rev-parse --is-inside-work-tree # 是否在仓库中,注意,在仓库中时,与git rev-parse --is-inside-git-dir是互斥的
git rev-parse --git-dir # 获取.git目录的位置,显示$GIT_DIR的值
git rev-parse --is-bare-repository # 是否是bare仓库
git rev-parse --show-toplevel # 在work tree中时,显示仓库的根目录
git rev-parse --show-preifx # 显示当前目录相对于仓库根目录的路径,也就是目录前缀
与commit相关的
git rev-parse --abbrev-ref HEAD #获取当前的分支名,比status更精简
git rev-parse --symbolic-full-name @{u} # 显示完成的ref路径
确定一个ref是否存在:
# --verify 校验只有一个commit, --quiet屏蔽错误,返回0
git rev-parse --verify --quiet origin/masterx
hash=$(git rev-parse --verify --quiet origin/masterx || true)
if [[ -z $hash ]];then ... fi
在git中,rev,或者revision,本质上对应的就是commit。
官方文档介绍:git rev-parse用于很多porcelainish(低级)命令
<rev>
的格式SHA1
refname
refname是指heads/master
, refs/heads/master
这样的引用
尝试规则:refs/<refname>
refs/tags/<refname>
refs/heads/<refname>
refs/remotes/<refname>
refs/remotes/<refname>/HEAD
refname@{n}
前n个,类似于~n
@{u}
, @{upstream}
pull时拉取的远程分支
@{push}
push时推送的远程分支,默认情况下应当与@{u}相同,除非你pull和push的目的地不一样
HEAD^{n}
第i个parent,注意,由于commit可能有多个parent(merge产生)
HEAD~{n}
第i层parent,线性的
$ git config push.default current
$ git config remote.pushdefault myfork
$ git switch -c mybranch origin/master
$ git rev-parse --symbolic-full-name @{upstream}
refs/remotes/origin/master
$ git rev-parse --symbolic-full-name @{push}
refs/remotes/myfork/mybranch
关于^
和~
Here is an illustration, by Jon Loeliger. Both commit nodes B and C are parents of commit node A. Parent commits are ordered left-to-right.
G H I J
\ / \ /
D E F
\ | / \
\ | / |
\|/ |
B C
\ /
\ /
A
A = = A^0
B = A^ = A^1 = A~1
C = A^2
D = A^^ = A^1^1 = A~2
E = B^2 = A^^2
F = B^3 = A^^3
G = A^^^ = A^1^1^1 = A~3
H = D^2 = B^^2 = A^^^2 = A~2^2
I = F^ = B^3^ = A^^3^
J = F^2 = B^3^2 = A^^3^2
展示tree对象的内容,类似于ls -a命令
git ls-tree HEAD~1 #展示当前目录下的HEAD~1对应的类型,log和文件名
git ls-tree HEAD~1 --name-only #只展示文件名
git ls-tree <Obj-Hash> # 错误:不是一个tree对象
git ls-tree HEAD~1 sub # 仅仅展示sub的类型,而不是其子文件夹
git ls-tree -r HEAD~1 sub # 展示sub文件夹的所有子文件,注意这里不会递归展示文件夹,而是把所有文件都枚举出来,因此-r选项只会展出blob
展示文件内容
git ls-files # 递归展示当前目录下的文件,类似于 ls -R
git ls-files --other # 展示未追踪的文件,不包含modified文件,但是包含ignored,untracked的文件
git ls-files --execlude-standard --other # 展示untracked的文件
git ls-files --modified # 展示发生了修改的文件,注意,这里只会展示那些还没有添加到index的修改
git ls-files --with-tree master # 展示master分支的文件
读取snapshot的内容, 如果<ref>
是tree,则展示tree的内容,如果是blob,则展示文件内容
git cat-file -p <ref> # -p: pretty格式化
用于显示与某个commit相近的tag
--follow
显示单个文件的修改
git log --follow -- filename
弥补 git ls-files --modified 的不足
#!/usr/bin/env bash
# git ls-files --modified does not list indexed changes,we should parse manually
# possible status:
# new file: c.go
# deleted: handler/allowlist/handler.go
# modified: main.go
# the following block reads each line of status,and output the file to be commited(new file or modified file)
status=$(git status)
{
state=startFindMsg
while read -d $'\n' line;do
if grep -q -E '^[Cc]hanges to be committed:' ;then
state=foundMsg
elif [[ $state == foundMsg ]];then
state=startFile
elif [[ $state == startFile ]];then
if [[ $line == 'deleted:'* ]];then
continue
fi
# remove prefixed
f=${line##'new file:'}
f=${f##'modified:'}
# if empty,then end the state
if grep -q -E '^\s*$';then
break
fi <<< $f
# trim leading spaces
echo $f
fi <<< $line
done
} <<< $status
https://git-scm.com/docs/gitignore
git可配置忽略文件的几个地方:
.gitignore
.git/info/exclude
变量core.excludesFile(全局或local)指定的文件
例子:
# 通过core.excludesfile指定文件路径
git config --global core.excludesfile ~/.gitignore_global
git show -s
: 显示object的内容
author date: git commit
的时间
commit date: git patch
或者rebase的时间
# %at --- author timestamp
git show -s --format=%at master
# commit message: %s=>subject
git show -s --format=%s
格式参考:http://schacon.github.io/git/git-show
在git中,有两种合并分支的方式:merge和rebase。
本质上,两者都是进行分支的合并,但是有一点不同:merge和rebase的方向时相反的。
merge时,如果产生冲突,HEAD部分是当前分支的,第二部分是另外一个分支的
rebase时,如果产生冲突,HEAD是rebase的目标分支,第二部分是当前分支的某个提交
checkout可接受 --theirs
, --ours
作为某个路径的合并策略
git checkout --theirs path/to/file
git add path/to/file
git rebase --show-current-path 会展示出当前冲突的更改
git show -U25 $commit 会显示出更多的上下文信息
运行git mergetool之后会创建.orig文件,这些文件可以在合并完成之后安全地删除.
设置变量mergetool.keepBackup=false
,则git会在合并之后自动删除文件