【问题标题】:Git + Python Virtualenvs: unique directory across repositoriesGit + Python Virtualenvs:跨存储库的唯一目录
【发布时间】:2014-11-07 08:03:39
【问题描述】:

我有 2 个通过公共服务器同步的存储库。

Repository 1 -- Server -- Repository 2

在每个存储库中,都有一个名为“unique”的目录,可以保存有关在 virtualenv 中使用的数据库设置的数据。

我想要实现的是分别保存每个 virtualenv 的“唯一”目录的内容,而不会干扰 python 代码的工作流。

感谢您的帮助。

【问题讨论】:

  • 想了解更多关于这个有点独特的用例...你看过 Git 子模块吗?
  • 这实际上是 Django 中南的一个迁移目录。它跟踪数据库结构的变化,每个分支对应一个不同的数据库。这可能是一种夸张的方法,没有它我可以做到,或者我可以在 git 之外找到解决方法。但是,我想知道是否有办法使用 git 的配置设置来做到这一点。我过去曾尝试过 git 子模块,我发现它们很麻烦且难以维护。如果它们是唯一的解决方案,我宁愿改变我的开发设置的结构。
  • 这两个存储库是否有某些原因需要它们的“唯一”目录具有相同的名称?如果您有其他方式来区分(例如,通过您的数据库名称或运行它的主机),您可以使用它来选择正确的目录。如果他们有不同的名字,你的问题就会消失。
  • 谢谢墨菲船长的想法。我会检查是否可以实施。我还计划使用带有每个分支名称的 .local 目录作为子目录,并在每次结帐或提交时使用挂钩来复制“唯一”文件。我会在下午工作。

标签: python git virtualenv


【解决方案1】:

编辑:以下是一种幼稚的“第一种方法”,无法验证为可行的解决方案。

这个问题要复杂得多,我会尝试改进我的方法,但这不是当前的优先事项。


我要解决这个问题的方法是将“唯一”文件或目录软链接到 .local 文件夹中的文件或目录。

第一次创建这个文件夹的结构-在创建其他分支之前在master分支-使用以下命令:

cd ./$(git rev-parse --show-cdup) && mkdir -p .local/$(echo "$USER")/$(git symbolic-ref --short -q HEAD)

此命令将在存储库的根目录中创建树:

.local/<username>/<git branch>.

存储库的“唯一”文件必须移动到此树下的相关目录,并且必须进行软链接。

例如:

独特的文件

./path/to/file/<my_file> 

将手动移至:

./.local/<username>/<git branch>/path/to/file/<my_file>

同时将在旧位置手动创建符号链接以链接到新位置。

"./path/to/file/<my_file>" -> "./.local/<username>/<git branch>/path/to/file/<my_file>"

符号链接也可以添加到 .gitignore 文件中:

path/to/file/<my_file>

如果其他分支已经存在,则必须从 master 分支中检出 .local 文件夹,并且必须为当前分支手动重复上述过程。

从现在开始有两个脚本叫做 post-merge 和 post-checkout in:

.git/hooks/post-merge
.git/hooks/post-checkout

如果权限设置为 755,则有望在每次合并后更新符号链接。

在第一次拉取/合并期间,它将创建一个本地环境,其中将复制和符号链接来自原始存储库的所有“唯一”文件。

用户必须删除或相应地安排 .local 子文件夹,然后链接将在需要时由主要使用 sed 的 bash 编写的脚本自动更新。

由于时间限制,我没有彻底测试脚本。它似乎在我的 Fedora 19 环境中工作,如果必须进行更改,我会更新帖子。

我仍然非常欢迎更面向 git 的解决方案。

合并后:

#! /bin/sh

# "origin": Refers to the existing merged files (soft links) information that will be overriden.
# "local": Refers to the local system information that will replace origin information.

#Get local user name to define the repository directory.
local_user=$(echo "$USER")

#Get local branch name to define the branch directory.
local_branch=$(git symbolic-ref --short -q HEAD)

#Go to the root of the repository
cd ./$(git rev-parse --show-cdup)

#Create local environment
local_environment=$(echo './.local/'"$local_user"'/'"$local_branch"'/')
mkdir -p $local_environment


