使用sed/awk/grep格式化git log输出

4

摘要/ 要点版本,

如果我有一组主题为[SUB]和正文为[BODY]的消息如下,那么如何只在存在[BODY]时在主题后添加一个换行符(并用*替换占位符)。

[SUB] some subject. [BODY] some body lines 
with newline chars and !@@# bunch of other *#@ chars
 without [(BODY)] or [(SUB)]... and more stuff
[SUB] Another subject. with no body [BODY] 
[SUB] another [BODY] some body.

我希望您能将此格式化为

* some subject.

some body lines 
with newline chars and !@@# bunch of other *#@ chars
 without [(BODY)] or [(SUB)]... and more stuff
* Another subject. with no body 
* another 

some body.

我真正想做的事情是:

我正在尝试从git log输出中自动生成我的CHANGELOG.md文件。问题在于,如果提交消息的正文部分不为空,我需要插入换行符。

当前代码如下(分成两行):

git log v0.1.0..v0.1.2 --no-merges --pretty=format:'* %s -- %cn | \
[%h](http://github.com/../../commit/%H) %n%b' | grep -v Minor | grep . >> CHANGELOG.md

并且有一个样例输出:

* Added run information display (0.1.2) -- ... | [f9b1f6c](http://github.com/../../commit/...) 
+ Added runs page to show a list of all the runs and run inforation, include sorting and global filtering.
+ Updated run information display panel on the run-info page
+ Changed the links and their names around.

* Update README.md -- abc | [2a90998](http://github.com/../../commit/...) 

* Update README.md -- xt | [00369bd](http://github.com/../../commit/...) 

在这里,以*开头的行是提交记录,以+开头的行只是第一个提交记录正文的一部分。现在,它会在所有正文段落的开头添加%n(换行符),无论是否为空。我希望仅在非空时添加此内容(可能在删除空格后)。

我该如何实现呢?我的sedawk知识几乎为零,尝试学习也没有多大帮助。

(我将确保正文中的所有代码都缩进,因此不会将提交列表与正文列表混淆)


我的答案

我确信jthills的答案是正确的(甚至可能是更好的方法),但当我试图弄清楚他的意思时,我想出了这个。希望它会对我或未来的某个人有所帮助。

我粘贴了我使用的完整shell脚本:

mv CHANGELOG.md CHANGELOG.md.temp
printf '### Version '$1' \n\n' > CHANGELOG.md
git log $2..$1 --no-merges --pretty=format:'[SUB]%s -- %cn | \
    [%h](http://github.com/<user>/<gitrepo>/commit/%H) [BODY]%b' | grep -v Minor | \
    sed '{:q;N;s/\s*\[BODY\][\n\s]*\[SUB\]/\n\[SUB\]/;b q}' | \
    sed 's/\[SUB\]/* /g' | 
    sed 's/\[BODY\]/\n\n/'>> CHANGELOG.md
cat CHANGELOG.md.temp >> CHANGELOG.md
rm CHANGELOG.md.temp

我基本上是使用临时文件将新的提交日志添加到CHANGELOG.md中。请随意提出这3个 sed 命令的更短版本。


那么您不想在“+ Changed”行后删除空行吗? - Håkon Hægland
那个可以被移除。这并不重要。但是在+*之间我需要添加一个换行符,就像这样。 - xcorat
我对变更日志不太熟悉,但从你的输出来看,+* 之间已经有了一个换行符。 - Håkon Hægland
是的,因为我在正文部分之前加了一个换行符,这样在没有正文的其他行上也会多出一个换行符。我只想要有正文的行有换行符。 - xcorat
谢谢更新,我觉得现在非常清楚了。 - Håkon Hægland
3个回答

5

git log输出中标记您的语法。这将正确处理插入换行符,其余部分您已经知道:

git log --pretty=tformat:'%s%xFF%x01%b%xFF%x02' \
| sed '1h;1!H;$!d;g              # buffer it all (see comments for details)
       s/\xFF\x01\xff\x02//g     # strip null bodies
       s/\xFF\x01/\n/g           # insert extra newline before the rest
       s/\xFF.//g                # cleanup
'

(编辑:引用/纠正错别字)


为什么是\xFF\x01?有特定的原因吗?您介意再添加几行来解释'1h;1!H;$!d;g吗? - xcorat
1
关于标记:它允许在结尾执行s/\xff.//g清理操作。 关于缓冲:对于每一行,根据情况将其放入或附加到保留缓冲区中,如果未到达文件末尾,则不做任何操作,否则获取累积文本以供后续使用。 - jthill

1

对于您问题中的第一个文件,您可以尝试以下操作:

awk -f r.awk input.txt 

其中 input.txt 是输入文件,r.awk 是:

{
    line=line $0 ORS
}

END {
    while (getSub()) {
        getBody()
        print "* " subj
        if (body) {
            print ""
            print body
        }
    }
}

function getBody(ind) {
    ind=index(line,"[SUB]")
    if (ind) {
        body=substr(line,1,ind-1)
        line=substr(line,ind)
    }
    else
        body=line
    sub(/^[[:space:]]*/,"",body)
    sub(/[[:space:]]*$/,"",body)
}

function getSub(ind,ind2) {
    ind=index(line,"[SUB]")
    if (ind) {
        ind=ind+5
        ind2=index(line,"[BODY]")
        subj=substr(line, ind, ind2-ind)
        line=substr(line,ind2+6)
        return 1
    }
    else
        return 0
}

给出输出:

*  some subject. 

some body lines 
with newline chars and !@@# bunch of other *#@ chars
 without [(BODY)] or [(SUB)]... and more stuff
*  Another subject. with no body 
*  another 

some body.

1
我比预期花费更长的时间来解决这个问题,只是尝试通过一些sed调整git消息来格式化/提取我们的JIRA消息的git日志输出。 这是我的解决方案: logsheet = "!f() { git log --format='%h ^ %<(80,trunc)%s ^ A:%<(20,trunc)%an ^ D:%ad ' --no-merges --date=short $1 | sed -e 's/\\([A-Z]*-[0-9]*\\)/\\1 ^/'; }; f" 转义字符、带有!的shell函数都是必需的,因为我有一个参数和一个管道。 :-)

最终得到了这个将制表符分隔的输出写入文件的命令:logsheet = "!f() { git log --format='%h%x09%s%x09A:%an%x09D:%ad' --no-merges --date=short $1 $2 | sed -e 's/\\([A-Z]*-[0-9]*\\)[^A-za-z]*/\\1 /'; }; f" - Drew Deal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接