git - Committing Machine Specific Configuration Files
git add ignore file (8)
Estoy de acuerdo con la mejor respuesta pero también me gustaría agregar algo. Uso una secuencia de comandos ANT para quitar y modificar archivos del repositorio de GIT, así que estoy seguro de que no se sobrescribirán los archivos de producción. Hay una buena opción en ANT para modificar archivos de propiedades java. Esto significa colocar las variables de prueba locales en un archivo de propiedades estilo java y agregar algún código para procesarlas, pero le brinda la oportunidad de automatizar la creación de su sitio antes de que lo FTP en línea. Por lo general, debe colocar su información de producción en el archivo site.default.properties y dejar que ANT administre la configuración. Su configuración local estaría en el sitio.local.properties.
<?php
/**
* This class will read one or two files with JAVA style property files. For instance site.local.properties & site.default.properties
* This will enable developers to make config files for their personal development environment, while maintaining a config file for
* the production site.
* Hint: use ANT to build the site and use the ANT <propertyfile> command to change some parameters while building.
* @author martin
*
*/
class javaPropertyFileReader {
private $_properties;
private $_validFile;
/**
* Constructor
* @return javaPropertyFileReader
*/
public function __construct(){
$this->_validFile = false;
return $this;
}//__construct
/**
* Reads one or both Java style property files
* @param String $filenameDefaults
* @param String $filenameLocal
* @throws Exception
* @return javaPropertyFileReader
*/
public function readFile($filenameDefaults, $filenameLocal = ""){
$this->handleFile($filenameDefaults);
if ($filenameLocal != "") $this->handleFile($filenameLocal);
}//readFile
/**
* This private function will do all the work of reading the file and setting up the properties
* @param String $filename
* @throws Exception
* @return javaPropertyFileReader
*/
private function handleFile($filename){
$file = @file_get_contents($filename);
if ($file === false) {
throw (New Exception("Cannot open property file: " . $filename, "01"));
}
else {
# indicate a valid file was opened
$this->_validFile = true;
// if file is Windows style, remove the carriage returns
$file = str_replace("/r", "", $file);
// split file into array : one line for each record
$lines = explode("/n", $file);
// cycle lines from file
foreach ($lines as $line){
$line = trim($line);
if (substr($line, 0,1) == "#" || $line == "") {
#skip comment line
}
else{
// create a property via an associative array
$parts = explode("=", $line);
$varName = trim($parts[0]);
$value = trim($parts[1]);
// assign property
$this->_properties[$varName] = $value;
}
}// for each line in a file
}
return $this;
}//readFile
/**
* This function will retrieve the value of a property from the property list.
* @param String $propertyName
* @throws Exception
* @return NULL or value of requested property
*/
function getProperty($propertyName){
if (!$this->_validFile) throw (new Exception("No file opened", "03"));
if (key_exists($propertyName, $this->_properties)){
return $this->_properties[$propertyName];
}
else{
return NULL;
}
}//getProperty
/**
* This function will retreive an array of properties beginning with a certain prefix.
* @param String $propertyPrefix
* @param Boolean $caseSensitive
* @throws Exception
* @return Array
*/
function getPropertyArray($propertyPrefix, $caseSensitive = true){
if (!$this->_validFile) throw (new Exception("No file opened", "03"));
$res = array();
if (! $caseSensitive) $propertyPrefix= strtolower($propertyPrefix);
foreach ($this->_properties as $key => $prop){
$l = strlen($propertyPrefix);
if (! $caseSensitive) $key = strtolower($key);
if (substr($key, 0, $l ) == $propertyPrefix) $res[$key] = $prop;
}//for each proprty
return $res;
}//getPropertyArray
function createDefineFromProperty($propertyName){
$propValue = $this->getProperty($propertyName);
define($propertyName, $propValue);
}//createDefineFromProperty
/**
* This will create a number of ''constants'' (DEFINE) from an array of properties that have a certain prefix.
* An exception is thrown if
* @param String $propertyPrefix
* @throws Exception
* @return Array The array of found properties is returned.
*/
function createDefinesFromProperties($propertyPrefix){
// find properties
$props = $this->getPropertyArray($propertyPrefix);
// cycle all properties
foreach($props as $key => $prop){
// check for a valid define name
if (preg_match("''[a-zA-Z_/x7f-/xff][a-zA-Z0-9_/x7f-/xff]*''", $key)) {
define($key, $prop);
}
else{
throw (new Exception("Invalid entry in property file: cannot create define for {" . $key . "}", "04"));
}
}// for each property found
return $props;
}//createDefineFromProperty
}//class javaPropertyFileReader
luego úsalo:
$props = new javaPropertyFileReader();
$props->readFile($_SERVER["DOCUMENT_ROOT"] . "/lib/site.default.properties",$_SERVER["DOCUMENT_ROOT"] . "/lib/site.local.properties");
#create one DEFINE
$props->createDefineFromProperty("picture-path");
# create a number of DEFINEs for enabled modules
$modules = $props->createDefinesFromProperties("mod_enabled_");
Su site.default.properties se vería así:
release-date=x
environment=PROD
picture-path=/images/
SITE_VERSION_PRODUCTION=PROD
SITE_VERSION_TEST=TEST
SITE_VERSION_DEVELOP=DEV
# Available Modules
mod_enabled_x=false
mod_enabled_y=true
mod_enabled_z=true
y su site.local.properties se vería así (observe el entorno de diferencia y los módulos habilitados):
release-date=x
environment=TEST
picture-path=/images/
SITE_VERSION_PRODUCTION=PROD
SITE_VERSION_TEST=TEST
SITE_VERSION_DEVELOP=DEV
# Available Modules
mod_enabled_x=true
mod_enabled_y=true
mod_enabled_z=true
Y sus instrucciones ANT: ($ d {deploy} es su directorio de destino de despliegue)
<propertyfile
file="${deploy}/lib/site.properties"
comment="Site properties">
<entry key="environment" value="PROD"/>
<entry key="release-date" type="date" value="now" pattern="yyyyMMddHHmm"/>
</propertyfile>
Un escenario común cuando desarrollo es que la base de código tendrá varios archivos de configuración que requieren configuraciones específicas de la máquina. Estos archivos se registrarán en Git y otros desarrolladores siempre los verifican accidentalmente y rompen la configuración de otra persona.
Una solución simple a esto sería simplemente no registrarlos en Git, o incluso agregar adicionalmente una entrada .gitignore para ellos. Sin embargo, considero que es mucho más elegante tener algunos valores predeterminados razonables en el archivo que el desarrollador puede modificar para satisfacer sus necesidades.
¿Hay alguna manera elegante de hacer que Git juegue bien con esos archivos? Me gustaría poder modificar un archivo de configuración específico de la máquina y luego poder ejecutar "git commit -a" sin verificar ese archivo.
La solución más simple es editar el archivo a los valores predeterminados, confirmarlo y luego agregarlo a su .gitignore
. De esta manera, los desarrolladores no lo cometerán accidentalmente al hacer git commit -a
, pero aún pueden hacerlo en el caso (presumiblemente raro) en el que desee cambiar los valores predeterminados con git add --force
.
Sin embargo, tener un archivo de configuración .default
y .local
es en última instancia la mejor solución, ya que esto permite a cualquier persona con una configuración específica de la máquina cambiar los valores predeterminados, sin tener que romper su propia configuración.
Lo hago como se recomienda aquí con los archivos de configuración locales y predeterminados. Para administrar mis archivos de configuración local que están en los proyectos .gitignore
, hice un git repo ~/settings
. Allí administro todas las configuraciones locales de todos los proyectos. Usted crea, por ejemplo, una carpeta project1
en ~/settings
y coloca todo el contenido de la configuración local para este proyecto. Después de eso, puede enlazar esos archivos / carpeta a su project1
.
Con ese enfoque, puede hacer un seguimiento de sus archivos de configuración local y no ponerlos en el repositorio de código fuente normal.
Otro enfoque es mantener los cambios locales a los archivos de configuración comunes en otra rama privada. Lo hago para algunos proyectos que requieren varios cambios locales. Esta técnica puede no ser aplicable a todas las situaciones, pero a mí me funciona en algunos casos.
Primero creo una nueva rama basada en la rama principal (en este caso particular, estoy usando git-svn, así que tengo que comprometerme desde el maestro pero eso no es terriblemente importante aquí):
git checkout -b work master
Ahora modifique los archivos de configuración según sea necesario y confirme. Normalmente pongo algo distintivo en el mensaje de confirmación como "NOCOMMIT" o "PRIVATE" (esto será útil más adelante). En este punto, puede trabajar en su sucursal privada usando su propio archivo de configuración.
Cuando desee impulsar su trabajo hacia arriba, seleccione cada cambio desde su rama de work
al maestro. Tengo un script para ayudarme a hacer esto, que se ve así:
#!/bin/sh
BRANCH=`git branch | grep ^//* | cut -d'' '' -f2`
if [ $BRANCH != "master" ]; then
echo "$0: Current branch is not master"
exit 1
fi
git log --pretty=oneline work...master | grep -v NOCOMMIT: | cut -d'' '' -f1 | tac | xargs -l git cherry-pick
Esto primero comprueba para asegurarse de que estoy en la rama master
(control de cordura). Luego, enumera cada compromiso en el work
, filtra los que mencionan la palabra clave NOCOMMIT, invierte el orden y, finalmente, selecciona con precisión cada confirmación (ahora desde la más antigua primero) en el master
.
Finalmente, después de empujar los cambios en el maestro en sentido ascendente, vuelvo a work
y volver a establecer la base:
git checkout work
git rebase master
Git volverá a aplicar cada una de las confirmaciones en la rama de work
, salteando efectivamente la (s) que ya se han aplicado en el master
través de la selección de cerezas. Lo que debería quedar es solo los commit locales de NOCOMMIT.
Esta técnica hace que el proceso de inserción requiera un poco más de tiempo, pero me solucionó un problema, así que pensé en compartirlo.
Puedes probar git update-index --skip-worktree filename
. Esto le dirá a git que pretenda que los cambios locales en el nombre del archivo no existen, por lo que git commit -a
lo ignorará. Tiene la ventaja adicional de que también resiste el git reset --hard
, por lo que no perderás accidentalmente tus cambios locales. Además, las fusiones automáticas fallarán correctamente si el archivo se cambia en sentido ascendente (a menos que la copia del directorio de trabajo coincida con la copia del índice, en cuyo caso se actualizará automáticamente). La desventaja es que el comando debe ejecutarse en todas las máquinas involucradas, y es difícil hacerlo automáticamente. Ver también git update-index --assume-unchanged
para una versión sutilmente diferente de esta idea. Los detalles sobre ambos se pueden encontrar con git help update-index
.
Una posibilidad es tener los archivos reales en su .gitignore, pero verifique las configuraciones predeterminadas con una extensión diferente. Un ejemplo típico para una aplicación de Rails sería el archivo config / database.yml. Nos registramos en config / database.yml.sample, y cada desarrollador crea su propia configuración / database.yml que ya está .gitignored.
Verifique una configuración predeterminada con una extensión diferente (por ejemplo, .default), use un enlace simbólico para enlazar de manera simbólica la ubicación predeterminada a la correcta, agregue la ubicación correcta a .gitignore y agregue todo lo demás relacionado con la configuración a .gitignore (por lo que el único Lo que se comprueba es config.default).
Además, escriba un script de instalación rápida que configure los enlaces simbólicos para su aplicación.
Usamos un enfoque similar en una compañía anterior. El script de instalación detectó automáticamente en qué entorno se estaba ejecutando (sandbox, desarrollo, control de calidad, producción) y automáticamente haría lo correcto. Si tuviera un archivo config.sandbox, y se ejecutara desde el sandbox, lo vincularía (de lo contrario, simplemente vincularía el archivo .defaults). El procedimiento común era copiar .defaults y cambiar la configuración según sea necesario.
Escribir el script de instalación es más fácil de lo que imagina, y le da mucha flexibilidad.
Haga que su programa lea un par de archivos de configuración para su configuración. En primer lugar, debe leer un archivo config.defaults
que se incluiría en el repositorio. Luego, debería leer un archivo config.local
que debería estar listado en .gitignore
Con esta disposición, las nuevas configuraciones aparecen en el archivo predeterminado y surten efecto tan pronto como se actualizan. Solo variarán en sistemas particulares si se invalidan.
Como una variación de esto, podría tener solo un archivo de config
general que envíe en el control de versión, y hacer que haga algo como include config.local
para traer los valores específicos de la máquina. Esto introduce un mecanismo más general (frente a la política) en su código, y en consecuencia habilita configuraciones más complicadas (si eso es deseable para su aplicación). La popular extensión de esto, que se ve en muchos softwares de código abierto a gran escala, es include conf.d
, que lee la configuración de todos los archivos en un directorio.
También vea mi respuesta a una pregunta similar.