目录

挑战

什么是开发模式?

什么是Git子模块?

设置项目

设置commonlib的Git存储库

设置myproj的Git存储库

设置Git子模块

将存储库添加为子模块

更新子模块

使用Git子模块设置开发模式

工作流

设置阶段

工作阶段

结论


作为一个软件工程师,我们在一个依赖于另一个同时也在工作的项目上工作的情况并不少见。场景可能如下:

我们有两个项目,每个项目都有其Git存储库:

  • 例如commonlib,许多项目使用的通用库。该库是独立的,并具有其测试套件和文档。
  • myproj是依赖commonlib的项目。

当我们在myproj上工作时,我们可能还需要同时进行更新commonlib。如果commonlib和myproj同时碰巧是Python项目,我们可以使用安装工具(setuptools)的发展模式(开发模式)和Git的子模块(子模块),使工作更容易。本文演示了如何使用开发模式和子模块来处理这种情况。希望需要处理此类示例的人员可以发现本文有所帮助。

commonlib和myproj在本文的其余部分用作示例,并且该示例假定代码在具有以下条件的虚拟环境中运行:

  • Ubuntu 18.04
  • Python 3.7
  • Git 2.17

挑战

首先,对于Python项目开发,我们通常首先设置一个虚拟环境,并将所有依赖项安装到虚拟环境中。然后,在这种情况下,我们开始进行myproj项目。但是,myproj需要commonlib,我们也在同一时间对其进行处理。如果commonlib以正常方式安装,例如,pip install,我们将无法使用Git来跟踪commonlib的更改。这是开发模式来解决的问题。

其次,commonlib被许多项目使用,包括myproj。一方面,在开发过程中,myproj可能需要坚持使用commonlib的特定版本或分支。另一方面,其他项目可能需要不同版本的commonlib。另外,为了确保在处理myproj时使用正确的commonlib分支或版本,可以将依赖项设置为Git子模块。

什么是开发模式?

开发模式允许安装和编辑项目。

通常,我们从PyPi上安装Python包。

$ pip install <package_name>  

或者,我们从本地软件包安装它。

$ pip install <path_to_local_archive>  

无论哪种方式,软件包都将安装到我们的(虚拟)环境中。例如,当我们将Python软件包安装到虚拟环境中时,该软件包将被复制到/virtual_environment/lib/python3.7/site-packages/。如果要安装commonlib到虚拟环境中,可以执行以下操作:

$ git clone https://github.com/shunsvineyard/commonlib.git
$ pip install commonlib/

安装后,commonlib将在site-packages文件夹中显示为已安装的软件包。我们可以使用ls命令来检查它。例如,结果可能如下所示:

(demo_env) shunsvineyard@remote-ubuntu:~$ ls -l   demo_env/lib/python3.7/site-packages/
total 40
drwxrwxr-x  2 shunsvineyard   shunsvineyard 4096 Dec 23 05:00 __pycache__
drwxrwxr-x  3 shunsvineyard   shunsvineyard 4096 Dec 23 05:01 commonlib
drwxrwxr-x  2 shunsvineyard   shunsvineyard 4096 Dec 23 05:01 commonlib-0.0.1.egg-info
-rw-rw-r--  1 shunsvineyard   shunsvineyard  126 Dec 23 05:00 easy_install.py
drwxrwxr-x 11 shunsvineyard   shunsvineyard 4096 Dec 23 05:00 pip
drwxrwxr-x  2 shunsvineyard   shunsvineyard 4096 Dec 23 05:00 pip-9.0.1.dist-info
drwxrwxr-x  5 shunsvineyard   shunsvineyard 4096 Dec 23 05:00 pkg_resources
drwxrwxr-x  2 shunsvineyard   shunsvineyard 4096 Dec 23 05:00 pkg_resources-0.0.0.dist-info
drwxrwxr-x  6 shunsvineyard   shunsvineyard 4096 Dec 23 05:00 setuptools
drwxrwxr-x  2 shunsvineyard   shunsvineyard 4096 Dec 23 05:00 setuptools-39.0.1.dist-info

