Фиксация Файлов Конфигурации Конкретной Машины
общий сценарий, когда я разрабатываю, заключается в том, что кодовая база будет иметь несколько файлов конфигурации, которые требуют специальных настроек машины. Эти файлы будут проверены в Git, и другие разработчики всегда будут случайно проверять их обратно и нарушать чужую конфигурацию.
простым решением для этого было бы просто не проверять их в Git или даже дополнительно добавлять .gitignore вход для них. Однако я считаю, что гораздо более элегантно иметь некоторые разумные значения по умолчанию в файле, который разработчик может изменить в соответствии с его потребностями.
есть ли элегантный способ заставить Git хорошо играть с такими файлами? Я хотел бы иметь возможность изменять файл конфигурации для конкретной машины, а затем запускать "git commit-a" без проверки этого файла.
9 ответов:
пусть ваша программа прочитает пару файлов конфигурации для своих настроек. Во-первых, он должен читать
config.defaults
файл, который будет включен в репозиторий. Затем он должен читатьconfig.local
файл, который должен быть указан в.gitignore
при таком расположении новые настройки появляются в файле по умолчанию и вступают в силу, как только он обновляется. Они будут различаться только в определенных системах, если они переопределены.
в качестве вариации на эту тему, вы могли бы просто общий
config
файл, который вы отправляете в систему управления версиями, и сделать что-то вродеinclude config.local
для ввода значений, специфичных для машины. Это вводит более общий механизм (по сравнению с политикой) в ваш код и, следовательно, позволяет более сложные конфигурации (если это желательно для вашего приложения). Популярное расширение от этого, замеченное во многих крупномасштабных программах с открытым исходным кодом, являетсяinclude conf.d
, который считывает конфигурацию из всех файлов в каталоге.и посмотреть мои ответ на подобный вопрос.
вы можете попробовать
git update-index --skip-worktree filename
. Это скажет git притвориться, что локальные изменения в имени файла не существуют, поэтомуgit commit -a
будет игнорировать его. Он имеет дополнительное преимущество также сопротивлятьсяgit reset --hard
, Так что вы случайно не потеряете свои локальные изменения. Кроме того, автоматическое слияние завершится ошибкой, если файл будет изменен вверх по течению (если копия рабочего каталога не соответствует копии индекса, и в этом случае он будет автоматически обновлен). Недостатком является то, что команда должна выполняться на всех вовлеченных машинах, и это трудно сделать автоматически. Смотрите такжеgit update-index --assume-unchanged
для тонко другой версии этой идеи. Подробности об обоих можно найти с помощьюgit help update-index
.
другой подход заключается в поддержании локальных изменений общих файлов конфигурации в другой частной ветви. Я делаю это для некоторых проектов, которые требуют нескольких локальных изменений. Этот метод может быть применим не ко всем ситуациям, но он работает для меня в некоторых случаях.
сначала я создаю новую ветвь на основе главной ветви (в данном конкретном случае я использую git-svn, поэтому мне нужно совершить от мастера, но это не очень важно здесь):
git checkout -b work master
теперь изменить файл (ы) конфигурации по мере необходимости и фиксации. Я обычно помещаю что-то отличительное в сообщение фиксации, например "NOCOMMIT" или "PRIVATE" (это будет полезно позже). На этом этапе вы можете работать над своей частной веткой, используя свой собственный файл конфигурации.
когда вы хотите, чтобы подтолкнуть вашу работу обратно вверх по течению, вишня-выбрать каждое изменение от вашего
work
филиала к мастеру. У меня есть скрипт для этого, который выглядит примерно так:#!/bin/sh BRANCH=`git branch | grep ^\* | cut -d' ' -f2` if [ $BRANCH != "master" ]; then echo ": 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
.наконец, после нажатия изменений в master upstream, я переключаюсь обратно на
work
и rebase:git checkout work git rebase master
Git повторно применит каждый из коммитов в
work
ветвь, эффективно пропуская один (ы), которые имеют уже был применен вmaster
через сбор вишни. То, что вам нужно оставить, - это только локальные коммиты NOCOMMIT.этот метод делает процесс толчка немного более трудоемким, но он решил проблему для меня, поэтому я подумал, что поделюсь.
одна из возможностей состоит в том, чтобы иметь фактические файлы в вашем .gitignore, но проверьте конфигурации по умолчанию с другим расширением. Типичным примером для приложения Rails будет конфигурация / база данных.yml-файл. Мы бы проверили в config / database.в формате YML.пример, и каждый разработчик создает свою собственную конфигурацию / базу данных.yml, который уже есть .gitignored.
Проверьте конфигурацию по умолчанию с другим расширением (скажем .по умолчанию), используйте символическую ссылку для символической ссылки по умолчанию на правильное местоположение, добавьте правильное местоположение.gitignore, и добавить все остальное, связанное с конфигурацией .gitignore (поэтому единственное, что проверяется, - это config.неисполнение.)
кроме того, напишите сценарий быстрой установки, который устанавливает символические ссылки для всего приложения.
мы использовали аналогичный подход в предыдущей компании. Сценарий установки автоматически определял, в какой среде вы работаете (sandbox, development, QA, production), и автоматически делал правильные вещи. Если у вас есть конфиг.песочница файл, и были запущены из песочницы, он будет связывать это (в противном случае он будет просто связать .файл по умолчанию.) Обычной процедурой было копирование .по умолчанию и при необходимости измените настройки.
написание сценария установки проще, чем вы можете себе представить, и дает вам большую гибкость.
Я согласен с лучшим ответом, но также хотел бы добавить что-то. Я использую сценарий ANT для удаления и изменения файлов из репозитория GIT, поэтому я уверен, что никакие производственные файлы не будут перезаписаны. В ANT есть хороший вариант для изменения файлов java-свойств. Это означает, что вы помещаете свои локальные тестовые переменные в файл свойств java-стиля и добавляете некоторый код для его обработки, но это дает вам возможность автоматизировать создание вашего сайта до того, как вы подключите его к FTP. Обычно вы помещаете свою производственную информацию на сайте.по умолчанию.свойства файла, и пусть муравей управлять настройками. Ваши локальные настройки будут находиться на сайте.местный.свойства.
<?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_");
вашего сайта.по умолчанию.свойства будут выглядеть так:
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
и ваш сайт.местный.свойства будут выглядеть так (Обратите внимание на разницу среды и включенных модулей):
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
и ваши инструкции 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>
самое простое решение-отредактировать файл по умолчанию, зафиксировать его, а затем добавить его в свой
, имеющего.gitignore
. Таким образом, разработчики не будут случайно фиксировать его при выполненииgit commit -a
, но они все равно могут зафиксировать его в (предположительно редком) случае, когда вы хотите изменить свои значения по умолчанию с помощьюgit add --force
..default
и.local
конфигурационный файл в конечном счете является лучшим решением, так как это позволяет любому пользователю с машинной конфигурацией изменять значения по умолчанию, не имея чтобы сломать собственную конфигурацию.
я делаю это так, как это рекомендуется здесь с файлами конфигурации по умолчанию и локальными файлами конфигурации. Для управления моими локальными конфигурационными файлами, которые находятся в проектах
.gitignore
, Я сделал git РЕПО~/settings
. Там я управляю всеми своими локальными настройками из всех проектов. Вы создаете, например папкуproject1
на~/settings
и поместите в него все локальные конфигурационные материалы для этого проекта. После этого вы можете символически связать эти файлы / папку с вашимproject1
.при таком подходе вы можете отслеживать свои локальные файлы конфигурации и не делать этого положить их в обычный репозиторий исходного кода.
основываясь на ответе @Greg Hewgill, вы можете добавить конкретную фиксацию с локальными изменениями и пометить ее как localchange:
git checkout -b feature master vim config.local git add -A && git commit -m "local commit" && git tag localchange
затем перейдите к добавлению коммитов вашей функции. После завершения работы вы можете объединить эту ветвь обратно в master без фиксации localchange, выполнив следующее:
git rebase --onto master localchange feature git fetch . feature:master git cherry-pick localchange git tag localchange -f
эти команды:
1) Перебазируйте свою ветвь функции в master, игнорируя фиксацию localchange. 2) быстрая перемотка вперед мастер, не выходя из функции отделение 3) Добавьте фиксацию localchange обратно в верхнюю часть ветви функции, чтобы вы могли продолжить работу над ней. Вы можете сделать это для любой другой ветви вы хотите продолжить работу. 4) сбросьте тег localchange на этот вишневый коммит, чтобы мы могли использовать
rebase --onto
опять таким же образом.Это не означает, чтобы заменить принятый ответ как лучшее общее решение, но как способ мышления из коробки о проблеме. Вы в основном избегаете случайного слияния локальных изменений в master by только перебазирование с
localchange
доfeature
и перемотка вперед мастер.