当前位置: 首页 > 工具软件 > go-parse > 使用案例 >

git命令(rev-list, rev-parse, log)

闻人高卓
2023-12-01

merge-base

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

rev-list

git最核心的命令,用于遍历git提交

# 展示所有提交
$ git rev-list --all|head -n10
a7e44e9cd88e64cb733443a3c8fd81ec3b180776
f5caa49c52f1f58b9e9b4d992381ea992bd9f863
d45c5e4ddfb708ab66e90da1a1f95592c222647d
cb24751cfc363ebabc78570a7d3fe60bf9c40ad4
788bc23b6381ecb117bacb4aba57144b5dcbefc8
fa3216e06c1ca3e89123b1cb3e5104b05d5a0927
a4df69d564597578a21c13253317d72ea21ba563
97d74965105da0d70ce5e7f3e98870cd07dfcce5
6cb159cf4050bf70da9bb43e68b62653692bf885

$ git rev-list --all|wc -l
    3913

添加过滤条件:

for-each-ref

遍历所有的引用(分支, 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

show-ref

展示一个名称的具体指向,比如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}

rev-parse

用来处理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

git ls-tree

展示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

展示文件内容

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分支的文件

git cat-file

读取snapshot的内容, 如果<ref>是tree,则展示tree的内容,如果是blob,则展示文件内容

git cat-file -p <ref> # -p: pretty格式化

git describe

用于显示与某个commit相近的tag

展示文件的修改

--follow显示单个文件的修改

git log --follow -- filename

我的脚本:git-ls-changes

弥补 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

ignore

https://git-scm.com/docs/gitignore
git可配置忽略文件的几个地方:
.gitignore
.git/info/exclude
变量core.excludesFile(全局或local)指定的文件

例子:

# 通过core.excludesfile指定文件路径
git config --global core.excludesfile ~/.gitignore_global   

how to change all commit author and email

git apply

git patch

git am

git format-path

git show -s

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

merge

在git中,有两种合并分支的方式:merge和rebase。
本质上,两者都是进行分支的合并,但是有一点不同:merge和rebase的方向时相反的。

merge时,如果产生冲突,HEAD部分是当前分支的,第二部分是另外一个分支的

rebase时,如果产生冲突,HEAD是rebase的目标分支,第二部分是当前分支的某个提交

checkout --theirs 合并

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

运行git mergetool之后会创建.orig文件,这些文件可以在合并完成之后安全地删除.
设置变量mergetool.keepBackup=false,则git会在合并之后自动删除文件

git rebase workflow

Git Workflow for Feature Branches

 类似资料: