版本控制系统,尤其是 Git,是跟踪代码更改、与团队协作以及确保代码库稳定性的重要工具。虽然 Git 主要是为源代码设计的,但您也可以将其与MySQL数据库结合使用,以进行版本控制和架构更改管理。
在本文中,我们将探讨如何使用 Git 挂钩将 Git 与 MySQL 集成以进行版本控制,并以指南格式提供具体示例。清单中给出的所有脚本功能齐全且完整。您可以在测试环境中按顺序重现它们。
首先,我们创建一个测试数据库和用户:
create database testdb_remote; create user 'user_remote'@'localhost' identified WITH mysql_native_password by 'remote123'; grant all on testdb_remote.* to 'user_remote'@'localhost';
接下来,我们将创建一个远程存储库。这可以是任何远程服务器上的存储库,但为了简单起见,我们将在本地创建它。为了方便执行命令,我使用git bash。我的本地机器已经有一个 git 文件夹,所以我使用它:
cd /c/git mkdir testdb.remote cd testdb.remote git init --bare
并创建一个本地存储库作为远程存储库的克隆:
cd /c/git git clone /c/git/testdb.remote testdb.local cd testdb.local git ls-files
存储库中没有文件;让我们创建一个并将更改推送到远程存储库:
echo "Test DB repo" > readme.md git status git add . git commit -m "1st commit" git push
让我们检查一下远程存储库的内容:
cd /c/git/testdb.remote git ls-tree --full-tree -r HEAD
远程存储库中有一个 hooks 文件夹,其中包含几个带有示例的文件:
ls -1 /c/git/testdb.remote/hooks
挂钩是当特定事件发生时执行的脚本。 Git 有客户端和服务器端挂钩。客户端钩子由提交和合并等操作触发。服务器端挂钩在网络操作上运行,例如接收推送的提交。这里详细描述了钩子。实现逻辑有不同的选择;我将给出一个使用post-receive服务器端挂钩的示例。
在 hooks 文件夹中,我们需要创建一个名为“post-receive”的文件,这是一个常规的 bash 脚本:
#!/bin/sh while read oval nval ref do echo List of files changed in the commit: git diff --name-only $oval $nval done
每当推送成功完成时,上述脚本就会在服务器上执行,并输出已修改文件的列表。让我们通过在 readme.md 中添加一行并将更改推送到远程存储库来检查它是如何工作的:
cd /c/git/testdb.local echo "New line" >> readme.md git add . git commit -m "Line added" git push
您可以看到,执行 git push 命令时,输出现在包含以remote:
- 这是在服务器上执行的 post-receive 脚本的输出。
说到基本更改,可以添加、修改和删除文件。您可以采用不同的方法将这些更改应用到数据库:
假设您选择了第二个选项,那么您需要执行已添加或更改的文件。我们可以通过添加参数--diff-filter=AM
来过滤此处描述的此类文件:
#!/bin/sh while read oval nval ref do echo List of files added or changed in the commit: git diff --name-only --diff-filter=AM $oval $nval done
添加几个文件,并再次更改readme.md:
echo "SELECT 1;" > test1.sql echo "SELECT 2;" > test2.sql echo "SELECT 3;" > test3.sql echo "New line 2" >> readme.md git add . git commit -m "New files" git push
挂钩脚本的输出包含 4 个文件:
我们编辑 test1.sql 和 readme.md 文件,删除 test2.sql,并添加另一个文件:
echo "SELECT 11;" > test1.sql echo "New line 2" >> readme.md rm test2.sql echo "SELECT 4;" > test4.sql git add . git commit -m "Modify, remove and add" git push
仅显示修改和添加的文件:
我们的目标是在每次成功推送后执行SQL脚本,因此我们需要过滤仅此类型的文件;在我们的例子中,我们将要求它们都必须具有“.sql”扩展名。要进行过滤,请将参数-- "*.sql"
添加到git diff
命令中:
#!/bin/sh while read oval nval ref do echo List of files added or changed in the commit: git diff --name-only --diff-filter=AM $oval $nval -- "*.sql" done
要执行脚本,我们还必须能够连接到数据库。为此,我们将创建凭据并测试连接:
mysql_config_editor set --login-path=testdb_remote --host=localhost --port=3306 --user=user_remote --password mysql --login-path=testdb_remote --database=testdb_remote
修改我们的脚本以迭代“.sql”文件,执行每个文件,并检查结果。我们还需要对列表输出进行排序,以按所需的顺序执行文件。使用 git show 命令,我们显示 SQL 脚本的内容,并将其通过管道传递给 MySQL 执行。
变量“$?”如果 SQL 脚本执行成功,则包含 0;如果出现错误,则包含另一个值:
#!/bin/sh while read oval nval ref do echo List of files added or changed in the commit: for file in $(git diff --name-only --diff-filter=AM $oval $nval -- "*.sql" | sort); do git show master:${file} | mysql --login-path=testdb_remote --database=testdb_remote echo "FILE: ${file} - result $?" done done
再执行一项测试 - 删除所有先前创建的“.sql”文件并为以下内容创建脚本:
我们还需要为每个文件名添加前缀(1_、2_ 等),以确保文件的所需执行顺序:
rm *.sql echo "DB Initialization" >> readme.md echo " DROP TABLE IF EXISTS customers; CREATE TABLE customers ( id int UNSIGNED NOT NULL AUTO_INCREMENT, name varchar(255) DEFAULT NULL, PRIMARY KEY (id) ); " > 1_customers.sql echo " INSERT INTO customers (id, name) VALUES (1, 'John Doe'), (2, 'Jane Smith') AS new ON DUPLICATE KEY UPDATE customers.name = new.name; " > 2_customers_init.sql echo " DROP PROCEDURE IF EXISTS get_customer; DELIMITER $$ CREATE PROCEDURE get_customer(IN customer_id int UNSIGNED) BEGIN SELECT c.id, c.name FROM customers c WHERE c.id = customer_id; END $$ " > 3_get_customer.sql echo "SELECT FROM customers;" > 4_error_select.sql ls -1
所以我们有四个“.sql”文件需要执行:
我们对存储库进行更改:
并且我们看到,当执行git push
时,文件是顺序执行的,并显示每个文件的执行结果(MySQL命令退出代码)。文件“4_error_select.sql”包含语法错误,因此其执行结果为1。
最后,让我们检查一下数据库中的内容:
mysql --login-path=testdb_remote --database=testdb_remote show tables; call get_customer(1); call get_customer(2);
如您所见,表和过程是在远程数据库中创建的。该过程成功执行并返回数据。
为了提高挂钩脚本输出的可读性,您可以抑制 MySQL CLI 输出或将其重定向到日志文件。您还可以分析MySQL命令执行的结果,并向钩子脚本添加更多逻辑。
例如,您可以在测试数据库上执行 SQL 脚本,然后对其运行一些测试(如我在此处所述)。如果测试在测试数据库上成功完成,请在生产数据库上运行 SQL 脚本,并可能对其运行一些测试。
通过分析每个步骤的结果,您可以创建任何配置的管道。
当然,每种方法都有许多优点和局限性。使用第二种方法,需要确保脚本的执行顺序,因为例如,在创建表之前我们无法将数据插入到表中。还需要确保可以重新执行脚本,即正确处理正在创建的对象已在数据库中或数据库已包含要添加的数据的情况。
当使用版本控制系统时,开发过程变得稍微复杂一些,因为需要另外形式化预定格式的脚本中的更改。但是,您可以通过使用安装文件来实现一定的灵活性。
所描述技术的主要优点是在数据库中轻松实现版本控制,以及实现CI/CD 管道的能力。
要评估这对您是否有用,您可以首先问自己:您直接在生产中编写代码的频率如何?