[git] 머신 특정 구성 파일 커밋

개발할 때 일반적인 시나리오는 코드베이스에 시스템 특정 설정이 필요한 여러 구성 파일이 있다는 것입니다. 이러한 파일은 Git에 체크인되고 다른 개발자는 항상 실수로 다시 체크인하여 다른 사람의 구성을 손상시킵니다.

이에 대한 간단한 해결책은 Git에 체크인하지 않거나 추가로 .gitignore 항목을 추가하는 것입니다. 그러나 개발자가 필요에 맞게 수정할 수있는 파일에 합리적인 기본값을 갖는 것이 훨씬 더 우아하다는 것을 알았습니다.

이러한 파일로 Git을 멋지게 재생하는 우아한 방법이 있습니까? 컴퓨터 별 구성 파일을 수정 한 다음 해당 파일을 확인하지 않고 “git commit -a”를 실행할 수 있기를 바랍니다.



답변

프로그램이 설정을 위해 한 쌍의 구성 파일을 읽도록하십시오. 첫째, config.defaults저장소에 포함될 파일을 읽어야 합니다. 그런 다음 다음에 config.local나열되어야 하는 파일을 읽어야합니다 ..gitignore

이 배열을 사용하면 새 설정이 기본 파일에 나타나고 업데이트되는 즉시 적용됩니다. 재정의 된 경우에만 특정 시스템에서 달라집니다.

이것에 대한 변형으로, config버전 제어에서 제공 하는 일반 파일 만 가질 수 있으며 include config.local컴퓨터 특정 값을 가져 오는 것과 같은 작업을 수행 하도록 할 수 있습니다. 이는 코드에보다 일반적인 메커니즘 (정책 대비)을 도입하고 결과적으로 더 복잡한 구성을 가능하게합니다 (응용 프로그램에 적합한 경우). 많은 대규모 오픈 소스 소프트웨어에서 볼 수있는이 확장은 to입니다 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 checkout -b work master

이제 필요에 따라 구성 파일을 수정하고 커밋합니다. 나는 보통 “NOCOMMIT”또는 “PRIVATE”와 같은 커밋 메시지에 독특한 것을 넣습니다 (나중에 유용 할 것입니다). 이 시점에서 자신의 구성 파일을 사용하여 비공개 브랜치에서 작업 할 수 있습니다.

작업을 다시 업스트림으로 푸시하려면 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에 실제 파일이 있지만 확장자가 다른 기본 구성을 체크인하는 것입니다. Rails 앱의 일반적인 예는 config / database.yml 파일입니다. config / database.yml.sample을 확인하고 각 개발자는 이미 .gitignored 인 자체 config / database.yml을 만듭니다.


답변

다른 확장자 (예 : .default)로 기본 구성을 체크인하고, symlink를 사용하여 기본값을 올바른 위치로 심볼릭 링크하고, 올바른 위치를 .gitignore에 추가하고, 구성과 관련된 다른 모든 것을 .gitignore에 추가합니다 (따라서 유일한 체크인되는 것은 config.default)입니다.

또한 응용 프로그램 전체에 대한 심볼릭 링크를 설정하는 빠른 설치 스크립트를 작성하십시오.

우리는 이전 회사에서 비슷한 접근 방식을 사용했습니다. 설치 스크립트는 실행중인 환경 (샌드 박스, 개발, QA, 프로덕션)을 자동으로 감지하고 자동으로 올바른 작업을 수행합니다. config.sandbox 파일이 있고 샌드 박스에서 실행중인 경우 해당 파일이 연결됩니다 (그렇지 않으면 .defaults 파일 만 연결됨). 일반적인 절차는 .defaults를 복사하고 필요에 따라 설정을 변경하는 것이 었습니다.

설치 스크립트를 작성하는 것은 상상하는 것보다 쉬우 며 많은 유연성을 제공합니다.


답변

베스트 답변에 동의하지만 추가하고 싶습니다. ANT 스크립트를 사용하여 GIT 저장소에서 파일을 제거하고 수정하므로 프로덕션 파일을 덮어 쓰지 않습니다. ANT에는 java-property 파일을 수정하는 좋은 옵션이 있습니다. 즉, 로컬 테스트 변수를 자바 스타일 속성 파일에 넣고 코드를 추가하여 처리하지만 FTP를 온라인으로 보내기 전에 사이트 구축을 자동화 할 수있는 기회를 제공합니다. 일반적으로 프로덕션 정보를 site.default.properties 파일에 넣고 ANT가 설정을 관리하도록합니다. 로컬 설정은 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=/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

귀하의 site.local.properties는 다음과 같습니다 (차이 환경 및 활성화 된 모듈에 유의하십시오).

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>


답변

요즘 (2019) 저는 예를 들어 python / django에서 ENV vars를 사용하며 기본값을 추가 할 수도 있습니다. docker의 컨텍스트에서 ENV vars를 docker-compose.yml 파일 또는 버전 제어에서 무시되는 추가 파일에 저장할 수 있습니다.

# settings.py
import os
DEBUG = os.getenv('DJANGO_DEBUG') == 'True'
EMAIL_HOST = os.environ.get('DJANGO_EMAIL_HOST', 'localhost')