#Grub and manipulate all soft links.
symlink_no=0
for link in $(find -L ./ -xtype l); 
do
    symlink_no=$[symlink_no+1]
    # Grub information from each symlink
    source_file=$(ls -la $link | sed -n 's/^.*[0-9]\+:[0-9]\+\s\(.*\)\s->.*$/\1/p')
    origin_target_file=$(ls -la $link | sed -n 's/.*>\s\+\(.*\)$/\1/p')
    relative_to_root_origin_target_file=$(ls -la $link | sed -n 's/.*>\s.*\(.\/.local.*\)$/\1/p')

    find_origin_user='s/.*local\/\([a-zA-Z0-9._-]\+\).*/\1/p'
    origin_user=$(echo $origin_target_file | sed -n "$find_origin_user")
    find_origin_branch='s/.*local\/'"$origin_user"'\/\([a-zA-Z0-9._-]\+\).*/\1/p' 
    origin_branch=$(echo $origin_target_file | sed -n "$find_origin_branch")

    local_target_file=$(echo $origin_target_file | sed -n 's/local\/'"$origin_user"'\/'"$origin_branch"'/local\/'"$local_user"'\/'"$local_branch"'/p')
    relative_to_root_local_target_file=$(echo $relative_to_root_origin_target_file | sed -n 's/local\/'"$origin_user"'\/'"$origin_branch"'/local\/'"$local_user"'\/'"$local_branch"'/p')

    remove_file_from_target_path='s/\(.*\)\/.*$/\1/p'
    find_local_target_layout=$(echo $relative_to_root_origin_target_file | sed -n "$remove_file_from_target_path" | sed -n 's/.*'"$origin_branch"'\/\(.*$\)/\1/p')
    local_environment_path=$(echo "$local_environment""$find_local_target_layout")

    #Wording header.
    echo ''
    echo ''
    echo 'Checking for symlink [' "$symlink_no"' ]:' "$source_file"
    echo '------------------------------------------------------'
    echo ''

    # ------------------- Target File Manipulation ---------------------------------
    #Check if target file does not exist in local environment.
    if [ ! -f "$relative_to_root_local_target_file" ]; 
    then
        #If true, verify that the source file exists and copy it.
        if [ -f "$relative_to_root_origin_target_file" ];
        then
            mkdir -p "$local_environment_path"
            rsync -va "$relative_to_root_origin_target_file" "$relative_to_root_local_target_file"
            echo ''
            origin_target_file_exists=1
            local_target_file_exists=1
            echo 'The source file has been created: ' "$relative_to_root_local_target_file"
        else
            echo 'The source file does not exist: ' "$relative_to_root_origin_target_file"
            origin_target_file_exists=0
            local_target_file_exists=0
        fi
    else
        local_target_file_exists=1
        echo 'The local file "'"$relative_to_root_local_target_file" '"  already exists.'
    fi
    source_path=$(echo $source_file | sed -n "$remove_file_from_target_path")
    source_check_target=$(echo "$source_path"'/'"$local_target_file")
    #-------------------- Source File Manipulation ---------------------------------
    # If target file exists:
    if [[ $local_target_file_exists -eq 1 ]];
    then
        #Check the source file if it is already linked.
        if [ "$local_target_file" == "$origin_target_file" ];
        then
            #Is it a correct link?
            if [ -f $source_check_target ];
            then
                echo 'symlink "' "$source_file" '->' "$local_target_file" '" already exists and it is correct.'
            else
                echo '*********** This is a broken link: ' "$source_file"
                echo '           **********************'
            fi
        else
            #Delete existing symlink
            rm "$source_file"

            #Create new symlink according to local environment
            ln -s "$local_target_file" "$source_file"
            echo 'symlink "' "$source_file" '->' "$local_target_file" '" has been created.'
        fi
    else
        echo '*********** This is a broken link: ' "$source_file"
        echo '            **********************'
    fi
done

结帐后:

#! /bin/sh

# Start from the repository root.
cd ./$(git rev-parse --show-cdup)

# Delete .pyc files and empty directories.
find . -name "*.pyc" -delete
#find . -type d -empty -delete

#----------------Symlink manipulation---------------------------------------
#Get local user name to define the repository directory.
local_user=$(echo "$USER")

#Get local branch name to define the branch directory.
local_branch=$(git symbolic-ref --short -q HEAD)

#Go to the root of the repository
cd ./$(git rev-parse --show-cdup)

