Using Git The Hard Way

本来想起个名字叫“跟 Git 的100件小事”,后来觉得太矫情了,于是改成了“笨办法用 Git”。

最近在公司做的项目有 git 使用的需求,于是钻研了一下,现在想着把这些东西整理一下写篇博客来加深记忆。

提取 Jira Ticket

关于创业公司有一个笑话是说:“Your startup either dies, or it lives long enough to use JIRA.”。不得不说,Jira 是个挺不错的项目管理系统,虽然里面各种 feature 太错综复杂,但对于 track 一个大的 team 的工作而言,已经做的非常好了。

需求陈述

需要从浩瀚的 git commit 中去提取某一段时间完成的 Jira ticekts,因为 Jira 的 filter 目前还无法满足我们的一些特殊查询需要。

一般使用 Jira 的公司的 convention 是通过在提交或者分支创建的时候加上 Jira 的 ticket 号来 track 工作,例如 d_OPP-12345_a_specific_feature。下面我就以分支上填写 Jira ticket号来分析如何从 commit info 去提取 Jira ticket,亦即从 git commit info 中去提取出 OPP-12345

分析 git commit info

首先我们分析一下常见的 commit info。(感觉分析好长啊,不太想写…)

一般而言对于一个大的 Team,我们无法控制每个人撰写 git commit message 的格式,所以我们能利用的其实只有 git 本身提供的一些 merge info 来进行信息提取。而对于真正进入我们 production base 的代码(我们以 dev 分支为例),往往都会有这么一条记录: Merge branch 'd_OPP-12345_a_specific_feature' into 'dev'。显然这条信息对于我们十分关键:

  • 提供了关于 Jira ticket 号的信息
  • 只需要过滤这些跟 merge 直接相关的 commit,减少了数据规模,同时筛掉了无意义分支内的 commit info

于是事情就变成了从

log --merges ```里去提取我们需要的 ticket 信息。
1
2
3
4
5
6
#### 获取提交 ticket 信息
首先我们需要获取两次 commit 之间的所有提交:
``` bash
filter_rows=( $(git -C $code_path log $branch --pretty='%h, %an, %ai, %s' --merges | nl | grep -E "$commit_start|$commit_end" | cut -f1) )

然后我们就可以批量进行 ticket 的提取工作:

1
filter_tickets=( $(git -C $code_path log $branch --pretty='%h, %an, %ai, %s' --merges | nl | awk 'NR >= v1 && NR <= v2' v1="${filter_rows[0]}" v2="${filter_rows[1]}" | grep -o "Merge branch .* into .*$branch" | grep -ioE "OPP(_|-)[1-9]{1}[0-9]+" | tr a-z A-Z | awk '/OPP/{gsub("_","-"); print $0}' | sort | uniq | xargs | tr ' ' ',') )

至此工作完成了一半,因为往往我们还需要和 Jira 系统做个集成,通过 filter 出来的这些 ticket 信息去获取一些别的字段的内容,但这就不是本文的核心了,在此直接略过。感兴趣的朋友可以自己研究一下用 node 写一个抓取 jira 内容的脚本与本文的内容提示做个集成即可。


确认某次 commit 是否存在于某个 branch

这个问题其实我困惑很久了,找了很多 StackOverflow 的答案也不太尽如人意。于是想着自己撸一个吧。刚好做的项目里存在一个需求是说确认某一次 hotfix 的提交都被 merge 到主分支 master 了。(显然这里的需求被我抽象成可以对外发布的版本了 😌)

获取提交时间戳

为了尽可能准确地保证某段时间内的提交都被 merge 到了主分支上,我们需要获取准确的提交时间戳(其实这事儿做完了之后我自己的想法是 git commit sha1 已经足够准确,我可能有点钻牛角尖了…)。我自己封装了一个函数来做这个事情。

1
2
3
4
5
6
7
get_commit_timestamp()
{
# $1 should treat as code_repo_path
# $2 should treat as code branch
# $3 should treat as commit sha1
git -C $1 log $2 --oneline --pretty="%h %ai" | grep $3 | awk '{$1=""; print $0}'
}

一些问题的解答

这里收集了一些我自己对于 git 使用过程中的问题的总结。

Fast Forward 是何方神圣?

一些常用 Git 命令

从上面的需求实现里总结了一下有些有用的小命令以飨(xiǎng)读者。也许上面的内容并不适合你,但下面这些命令里,总有一个能让你在日常的 coding 中用到。

不切换路径查询 git log

git -C targeted/code/repo/path log

不切换分支查询 git log

git log branch_name

这个很有用,可以在 master 分支的时候去查询 dev 分支上的 code 变化情况。

临时rollback某个文件

git checkout – /path/to/file

上述文件使用完毕测试完毕,恢复回最新版本

git reset HEAD /path/to/file

git checkout /path/to/file