web-dev-qa-db-fra.com

Git substitution de mots clés comme ceux de Subversion?

J'avais l'habitude de travailler sous Subversion/SVN et j'utilisais instantanément la fonctionnalité Nice appelée substitution de mots clés. Il suffit de mettre des fichiers source comme:

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

Et chaque fois que Subversion remplaçait des mots clés (Author, Rev, LastChangedDate) par des mots réels.

Il y a quelque temps, j'ai été obligé de passer à Git et je me demande simplement s'il y a quelque chose de similaire à la substitution de mots clés de Subversion dans Git?

44
Barmaley Red Star

Solution

Eh bien, vous pourriez facilement implémenter une telle fonctionnalité vous-même.

Fondamentalement, j'ai intégré la commande commit dans un script Shell. Ce script remplace d'abord les macros souhaitées, puis valide les modifications. Le projet se compose de deux fichiers:

Contenu?

keysub, un script shell bash et keysub.awk un script awk pour remplacer les mots clés dans un fichier spécifique. Un troisième fichier est un fichier de configuration qui contient les valeurs qui doivent être substituées (en plus des éléments variables comme le nombre de validations et l'horodatage).

Comment l'utilisez-vous?

Vous appelez keysub au lieu de valider avec les mêmes options. Le -m ou -a l'option doit précéder toute autre option de validation. Une nouvelle option (qui devrait toujours venir en premier) est -f qui prend un fichier de configuration comme valeur. Exemple:

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

ou

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

sous-clé

#!/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
}

Fichier de configuration

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

Remarques

J'ai essayé de documenter suffisamment bien pour que vous puissiez facilement l'implémenter et le modifier selon vos besoins personnels. Notez que vous pouvez donner aux macros le nom que vous souhaitez, tant que vous le modifiez dans le code source. J'ai également cherché à garder relativement facile à étendre le script, vous devriez pouvoir ajouter de nouvelles macros assez facilement. Si vous souhaitez étendre ou modifier le script, vous pouvez également consulter le répertoire .git, il devrait y avoir beaucoup d'informations qui peuvent aider à améliorer le script, en raison du manque de temps, je ne l'ai pas fait enquêtez sur le dossier.

16
ShellFish

Git n'est pas livré avec cette fonctionnalité prête à l'emploi. Cependant, il y a un chapitre dans le livre Git sur Customizing Git et l'un des exemples est comment utiliser les attributs git pour implémenter un résultat similaire.

Il s'avère que vous pouvez écrire vos propres filtres pour effectuer des substitutions dans les fichiers lors de la validation/extraction. Ceux-ci sont appelés filtres "propres" et "maculés". Dans le .gitattributes fichier, vous pouvez définir un filtre pour des chemins particuliers, puis configurer des scripts qui traiteront les fichiers juste avant leur extraction ("smudge") et juste avant leur mise en scène ("clean"). Ces filtres peuvent être configurés pour faire toutes sortes de choses amusantes.

Il y a même un exemple pour $LastChangedDate: $:

Un autre exemple intéressant obtient $Date$ extension de mots clés, style RCS. Pour le faire correctement, vous avez besoin d'un petit script qui prend un nom de fichier, détermine la dernière date de validation de ce projet et insère la date dans le fichier. Voici un petit script Ruby qui fait ça:

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

Tout ce que le script fait, c'est obtenir la dernière date de validation du git log commande, collez-la dans n'importe quel $Date$ les chaînes qu'il voit dans stdin et imprimer les résultats - cela devrait être simple à faire dans la langue dans laquelle vous êtes le plus à l'aise. Vous pouvez nommer ce fichier expand_date et mettez-le sur votre chemin. Maintenant, vous devez configurer un filtre dans Git (appelez-le dater) et lui dire d'utiliser votre expand_date filtre pour tacher les fichiers à la caisse. Vous allez utiliser une expression Perl pour nettoyer cela lors de la validation:

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

Cet extrait de Perl supprime tout ce qu'il voit dans un $Date$ chaîne, pour revenir à votre point de départ. Maintenant que votre filtre est prêt, vous pouvez le tester en configurant un attribut Git pour ce fichier qui engage le nouveau filtre et en créant un fichier avec votre $Date$ mot-clé:

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

ces modifications et extraire à nouveau le fichier, vous voyez le mot-clé correctement substitué:

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

Vous pouvez voir à quel point cette technique peut être puissante pour des applications personnalisées. Vous devez cependant être prudent, car le .gitattributes Le fichier est validé et transmis avec le projet, mais le pilote (dans ce cas, dater) ne l'est pas, donc il ne fonctionnera pas partout. Lorsque vous concevez ces filtres, ils doivent pouvoir échouer correctement et que le projet fonctionne toujours correctement.

31
Marcin Koziński

Malheureusement pas.

Lisez leur documentation, lien attaché: Expansion des mots clés

6
Dane Balia