开发模式会创建一个从程序包到虚拟环境的链接。在开发模式下,可以以允许我们在安装后编辑代码的方式安装Python软件包。因此,当我们对代码进行任何更改时,该更改将在虚拟环境中立即生效。

要将Python软件包安装为开发模式,请使用以下命令

$ pip install -e <path to the package>  

以commonlib为例,结果可能如下所示:

(demo_env) shunsvineyard@remote-ubuntu:~$ pip install -e commonlib/
Obtaining file:///home/shunsvineyard/commonlib
Installing collected packages: commonlibRunning setup.py develop for commonlib
Successfully installed commonlib
(demo_env) shunsvineyard@remote-ubuntu:~$ ls -l demo_env/lib/python3.7/site-packages/
total 40
drwxrwxr-x  2 shunsvineyard shunsvineyard 4096 Dec 23 05:08 __pycache__
-rw-rw-r--  1 shunsvineyard shunsvineyard   31 Dec 23 05:09 commonlib.egg-link
-rw-rw-r--  1 shunsvineyard shunsvineyard   30 Dec 23 05:09 easy-install.pth
-rw-rw-r--  1 shunsvineyard shunsvineyard  126 Dec 23 05:08 easy_install.py
drwxrwxr-x 11 shunsvineyard shunsvineyard 4096 Dec 23 05:08 pip
drwxrwxr-x  2 shunsvineyard shunsvineyard 4096 Dec 23 05:08 pip-9.0.1.dist-info
drwxrwxr-x  5 shunsvineyard shunsvineyard 4096 Dec 23 05:08 pkg_resources
drwxrwxr-x  2 shunsvineyard shunsvineyard 4096 Dec 23 05:08 pkg_resources-0.0.0.dist-info
drwxrwxr-x  6 shunsvineyard shunsvineyard 4096 Dec 23 05:08 setuptools
drwxrwxr-x  2 shunsvineyard shunsvineyard 4096 Dec 23 05:08 setuptools-39.0.1.dist-info

如果打开文件commonlib.egg-link,我们将看到它链接到的位置。例如,

(demo_env) shunsvineyard@remote-ubuntu:~$ cat demo_env/lib/python3.7/site-packages/commonlib.egg-link
/home/shunsvineyard/commonlib

请注意,开发模式仅适用于本地项目或VCS URL。如果我们尝试以开发模式从PyPi安装软件包,则会显示以下错误消息。使用numpy为例,

$ pip install -e numpy
numpy should either be a path to a local project or a VCS url beginning with svn+, git+, hg+, or bzr+

什么是Git子模块?

Git子模块是另一个Git存储库中的Git存储库。就像一个Git存储库引用了另一个Git存储库一样。例如,myproj对commonlib有依赖。如果commonlib是myproj的Git子模块,下图说明了它们之间的关系。

Git子模块允许我们将Git存储库保留为另一个Git存储库的子目录。当我们执行git clone myproj时,Myproj子模块引用commonlib中定义的的特定版本将从commonlib存储库下载。这样,我们可以将另一个存储库(即commonlib)克隆到我们的项目(即myproj)中,并使提交分开。

以下各节以commonlib和myproj作为示例来演示开发模式和子模块的设置和工作流程。以下各节还假设我们从头开始做所有事情,包括设置Git存储库。

设置项目

假设commonlib提供了一个非常简单且唯一的功能:greeting。项目布局和代码如下所示:

commonlib/
├── LICENSE
├── README.rst
├── commonlib
│   ├── __init__.py
│   └── greeting.py
└── setup.py

greeting.py

def greeting(name: str):"""Print a simple greeting with the name."""print(f"Howdy, {name}")

setup.py

