像 Subversion 中的那些 Git 关键字替换?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11534655/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 14:09:06  来源:igfitidea点击:

Git keyword substitution like those in Subversion?

gitsvnversion-controlversion-control-keywords

提问by Barmaley Red Star

I used to work under Subversion/SVN and was instantly using nice feature called keyword substitution. Just putting in source files smth like:

我曾经在 Subversion/SVN 下工作,并且立即使用了称为关键字替换的好功能。只需放入源文件,例如:

/*
 *   $Author: ivanovpv $
 *   $Rev: 42 $
 *   $LastChangedDate: 2012-05-25 21:47:42 +0200 (Fri, 25 May 2012) $
 */

And each time Subversion was substituting keywords (Author, Rev, LastChangedDate) with actual ones.

并且每次 Subversion 都用实际的关键字替换关键字(作者、修订版、LastChangedDate)。

Some time ago I was forced to move to Git and just wondering is there's something similar to Subversion's keyword substitution in Git?

前段时间我被迫转向 Git,只是想知道 Git 中是否有类似于 Subversion 的关键字替换的东西?

采纳答案by ShellFish

Solution

解决方案

Well, you could easily implement such a feature yourself.

好吧,您可以自己轻松实现这样的功能。

Basically I embedded the commit command into a shell script. This script will first substitute the desired macros and then commit the changes. The project consists of two files:

基本上我将提交命令嵌入到 shell 脚本中。此脚本将首先替换所需的宏,然后提交更改。该项目由两个文件组成:

Content?

内容?

keysub, a bash shell script and keysub.awkan awk script to replace keywords in a specific file. A third file is a config file which contains the values that should be substituted (besides variable stuff like commit count and timestamp).

keysub,一个 bash shell 脚本和keysub.awk一个 awk 脚本来替换特定文件中的关键字。第三个文件是一个配置文件,其中包含应该替换的值(除了提交计数和时间戳等变量之外)。

How do you use it?

你如何使用它?

You call keysubinstead of commit with the same options. The -mor -aoption should come before any other commit option. A new option (that should always come first) is -fwhich takes a config file as a value. Example:

keysub使用相同的选项调用而不是提交。该-m-a选项应该出现在任何其他提交选项。一个新选项(应该始终放在首位)是-f将配置文件作为值。例子:

$ git add 'someJavaFile.java'
$ keysub -m 'fixed concurrent thread issue'
$ git push

or

或者

$ git -f .myfile.cnf -m 'enhanced javadoc entries'

keysub

钥匙扣

#!/bin/bash

# 0 -- functions/methods
#########################
# <Function description>
function get_timestamp () {
  date    # change this to get a custom timestamp
}

# 1 -- Variable declarations
#############################
# input file for mapping
file=".keysub.cnf"
timestamp=$(get_timestamp)


# 2 -- Argument parsing and flag checks
########################################

# Parsing flag-list
while getopts ":f:m:a" opt;
do
  case $opt in
    f) file=${OPTARG}
       ;;
    a) echo 'Warning, keyword substitution will be incomplete when invoked'
       echo 'with the -a flag. The commit message will not be substituted into'
       echo 'source files. Use -m "message" for full substitutions.'
       echo -e 'Would you like to continue [y/n]? \c'
       read answer
       [[ ${answer} =~ [Yy] ]] || exit 3
       unset answer
       type="commit_a"
       break
       ;;
    m) type="commit_m"
       commitmsg=${OPTARG}
       break
       ;;
   \?) break
       ;;
  esac
done
shift $(($OPTIND - 1))

# check file for typing
if [[ ! -f ${file} ]]
then
  echo 'No valid config file found.'
  exit 1
fi

# check if commit type was supplied
if [[ -z ${type} ]]
then
  echo 'No commit parameters/flags supplied...'
  exit 2
fi

# 3 -- write config file
#########################
sed "
  /timestamp:/ {
    s/\(timestamp:\).*/${timestamp}/
  }
  /commitmsg:/ {
    s/\(commitmsg:\).*/${commitmsg:-default commit message}/
  }
" ${file} > tmp

mv tmp ${file}

# 4 -- get remaining tags
##########################
author=$(grep 'author' ${file} | cut -f1 -d':' --complement)


# 5 -- get files ready to commit
#################################
git status -s | grep '^[MARCU]' | cut -c1-3 --complement > tmplist

# 6 -- invoke awk and perform substitution
###########################################
# beware to change path to your location of the awk script
for item in $(cat tmplist)
do
  echo ${item}
  awk -v "commitmsg=${commitmsg}" -v "author=${author}" \
      -v "timestamp=${timestamp}" -f "${HOME}/lib/awk/keysub.awk" ${item} \
      > tmpfile
  mv tmpfile ${item}
done
rm tmplist

# 5 -- invoke git commit
#########################
case ${type} in
  "commit_m") git commit -m "${commitmsg}" "$@"
              ;;
  "commit_a") git commit -a "$@"
              ;;
esac

# exit using success code
exit 0

keysub.awk

密钥子.awk

# 0 BEGIN
##########
BEGIN {
  FS=":"
  OFS=": "
}

# 1 parse source files 
########################
# update author
author:ubunut-420
timestamp:Fri Jun 21 20:42:54 CEST 2013
commitmsg:default commit message
~ /.*$Author.*$.*/ { =author " $" } # update timestamp
#! /usr/bin/env ruby
data = STDIN.read
last_date = `git log --pretty=format:"%ad" -1`
puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')
~ /.*$LastChangedDate.*$.*/ {
$ git config filter.dater.smudge expand_date
$ git config filter.dater.clean 'perl -pe "s/\$Date[^\$]*\$/\$Date\$/"'
= =timestamp " $" } # update commit message
date*.txt filter=dater
$ echo '# $Date$' > date_test.txt If you commit
~ /.*$LastChangeMessage.*$.*/ { =commitmsg " $" } # update commit counts
$ git add date_test.txt .gitattributes
$ git commit -m "Testing date expansion in Git"
$ rm date_test.txt
$ git checkout date_test.txt
$ cat date_test.txt
# $Date: Tue Apr 21 07:26:52 2009 -0700$
~ /.*$Rev.*$.*/ { ++ = " $" } # print line { print }

