背景
有时我们需要分析git提交历史,而git log支持–format选项,无法直接输出json,所以需要自己包装下。
git-log文档
实现
本示例使用clojure实现,我们可以看到其简洁、优美之处。
格式化参数可以参考git log文档,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| (defn get-git-log-params [] {:commit "%H" :abbreviated-commit "%h" :tree "%T" :abbreviated-tree "%t" :parent "%P" :abbreviated-parent "%p" :refs "%D" :encoding "%e" :subject "%s" :body "%b" :commit-notes "%N" :author-name "%aN" :author-email "%aE" :author-date "%ai" :author-timestamp "%at" :committer-name "%cN" :committer-email "%cE" :committer-date "%ci" :committer-timestamp "%ct"})
|
格式化方式选择
我们需要格式化为json格式,format支持自定义格式,我们有两种方式可以选择:
format参数直接包装为json格式
此种方式每个commit之后需要加个空格,最末尾的需要去除。
优点:直接得到json
缺点:某些字段内容包含引号,转义问题不容易解决
利用特殊分隔字符
我们将每个输出参数分隔,再将每个commit输出分隔,拿到输出后进行解析。
优点:不用考虑转义问题
缺点:实现略微复杂
不过我们利用clojure可以很方便实现。
需要用的小知识点
1 2 3 4 5 6 7 8 9 10 11
| user=> (clojure.string/split "a#&b#&c" (re-pattern "#&")) ["a" "b" "c"]
user=> (into {} (mapv vector [:a :b :c] [1 2 3])) {:a 1, :b 2, :c 3}
user=> (apply mapv vector (seq *1)) [[:a :b :c] [1 2 3]]
|
实现步骤
定义分割符
1 2
| (def magic-item "&=&=&=&=&=&=") (def magic-line "#@#@#@#@#@#@")
|
拆分参数并构造命令
1 2 3 4 5
| (let [[ks vs] (apply mapv vector (seq (get-git-log-params)))] (-> (clojure.string/join magic-item vs) (str ,,, magic-line) (#(format "git log remotes/origin/%s --format='%s'" branch-name %) ,,,)))
|
执行shell命令
1 2 3 4
| user=> (require '[clojure.java.shell :as shell]) nil user=> (->> (shell/sh "bash" "-c" "date") :out) "Fri Dec 14 08:28:00 CST 2018\n"
|
这里使用bash -c是个小技巧,后面的命令可以可以放在一个字符串中处理。
解析结果
假设我们完成了上述步骤,将会得到如下格式的输出:
1
| aaa&=&=&=&=&=&=bbb&=&=&=&=&=&=ccc#@#@#@#@#@#@123&=&=&=&=&=&=456&=&=&=&=&=&=789\n
|
我们需要按照如下步骤解析:
- 删除末尾换行
- 按照行分隔符切分
- 每行再按照列分隔符切分
- 组合key和切分后的内容
具体实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| (->> (clojure.java.shell/sh "bash" "-c" (-> (clojure.string/join magic-item vs) (str ,,, magic-line) (#(format "git log remotes/origin/%s --format='%s'" branch %) ,,,)) :dir code-path) :out clojure.string/trim-newline (#(clojure.string/split % (re-pattern magic-line)) ,,,) (map #(clojure.string/split % (re-pattern magic-item)) ,,,) (map #(into {} (mapv vector ks %)) ,,,) (cheshire.core/generate-string ,,,))
|
我们将默认的map改用pmap,即可多核并行处理,clojure就是如此简单。
完整实现
见 https://github.com/rainmote/blog-example/blob/master/git-log.clj