#Grub and manipulate all soft links.
symlink_no=0
for link in $(find -L ./ -xtype l); 
do
    symlink_no=$[symlink_no+1]
    # Grub information from each symlink
    source_file=$(ls -la $link | sed -n 's/^.*[0-9]\+:[0-9]\+\s\(.*\)\s->.*$/\1/p')
    origin_target_file=$(ls -la $link | sed -n 's/.*>\s\+\(.*\)$/\1/p')
    relative_to_root_origin_target_file=$(ls -la $link | sed -n 's/.*>\s.*\(.\/.local.*\)$/\1/p')

    find_origin_user='s/.*local\/\([a-zA-Z0-9._-]\+\).*/\1/p'
    origin_user=$(echo $origin_target_file | sed -n "$find_origin_user")
    find_origin_branch='s/.*local\/'"$origin_user"'\/\([a-zA-Z0-9._-]\+\).*/\1/p' 
    origin_branch=$(echo $origin_target_file | sed -n "$find_origin_branch")

    local_target_file=$(echo $origin_target_file | sed -n 's/local\/'"$origin_user"'\/'"$origin_branch"'/local\/'"$local_user"'\/'"$local_branch"'/p')
    relative_to_root_local_target_file=$(echo $relative_to_root_origin_target_file | sed -n 's/local\/'"$origin_user"'\/'"$origin_branch"'/local\/'"$local_user"'\/'"$local_branch"'/p')

    remove_file_from_target_path='s/\(.*\)\/.*$/\1/p'
    find_local_target_layout=$(echo $relative_to_root_origin_target_file | sed -n "$remove_file_from_target_path" | sed -n 's/.*'"$origin_branch"'\/\(.*$\)/\1/p')
    local_environment_path=$(echo "$local_environment""$find_local_target_layout")

    source_path=$(echo $source_file | sed -n "$remove_file_from_target_path")
    source_check_target=$(echo "$source_path"'/'"$local_target_file")

    #Wording header.
    echo ''
    echo ''
    echo 'Checking for symlink [' "$symlink_no"' ]:' "$source_file"
    echo '------------------------------------------------------'
    echo ''

    # ------------------- Target File Manipulation ---------------------------------
    #Check if target file does not exist in local environment.
    if [ -f "$relative_to_root_local_target_file" ]; 
    then
        #Check the source file if it is already linked.
        if [ "$local_target_file" == "$origin_target_file" ];
        then
            #Is it a correct link?
            if [ -f $source_check_target ];
            then
                echo 'symlink "' "$source_file" '->' "$local_target_file" '" already exists and it is correct.'
            else
                echo '*********** This is a broken link: ' "$source_file"
                echo '           **********************'
            fi
        else
            #Delete existing symlink
            rm "$source_file"

            #Create new symlink according to local environment
            ln -s "$local_target_file" "$source_file"
            echo 'symlink "' "$source_file" '->' "$local_target_file" '" has been created.'
        fi
    else
        echo 'The source file does not exist: ' "$relative_to_root_local_target_file"
    fi
done

【讨论】:

    【解决方案2】:

    最后,解决方案由 virtualenvs 提供。

    Marina Mele's 是一篇很好的文章,它解释了在 django 开发中如何以及为什么应该使用不同的 virtualenvs。

    unique 文件对应于一个 virtualenv,并且它们有一个指向目录 .local/path/to/unique/file 的符号链接,每次激活环境时都会更新该目录。

    例如,如果我使用不同的数据库进行开发、测试和生产,为了处理我的项目需要的不同 migrations 文件夹,我将执行以下操作:

    ~/.virtualenvs/&lt;myvirtualenv&gt;/bin/activate 我会附加命令:

    cd /path/to/git/repository/of/my/project && manage_migrations <myvirtualenv>
    for item in $(find ./ ! -type f -name "migrations" ! -path "./.git/*" ! -path "./.local/*" ); do touch "$item"/__init__.py; done
    

    在哪里manage_migrations

    (在 virtualenv 激活时,它会更新每个 migrations 文件夹的符号链接的脚本。例如

    ./app/migrations -&gt; .local/&lt;myvirtualenv&gt;/app/migrations

    #!/bin/bash
    # vim: filetype=sh
    
    virtualenv_name=$1
    repository_root=$(git rev-parse --show-toplevel)
    local_environment="$repository_root"/.local/"$virtualenv_name"
    
    mkdir -p "$local_environment"
    cd "$repository_root"
    for item in $(find ./ ! -type f -name "migrations" ! -path "./.git/*" ! -path "./.local/*" )
    do 
        if [ -f $item ]
        then
            echo "$item is a file, this is a plain burdain."
            exit
        elif [ ! -L $item ]
        then
            rsync -vaR --delete "$item" "$local_environment"/
            rm -Rf $item
        fi
        rm "$item" > /dev/null 2>&1
        mkdir -p "$local_environment"/"${item#./}"
        ln -s "$local_environment"/"${item#./}" "$item"
    done
    

    我已经在以下存储库中发布了一个基于 git-hooks 的方法来解决这个问题:

    禁用链接

    但是,这似乎不是一个可行的解决方案。

    建议使用symlinksMerge Strategieslocal config files 覆盖默认值。

    在信任 Merge Strategies 之前,请注意this issue

    【讨论】:

      猜你喜欢
      • 2023-01-13
      • 2011-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-06
      • 2010-09-30
      • 2012-07-30
      相关资源
      最近更新 更多