Config file

配置文件

#! /usr/bin/env ruby
data = STDIN.read
last_date = `git log --pretty=format:"%ad" -1`
puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')

Remarks

评论

I've tried to document well enough so you can easily implement it and modify it to your own, personal needs. Note that you can give the macros any name you want to, as long as you modify it in the source code. I also aimed to keep it relatively easy to extend the script, you should be able to add new macros fairly easily. If you're interested in extending or modifying the script, you might want to take a look at the .git directory too, there should be plenty of info there that can help to enhance the script, due to lack of time I didn't investigate the folder though.

我已经尝试记录得足够好,以便您可以轻松地实施它并根据您自己的个人需求对其进行修改。请注意,您可以为宏指定任何您想要的名称,只要您在源代码中对其进行修改即可。我还旨在使扩展脚本相对容易,您应该能够相当容易地添加新的宏。如果您对扩展或修改脚本感兴趣,您可能还想查看 .git 目录,那里应该有很多信息可以帮助增强脚本,因为我没有时间调查文件夹虽然。

回答by Marcin Koziński

Git doesn't ship with this functionality out of the box. However, there is a chapter in the Git Book on Customizing Gitand one of the examples is how to use git attributes to implement a similar result.

Git 不附带此功能。但是,Git Book 中有一章关于自定义 Git,其中一个示例是如何使用 git 属性来实现类似的结果。

It turns out that you can write your own filters for doing substitutions in files on commit/checkout. These are called “clean” and “smudge” filters. In the .gitattributesfile, you can set a filter for particular paths and then set up scripts that will process files just before they're checked out (“smudge”) and just before they're staged (“clean”). These filters can be set to do all sorts of fun things.

事实证明,您可以编写自己的过滤器,以便在提交/签出时在文件中进行替换。这些被称为“清洁”和“污迹”过滤器。在.gitattributes文件中,您可以为特定路径设置过滤器,然后设置将在文件签出之前(“涂抹”)和暂存之前(“清理”)处理文件的脚本。这些过滤器可以设置为做各种有趣的事情。

There is even an example for $LastChangedDate: $:

甚至还有一个例子$LastChangedDate: $

Another interesting example gets $Date$keyword expansion, RCS style. To do this properly, you need a small script that takes a filename, figures out the last commit date for this project, and inserts the date into the file. Here is a small Ruby script that does that:

$ git config filter.dater.smudge expand_date
$ git config filter.dater.clean 'perl -pe "s/\$Date[^\$]*\$/\$Date\$/"'

All the script does is get the latest commit date from the git logcommand, stick that into any $Date$strings it sees in stdin, and print the results – it should be simple to do in whatever language you're most comfortable in. You can name this file expand_dateand put it in your path. Now, you need to set up a filter in Git (call it dater) and tell it to use your expand_datefilter to smudge the files on checkout. You'll use a Perl expression to clean that up on commit:

date*.txt filter=dater
$ echo '# $Date$' > date_test.txt If you commit

This Perl snippet strips out anything it sees in a $Date$string, to get back to where you started. Now that your filter is ready, you can test it by setting up a Git attribute for that file that engages the new filter and creating a file with your $Date$keyword:

$ git add date_test.txt .gitattributes
$ git commit -m "Testing date expansion in Git"
$ rm date_test.txt
$ git checkout date_test.txt
$ cat date_test.txt
# $Date: Tue Apr 21 07:26:52 2009 -0700$

those changes and check out the file again, you see the keyword properly substituted:

##代码##

You can see how powerful this technique can be for customized applications. You have to be careful, though, because the .gitattributesfile is committed and passed around with the project, but the driver (in this case, dater) isn't, so it won't work everywhere. When you design these filters, they should be able to fail gracefully and have the project still work properly.

另一个有趣的例子是$Date$关键字扩展,RCS 风格。要正确执行此操作,您需要一个小脚本,该脚本获取文件名、计算此项目的最后提交日期并将日期插入文件中。这是一个执行此操作的小型 Rub​​y 脚本:

##代码##

脚本所做的就是从git log命令中获取最新的提交日期,将其粘贴到$Date$它在 stdin 中看到的任何字符串中,然后打印结果——使用您最熟悉的任何语言都应该很简单。您可以命名这个文件 expand_date并将其放在您的路径中。现在,您需要在 Git 中设置一个过滤器(称为dater)并告诉它使用您的expand_date过滤器在结帐时涂抹文件。您将使用 Perl 表达式在提交时清理它:

##代码##

这个 Perl 片段$Date$去掉了它在字符串中看到的任何东西,回到你开始的地方。现在您的过滤器已准备就绪,您可以通过为该文件设置一个使用新过滤器的 Git 属性并使用您的$Date$关键字创建一个文件来测试它:

##代码##

这些更改并再次检查文件,您会看到正确替换的关键字:

##代码##

您可以看到此技术对于自定义应用程序的强大功能。但是,您必须小心,因为该.gitattributes文件已提交并随项目一起传递,但驱动程序(在本例中为dater)不是,因此它不会在任何地方工作。当您设计这些过滤器时,它们应该能够优雅地失败并让项目仍然正常工作。

回答by Dane Balia

Sadly not.

可惜不是。

Read their documentation, link attached: Keyword Expansion

阅读他们的文档,附上链接: 关键字扩展