web-dev-qa-db-ja.com

SubversionのようなGitキーワード置換?

以前はSubversion/SVNで作業していましたが、すぐにキーワード置換と呼ばれる素敵な機能を使用していました。次のようなソースファイルを置くだけです。

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

そして、Subversionがキーワード(Author、Rev、LastChangedDate)を実際のキーワードで置き換えるたびに。

しばらく前に、私はGitに移行することを余儀なくされましたが、GitにSubversionのキーワード置換に似たものがあるのではないかと思っただけです。

39
barmaley

解決

まあ、あなたは簡単にそのような機能を自分で実装することができます。

基本的に、コミットコマンドをシェルスクリプトに埋め込みました。このスクリプトは、最初に目的のマクロを置換してから、変更をコミットします。プロジェクトは2つのファイルで構成されます。

コンテンツ?

keysub、bashシェルスクリプトおよびkeysub.awk特定のファイルのキーワードを置き換えるawkスクリプト。 3番目のファイルは、置換する必要のある値を含む構成ファイルです(コミットカウントやタイムスタンプなどの変数以外のもの)。

どんなふうに使うの?

同じオプションでcommitの代わりにkeysubを呼び出します。 -mまたは-aオプションは、他のコミットオプションの前に来る必要があります。新しいオプション(常に最初に来る必要がある)は-f構成ファイルを値として使用します。例:

$ git add 'someJavaFile.Java'
$ keysub -m 'fixed concurrent thread issue'
$ git Push

または

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

キーサブ

#!/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:\).*/\1${timestamp}/
  }
  /commitmsg:/ {
    s/\(commitmsg:\).*/\1${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

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

# 1 parse source files 
########################
# update author
$0 ~ /.*\$Author.*\$.*/ {
  $2=author " $"
}

# update timestamp
$0 ~ /.*\$LastChangedDate.*\$.*/ {
  $0=$1
  $2=timestamp " $"
}

# update commit message
$0 ~ /.*\$LastChangeMessage.*\$.*/ {
  $2=commitmsg " $"
}

# update commit counts
$0 ~ /.*\$Rev.*\$.*/ {
  ++$2
  $2=$2 " $"
}

# print line
{
  print
}

構成ファイル

author:ubunut-420
timestamp:Fri Jun 21 20:42:54 CEST 2013
commitmsg:default commit message

備考

あなたが簡単に実装し、あなた自身の個人的なニーズに合わせて修正できるように、十分に文書化しようとしました。ソースコードでマクロを変更する限り、マクロに任意の名前を付けることができます。また、スクリプトを比較的簡単に拡張できるようにすることも目指しました。新しいマクロをかなり簡単に追加できるはずです。スクリプトを拡張または変更することに興味がある場合は、.gitディレクトリも参照してください。時間がないため、スクリプトを強化するのに役立つ情報が豊富にあります。ただし、フォルダを調査してください。

14
ShellFish

Gitは、この機能をすぐに使える状態で出荷しません。ただし、Git Bookには Gitのカスタマイズ に関する章があり、例の1つはgit属性を使用して同様の結果を実装する方法です。

コミット/チェックアウト時にファイルの置換を行うための独自のフィルターを作成できることがわかりました。これらは「クリーン」および「スマッジ」フィルターと呼ばれます。 .gitattributesファイルでは、特定のパスにフィルターを設定し、ファイルがチェックアウトされる直前(「汚れ」)およびステージングされる前(「クリーン」)にファイルを処理するスクリプトを設定できます。 。これらのフィルターは、あらゆる種類の楽しいことを行うように設定できます。

$LastChangedDate: $の例さえあります:

別の興味深い例は、$Date$キーワード拡張、RCSスタイルを取得します。これを適切に行うには、ファイル名を取得し、このプロジェクトの最終コミット日を把握し、その日付をファイルに挿入する小さなスクリプトが必要です。これを行う小さなRubyスクリプト:

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

スクリプトは、git logコマンドから最新のコミット日を取得し、stdinで表示される$Date$文字列に貼り付け、結果を出力します。どの言語でも簡単に実行できます。このファイルにexpand_dateという名前を付けて、パスに入れることができます。次に、Gitでフィルターをセットアップし(daterと呼びます)、expand_dateフィルターを使用してチェックアウト時にファイルを汚すように指示する必要があります。 Perl式を使用して、コミット時にクリーンアップします。

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

このPerlスニペットは、$Date$文字列にあるものをすべて取り除き、開始した場所に戻ります。フィルターの準備ができたので、新しいフィルターを使用するファイルのGit属性を設定し、$Date$キーワードを使用してファイルを作成することで、フィルターをテストできます。

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

これらの変更を行い、ファイルを再度チェックアウトすると、キーワードが適切に置換されていることがわかります。

$ 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$

この手法がカスタマイズされたアプリケーションに対してどれほど強力であるかを見ることができます。ただし、.gitattributesファイルはコミットされてプロジェクトに渡されますが、ドライバー(この場合はdater)はそうではないため、機能しません。どこにでも。これらのフィルターを設計するとき、それらは正常に失敗し、プロジェクトが適切に機能するようにする必要があります。

27

悲しいことではありません。

ドキュメントを読んで、リンクを添付: キーワード拡張

6
Dane Balia