import pathlib
import setuptools# The directory containing this file
HERE = pathlib.Path(__file__).parent# The text of the README file
README = (HERE / "README.rst").read_text()# This call to setup() does all the work
setuptools.setup(name="commonlib",version="0.0.1",description="A simple Python package",long_description=README,long_description_content_type="text/x-rst",author="Author Name",author_email="author@email.com",license="MIT",classifiers=["License :: OSI Approved :: MIT License","Programming Language :: Python"],packages=setuptools.find_packages(),python_requires=">=3.7"
)

(commonlib的完整示例可以在https://github.com/shunsvineyard/commonlib中找到)

现在,我们准备设立两个Git仓库commonlib和myproj。在此之前,我们需要设置一个Git服务器。此示例使用本地主机(即127.0.0.1)作为Git服务器。

$ sudo useradd git
$ sudo passwd git
$ su git
$ cd ~
$ git init --bare commonlib
$ git init --bare myproj

设置commonlib的Git存储库

拥有Git服务器之后,我们可以将现有commonlib服务器添加到Git服务器中。返回到本地用户。

user:~$ cd commonlib/
user:~/commonlib$ git init
user:~/commonlib$ git add –all
user:~/commonlib$ git commit -a -m "Initialize commonlib repository"
user:~/commonlib$ git remote add origin git@127.0.0.1:commonlib
user:~/commonlib $ git push -u origin master

设置myproj的Git存储库

对于myproj,我们可以做类似commonlib的事情。项目布局和代码如下:

myproj/
├── LICENSE
├── README.rst
├── app.py
└── setup.py

app.py

from commonlib import greetingdef run():greeting.greeting("Git Submodule")if __name__ == "__main__":run()

setup.py

import pathlib
import setuptools# The directory containing this file
HERE = pathlib.Path(__file__).parent# The text of the README file
README = (HERE / "README.rst").read_text()# This call to setup() does all the work
setuptools.setup(name="myproj",version="0.0.1",description="A simple Python project",long_description=README,long_description_content_type="text/x-rst",url="https://github.com/shunsvineyard/myproj",author="Author Name",author_email="author@email.com",license="MIT",classifiers=["License :: OSI Approved :: MIT License","Programming Language :: Python"],packages=setuptools.find_packages(),python_requires=">=3.7"
)

然后,将现有代码添加到Git服务器。

user:~$ cd myproj/
user:~/myproj$ git init
user:~/myproj$ git add –all
user:~/myproj$ git commit -a -m "Initialize myprojrepository"
user:~/myproj$ git remote add origin git@127.0.0.1: myproj
user:~/myproj$ git push -u origin master

设置Git子模块

尽管Git子模块为各种情况提供了许多功能,但使用最多的两个用例是:1.将存储库添加为子模块;以及2.更新子模块。

将存储库添加为子模块

通过以下命令可以简单地将现有存储库添加为另一个存储库的子模块:

user:~$ cd myproj/
user:~/myproj$ git submodule add git@127.0.0.1:commonlib
user:~/myproj$ git submodule init
user:~/myproj$ git commit -a -m "Add commonlib as submodule"
user:~/myproj$ git push

添加子模块后,将创建一个子模块引用,即.gitmodules文件。它看起来可能如下所示:

shunsvineyard@remote-ubuntu:~/workspace/myproj$ ls -al
total 40
drwxrwxr-x  4 shunsvineyard shunsvineyard 4096 Dec 20 07:20 .
drwxrwxr-x 10 shunsvineyard shunsvineyard 4096 Dec 20 06:47 ..
drwxrwxr-x  9 shunsvineyard shunsvineyard 4096 Dec 20 07:22 .git
-rw-rw-r--  1 shunsvineyard shunsvineyard 1233 Dec 20 06:44 .gitignore
-rw-rw-r--  1 shunsvineyard shunsvineyard   73 Dec 20 07:20 .gitmodules
-rw-rw-r--  1 shunsvineyard shunsvineyard 1067 Dec 20 06:44 LICENSE
-rw-rw-r--  1 shunsvineyard shunsvineyard  278 Dec 20 06:58 README.rst
-rw-rw-r--  1 shunsvineyard shunsvineyard  123 Dec 20 06:57 app.py
drwxrwxr-x  3 shunsvineyard shunsvineyard 4096 Dec 20 07:20 commonlib
-rw-rw-r--  1 shunsvineyard shunsvineyard  724 Dec 20 06:57 setup.py

如果打开文件.gitmodules,我们可以看到它记录了子模块的信息。

$ cat .gitmodules
[submodule "commonlib"]path = commonliburl = git@127.0.0.1:commonlib

注意:.gitmodules中的子模块的url可以是相对路径。例如,commonlib和myproj都位于Git服务器的同一文件夹中。url可以简化为../commonlib。

如果我们使用Github托管我们的存储库,则子模块可能如下所示:

(示例myproj可以在https://github.com/shunsvineyard/myproj上找到)

更新子模块

通常,在两种情况下,我们可能需要更新子模块:1.由于某些代码更改,因此更新了子模块。2.将子模块更新为较新的或特定的版本。

情况1:由于代码更改而更新子模块

子模块只是另一个Git存储库中的一个Git存储库。当我们在子模块上进行一些代码更改时,我们将执行与通常在常规Git存储库上相同的操作。

例如,我们添加了一个调用greeting2到commonlib的新功能。

greeting.py

def greeting2(name: str):"""Print a simple greeting with the name."""print(f"How are you, {name}?")

我们对子模块执行的操作与常规存储库相同:提交更改并推送更改。

user:~$ cd myproj/commonlib
user:~/myproj/commonlib$ git status
On branch master
Your branch is up to date with 'origin/master'.Changes not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified:   greeting.pyno changes added to commit (use "git add" and/or "git commit -a")user:~/myproj/commonlib$ git commit -a -m "Added a new greeting function."
user:~/myproj/commonlib$ git push

提交并推送子模块的更改后,我们可以看到主项目的子模块引用,即myproj,也已更改,然后我们可以做同样的事情来更新引用。然后,myproj将附加较新的commonlib。

user:~/myproj/commonlib$ cd ../
user:~/myproj$ git status
On branch master
Your branch is up to date with 'origin/master'.Changes not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified:   commonlib (new commits)no changes added to commit (use "git add" and/or "git commit -a")user:~/myproj$ git commit -a -m "Update submodule, commonlib"
user:~/myproj$ git push

情况2:将子模块更新为较新的或特定的版本

当其他人修改commonlib或添加了新功能时,我们可能需要将commonlib子模块更新为较新的版本。

例如,有人就加了一个新的功能greeting3到commonlib。

greeting.py

def greeting3():"""Print a simple greeting with the name."""print("How's going?")

提交哈希7735cf8460acd03f92e7c0529486c86ec83b2c0e如下所示。

user2:~$ git clone git@127.0.0.1:commonlib
user2:~$ cd commonlib
user2:~/commonlib$ vim commonlib/greeting.py # add greeting3 function as the following
user2:~/commonlib$ git commit -a -m "Added greeting3 function."
user2:~/commonlib$ git push
user2:~/commonlib$ git log
commit 7735cf8460acd03f92e7c0529486c86ec83b2c0e (HEAD -> master, origin/master, origin/HEAD)
Author: user2 <user2@email.com>
Date:   Sun Dec 22 00:27:09 2019 +0000Added greeting3 function.

我们将子模块更新为较新版本或特定版本的方法是更新子模块指向的提交哈希。

Git子模块官方文档说:“子模块存储库处于指向特定提交的分离HEAD状态。更改提交仅涉及签出其他标签或提交,然后将更改添加到父存储库。

以下是更新子模块以提交的示例hash7735cf8460acd03f92e7c0529486c86ec83b2c0e。

user:~/myproj$ cd commonlib
user:~/myproj/commonlib$ git pull
user:~/myproj/commonlib$ git checkout 7735cf8460acd03f92e7c0529486c86ec83b2c0e
Note: checking out '7735cf8460acd03f92e7c0529486c86ec83b2c0e'.You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:git checkout -b <new-branch-name>HEAD is now at 7735cf8 Added greeting3 function.
user:~/myproj/commonlib$ cd ..
user:~/myproj$ git status
On branch master
Your branch is up to date with 'origin/master'.Changes not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified:   commonlib (new commits)no changes added to commit (use "git add" and/or "git commit -a")
user:~/myproj$ git commit -a -m "Update submodule, commonlib, to the newer one."
user:~/myproj$ git push

使用Git子模块设置开发模式

开发模式是安装工具(setuptools)提供的功能,因此与编写用于打包Python项目的setup.py没什么不同。但是,当一个Python项目中有另一个Python项目作为子模块,而我们想将该子模块安装为开发模式时,则需要将该子模块添加到主项目的requirements.txt文件中。例如,myproj的requirements.txt可以是以下内容。

# Install commonlib as development mode
-e ./commonlib # Path to the submodule

因此,当我们安装myproj的依赖项时,commonlib将自动安装为开发模式。

工作流

当我们处理包含多个较小项目的大项目时,会同时需要同时处理主项目及其从属项目。在这种情况下,我们通常与其他团队一起工作。针对这种情况的建议工作流程分为两个阶段:设置阶段和工作阶段。

设置阶段

此阶段准备代码和工作环境。

1.创建一个虚拟环境

2.使用 --recurse-submodules下载源代码。--recurse-submodules将下载所有子模块。

$ git clone --recurse-submodules <URL_to_the_repository>

3.签出分支。通常,当我们处理某个功能或修复错误时,我们将为该工作创建一个分支。我们应该避免直接与master(或develop)分支工作。关于此的更多信息可以在https://guides.github.com/introduction/flow/中找到

$ git checkout <branch_name>

4.将依赖项安装到虚拟环境中。

$ pip install -r requirements.txt

工作阶段

这一阶段表明我们正努力解决我们的问题。除了代码更改外,还有两种情况需要修改子模块。

情况1:如果我们需要对子模块进行一些代码更改:

  1. 创建此更改的分支,并为子模块代码更改创建pull请求(PR)。
  2. 在PR被批准并且分支合并之后,将子模块更新为PR刚刚合并的提交。

情况2:某人更新了一个存储库,这是我们的子模块,我们想将该子模块更新为较新的提交:

  1. git pull在子模块文件夹上使用进行更改。
  2. 将子模块的提交哈希更新为所需的哈希。
  3. cd 到主项目并提交子模块的更改

结论

当我们同时从事多个相关项目时,很容易出错。当我们必须在这种情况下工作时,开发模式和子模块提供了一种管理项目的简便方法。一开始使用开发模式和子模块可能并不容易。但是一旦我们熟悉了它的使用,开发模式和子模块的结合不仅可以防止我们犯错误,而且可以提高生产率。

使用Git子模块和开发模式管理Python项目相关推荐

  1. webpack 开发模式管理 Development

    webpack 开发模式管理 Development GitHub 学习 Demo. 接下来将一些开发时的 webpack 配置. warnning : 本指南中的工具仅用于开发,请避免在生产中使用它 ...

  2. Python的IDE:基于Eclipse/MyEclipse软件的PyDev插件配置python的开发环境(不同python项目加载不同版本的python)—从而实现Python编程图文教程之详细攻略

    Python的IDE:基于Eclipse/MyEclipse软件的PyDev插件配置python的开发环境(不同python项目加载不同版本的python)-从而实现Python编程图文教程之详细攻略 ...

  3. python项目开发实例-《Python项目案例开发从入门到实战》PDF版百度网盘

    「教程分享:Python项目开发从入门到实列」 本书例子具有实用性,20个不同类型的完整列子,600分钟高品质配套教学视频,完整的源码和教学课件,让你对枯燥的Python语言学习充满乐趣. 编辑推荐 ...

  4. python项目开发实例书-Python项目开发实战

    本书案例具有实用性,如校园网搜索引擎.小小翻译器.抓取百度图片这些爬虫案例略加修改可以应用实际项目中:还有通过微信通信协议开发微信机器人,机器学习的文本分类.基于卷积神经网络的手写体识别等案例,另外是 ...

  5. “好家园房产中介网后台管理”python项目

    一.语言和环境 1.实现语言:python语言. 2.环境要求:pycharm + mysql. 二.实现功能 使用flask技术开发"好家园房产中介网"的后台管理功能,具体实现功 ...

  6. python项目开发实例集锦-python项目开发案例集锦 mobi|金融租赁公司 业务

    出产线落在深圳,至关水平 上折射出国际 商业航天的"经济地舆".目前,国际 大约有六0家商业航天初创企业,其中大部份会聚于北京--这是航天"国度 队"的传统研发 ...

  7. python 创意项目_针对python开发人员的10个很棒的python项目创意

    python 创意项目 The joy of coding Python should be in seeing short, concise, readable classes that expre ...

  8. 从零开始,教你如何安装、配置Python开发环境,Python入门安装教程,超级详细

    近日,我鼓起勇气从头开始正儿八经地配置一个更轻量.更趁手的 Python 开发环境.经过一番折腾,我比较顺利地在 Windows 10 上配置了一个比较满意的 Python 环境: 安装稳定版本的 P ...

  9. 项目开发流程_绿维文旅:旅游项目开发模式与流程

    一.旅游项目开发模式 旅游综合开发是立足旅游项目自有资源基础,以旅游产业为主导,以市场为导向,以资本为驱动,以资源整合为核心,通过集中土地.资本.技术.交通.劳动力等生产要素,推进土地开发.交通建设. ...

最新文章

  1. AJPFX实列判断一个字符串是不是对称字符串
  2. android socket 长连接_php socket如何实现长连接
  3. 冯珊珊_模拟器企业衡泰信签约冯珊珊,推动高尔夫运动下沉
  4. QT [007] QT UI 的控件操控问题 - 如何操控多嵌套的UI控件
  5. node-gulp插件
  6. linux 云主机安装方法,虚拟主机linux服务器安装教程
  7. 比尔·盖茨退出微软董事会,回顾盖茨与微软的传奇故事
  8. 动软代码生成器v2.78Mysql备注生成完美修复dll
  9. DNK基础之静态库、动态库、编译流程
  10. CSS hover改变背景图片过渡动画生硬
  11. Python脚本刷网页访问量或关键词搜索频率
  12. 触发器一(触发器简介)
  13. 数据可视化,选择Echarts还是Highcharts?
  14. Java虚拟机(jvm)——垃圾收集器与内存分配策略
  15. java中di,初识Spring的DI及其基本用法
  16. CSS:标准文档流、浮动、绝对定位—(解决有时候父元素不能自动扩展)
  17. SpringBoot 微信点餐开源系统,值得一看
  18. 通信电子电路实验(二)—— 高频C类谐振功率放大器的设计与仿真
  19. 为什么用了大牌工具后报表开发依然头疼
  20. scrum回顾_[原创]如何开回顾会议(retrospective meeting)

热门文章

  1. api压测工具_高并发之API接口,分布式,防刷限流,如何做?
  2. jmail mysql_利用VB+jmail发送邮件源码
  3. 2021牛气新年素材模板,你真的不来看一看吗?
  4. 电商优秀设计作品展示
  5. 平面设计师进步素材模板,设计基础!
  6. 数据表字段不存在 php,laravel 使用原生表达式增加原数据表不存在的字段
  7. slam十四讲第二版 pdf_先搞定SLAM,再谈如何抓住下一代互联网产业爆发点!
  8. 《深入浅出DPDK》读书笔记(十四):DPDK应用篇(DPDK与网络功能虚拟化:NFV、VNF、IVSHMEM、Virtual BRAS“商业案例”)
  9. GitHub:Python 强化学习实用指南
  10. Linux Socket C语言网络编程:UDP Socket