git submodule 使用

git submodule应用

开发软件时,自己开发的某个项目/模块可能依赖于其他模块或第三方库。自己那部分代码是一个独立代码仓库,并不想把依赖的模块或第三方库直接放到代码库中,但是本地编译的时候又需要用到。此时,希望在本地能实现自动将依赖的模块或第三方库拉取到项目指定目录中。

这里介绍一种适用于git作为版本管理工具的常用方法:使用git submodule。

应用场景:

目前代码仓库有一个模块A,仓库地址:projectA.git,它需要引用另一个模块B,仓库地址:projectB.git。
假设模块A的本地目录:projectA
目的:希望引用模块B作为模块A的子模块,在模块A目录下的路径为:projectA/projectB

下面通过git submodule机制实现。

通过git submodule为模块A引用子模块B

1)引用模块B作为模块A的子模块

cd projectA
git submodule add projectB.git projectB # 如果失败, 可能提示加上选项-f, 就在add后加上

注意:该submodule的子目录指定时不能以"/"结尾,如上面命令,不能写成"projectB/"。

此时,可以在projectA目录下执行git status命令,可以看到会生成1个文件".gitmodules"和一个目录"projectB"。

2)提交、推送修改到模块A对应仓库
利用git commit命令和git push命令,将步骤1)将模块B添加为模块A的子模块的结果push到模块A的代码仓库中。

git commit -m "" # commit修改到本地分支
git push origin master # 推送到远程仓库

git clone包含子模块B的代码仓库A

其他人或其他机器如何从远程仓库拉取模块A的代码仓库,同时抓取到依赖的模块B?
可使用如下方法:

git clone projectA.git
cd projectA
git submodule init
git submodule update

后面两条git submodule命令可以替换为

git submodule update --init --recurisive

也可以在用git clone命令时,加上-recurse-submodules或-recursive递归参数

git clone --recursive projectA.git

这里在GitHub找到一个实际的项目用到git submodule机制,可以参考:https://github.com/continental/fineftp-server

git submodule工作原理

拉取子模块代码

拉取代码库的子模块时,调用

git submodule init
git submodule update
git submodule init 用来初始化本地配置文件,将.git/config中关于[submodule]的部分拷贝到.gitmodules文件中。
git submodule update 根据项目的.gitmodules文件,抓取远程仓库的代码。

如果在git clone项目时,加上--recursive参数,就会自动初始化并更新仓库中的每个子模块。

添加子模块

为一个项目模块添加依赖的子模块,会在项目根目录下生成.gitmodules文件,可用于后续拉取、更新子模块代码。

git clone {母模块git仓库地址}
cd {母模块项目路径}
git submodule add {子模块git仓库地址} {子模块名称}

删除子模块

既然有添加子模块,也有删除子模块。

步骤:

rm -rf 子模块目录 删除子模块目录及源码
vi .gitmodules 删除项目目录下.gitmodules文件中子模块相关条目
vi .git/config 删除配置项中子模块相关条目
rm -rf .git/module/* 删除模块下的子模块目录,每个子模块对应一个目录,注意只删除对应的模块目录即可

git rm --cached 子模块名称  清除git缓存

如果还是提示错误,直接删掉.gitmodules文件即可

rm .gitmodules

如果还是提示错误,实在没办法情况下,还可以尝试直接删掉.git所有内容,然后重新建git仓库或者从远程拉取(慎重操作)

rm -rf .git/*

实战:为项目添加依赖库non-boost asio

环境

OS:Win10x64 专业版
运行环境:WSL,Ubuntu 20
IDE: VS 2022 Community
项目构建工具:CMake 3.8

asio依赖包

我们知道asio分为两个版本,包含boost的boost.Asio,和不包含boost的non-Boost.Asio。本文使用non-Boost.Asio。
官网:http://think-async.com/Asio/index.html
源码地址:https://github.com/chriskohlhoff/asio/
仓库git地址:https://github.com/chriskohlhoff/asio.git

使用git submodule机制为项目添加asio依赖支持

1)新建项目CMake管理的项目
项目自定义名称“boost_asio_study”

2)在项目根目录下新建目录“thridparty”

3)为项目添加第三方依赖包asio
运行终端,进入项目所在根目录,执行git submodule add命令

$ cd F:\workspace\visual_studio_2022\boost_asio_study
$ git submodule add https://github.com/chriskohlhoff/asio.git thirdparty/asio

注意: https://github.com/chriskohlhoff/asio.git 是上面github对应non-boost asio的git仓库地址,thirdparty下asio目录无需事先创建。

执行成功后,会自动在项目目录下生成.gitmodules文件,其内容如下:

4)设置asio包路径
我们使用一个专门的文件Findasio.cmake放到cmake目录下,用于查找asio库路径,并设置生成library,以及做必要的检测。

根目录下CMake文件(./CMakeLists.txt),为CMake内置变量CMAKE_MODULE_PATH添加cmake搜索路径:

cmake_minimum_required (VERSION 3.8)
set (CMAKE_CXX_STANDARD 11)

list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
message(STATUS "Current Source Path: ${CMAKE_CURRENT_SOURCE_DIR}")
project ("boost_asio_study")

add_subdirectory ("boost_asio_study")

这样,CMake内find_package寻找asio 包的时候,会自动去这个目录下找名为Findasio.cmake的文件

5)添加cmake/Findasio.cmake文件
Findasio.cmake内容如下

find_path(asio_INCLUDE_DIR
  NAMES asio.hpp
  HINTS
    "${CMAKE_CURRENT_LIST_DIR}/../thirdparty/asio/asio/include"
  NO_DEFAULT_PATH
  NO_CMAKE_FIND_ROOT_PATH
)

if(asio_INCLUDE_DIR-NOTFOUND)
  message(FATAL_ERROR "Could not find asio library")
  set(asio_FOUND FALSE)
else()
  set(asio_FOUND TRUE)
  set(ASIO_INCLUDE_DIR ${asio_INCLUDE_DIR})
endif()

if(asio_FOUND)
  include(FindPackageHandleStandardArgs)
  find_package_handle_standard_args(asio
    REQUIRED_VARS asio_INCLUDE_DIR)
  if(NOT TARGET asio::asio)
    set(asio_INCLUDE_DIRS ${asio_INCLUDE_DIR})
    add_library(asio::asio INTERFACE IMPORTED)
    message(STATUS "asio::asio dir:${asio_INCLUDE_DIR}")
    set_target_properties(asio::asio PROPERTIES
      INTERFACE_INCLUDE_DIRECTORIES ${asio_INCLUDE_DIR}
      INTERFACE_COMPILE_DEFINITIONS ASIO_STANDALONE)
    mark_as_advanced(asio_INCLUDE_DIR)
  endif()
endif()

注意:该文件会生成asio::asio library,目标程序中需要链接该库。

6)在自己的源码CMake中,寻找asio包

./boost_asio_study/CMakeLists.txt,注意两个find_package指令,确保依赖包存在

cmake_minimum_required (VERSION 3.8)
project(boost_asio_study)

find_package(Threads REQUIRED)
find_package(asio REQUIRED)

add_subdirectory(echoserver)

./boost_asio_study/echoserver/CMakeLists.txt

add_executable(echoserver echoserver.cpp)
target_link_libraries(echoserver asio::asio pthread)

./boost_asio_study/echoserver/echoserver.cpp是自定义源码文件,需要链接编译生成的asio::asio库,pthread是linux下才需要,Windows下不需要。

7)编写编码echoserver.cpp,使用asio库
我们以官方源码阻塞tcp echo 服务器demo(blocking_tcp_echo_server)为例
https://www.boost.org/doc/libs/1_79_0/doc/html/boost_asio/example/cpp11/echo/blocking_tcp_echo_server.cpp

echoserver.cpp:

#include <iostream>
#include <string>
#include <asio.hpp> 
#include <thread>

using namespace std;
using asio::ip::tcp;

const int max_length = 1024;

void session(tcp::socket sock)
{
       try {
              for (; ;) {
                     char data[max_length];
                     asio::error_code error;
                     size_t length = sock.read_some(asio::buffer(data), error);
                     if (error == asio::error::eof) {
                           break; // Connectoin closd cleanly by peer.
                     }
                     else if (error) {
                           throw asio::system_error(error); // Some other error.
                     }
                     asio::write(sock, asio::buffer(data, length));
              }
       }
       catch (std::exception& e) {
              std::cerr << "Exception in thread: " << e.what() << "\n";
       }
}

void server(asio::io_context& io_context, unsigned short port)
{
       tcp::acceptor a(io_context, tcp::endpoint(tcp::v4(), port));
       for (; ;) {
              std::thread(session, a.accept()).detach();
       }
}

int main(int argc, char* argv[])
{
       unsigned short port = 2008;
       try {
              //if (argc != 2) {
              //     std::cerr << "Usage: blocking_tcp_echo_server <port>\n";
              //     return 1;
              //     asio::io_context io_context;
              //     server(io_context, std::atoi(argv[1]));
              //}
              asio::io_context io_context;
              server(io_context, port);
       }
       catch (std::exception& e) {
              std::cerr << "Exception: " << e.what() << "\n";
       }
       return 0;
}

注意:这里可以直接使用#include <asio.hpp>了。

8)测试
启动echo server后,终端使用nc工具连接echo server

$ nc 127.0.0.1 2008

接着输入任意字符串内容,发现可以收到服务器返回的相同字符串。

参考

https://www.jianshu.com/p/5ec0e413c2f0

https://stackoverflow.com/questions/44366417/what-is-the-point-of-git-submodule-init

https://stackoverflow.com/questions/3796927/how-to-git-clone-including-submodules

https://git-scm.com/docs/git-submodule

https://blog.csdn.net/xuanwolanxue/article/details/80609986

https://blog.csdn.net/guotianqing/article/details/82391665

posted @ 2022-05-21 22:37  明明1109  阅读(2946)  评论(0编辑  收藏  举报