|
|
@@ -0,0 +1,260 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+$confFile = __DIR__ . '/gitsync.ini';
|
|
|
+$argk = '';
|
|
|
+
|
|
|
+foreach ($argv as $k => $v) {
|
|
|
+ if ($k == 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ switch (strtolower($v)) {
|
|
|
+ case '-?':
|
|
|
+ case '--help':
|
|
|
+ case '-h':
|
|
|
+ help();
|
|
|
+ case '--conf':
|
|
|
+ $argk = 'confFile';
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ $confs[$argk] = $v;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+if (!empty($confs['confFile'])) {
|
|
|
+ $confFile = $confs['confFile'];
|
|
|
+} elseif (!file_exists($confFile)) {
|
|
|
+ $confFile = 'gitsync.ini';
|
|
|
+ if (!file_exists($confFile)) {
|
|
|
+ help();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+$confs = parse_ini_file($confFile, true);
|
|
|
+
|
|
|
+if (empty($confs['sync']['toPath']) || !file_exists($confs['sync']['toPath']) || !is_dir($confs['sync']['toPath'])) {
|
|
|
+ gexit('sync.toPath not found');
|
|
|
+}
|
|
|
+
|
|
|
+$tmpPath = sys_get_temp_dir();
|
|
|
+if (!file_exists($tmpPath)) {
|
|
|
+ gexit('system temp path not found');
|
|
|
+}
|
|
|
+if (!is_writable($tmpPath)) {
|
|
|
+ gexit('system temp path not writable');
|
|
|
+}
|
|
|
+
|
|
|
+if (!empty($confs['git']['path'])) {
|
|
|
+ if (!file_exists($confs['git']['path']) || !is_dir($confs['git']['path'])) {
|
|
|
+ gexit('git.path not found');
|
|
|
+ }
|
|
|
+ $dotGitPath = $confs['git']['path'] . '/.git';
|
|
|
+ if (!file_exists($dotGitPath) || !is_dir($dotGitPath)) {
|
|
|
+ gexit('git.path: ".git" not found');
|
|
|
+ }
|
|
|
+ $gitConfig = $dotGitPath . '/config';
|
|
|
+ if (!file_exists($gitConfig)) {
|
|
|
+ gexit('git.path: ".git/config" not found');
|
|
|
+ }
|
|
|
+ $gitConfs = parse_ini_file($gitConfig, true);
|
|
|
+ if (empty($gitConfs['remote origin']['url'])) {
|
|
|
+ gexit('git.path: git url not found in ".git/config"');
|
|
|
+ }
|
|
|
+ $gitUrl = $gitConfs['remote origin']['url'];
|
|
|
+} elseif (!empty($confs['git']['url'])) {
|
|
|
+ $gitUrl = $confs['git']['url'];
|
|
|
+} else {
|
|
|
+ gexit('git url not found');
|
|
|
+}
|
|
|
+
|
|
|
+if (!empty($confs['git']['user']) && !empty($confs['git']['password'])) {
|
|
|
+ $e = parse_url($gitUrl);
|
|
|
+ $gitUrl = $e['scheme'] . '://' . rawurlencode($confs['git']['user']) . ':' . rawurlencode($confs['git']['password']) . '@' . $e['host'] . $e['path'];
|
|
|
+}
|
|
|
+
|
|
|
+if (!function_exists('exec')) {
|
|
|
+ gexit('exec() function not found');
|
|
|
+}
|
|
|
+
|
|
|
+$branch = !empty($confs['git']['branch']) ? $confs['git']['branch'] : 'master';
|
|
|
+
|
|
|
+$tmpPath = $tmpPath . '/gitsync/' . md5($gitUrl . '.' . $branch);
|
|
|
+if (!is_dir($tmpPath)) {
|
|
|
+ @mkdir($tmpPath, 0777, true);
|
|
|
+ if (!file_exists($tmpPath)) {
|
|
|
+ gexit('system temp path not found');
|
|
|
+ }
|
|
|
+ if (!is_writable($tmpPath)) {
|
|
|
+ gexit('system temp path not writable');
|
|
|
+ }
|
|
|
+
|
|
|
+ $output = $ret = null;
|
|
|
+ $cmd = 'git clone -b ' . $branch . ' ' . $gitUrl . ' ' . $tmpPath;
|
|
|
+ exec($cmd, $output, $ret) . "\n";
|
|
|
+ if ($ret) {
|
|
|
+ gexit('git failed: ' . print_r($output, 1) . ' ' . $ret);
|
|
|
+ }
|
|
|
+} else {
|
|
|
+ chdir($tmpPath);
|
|
|
+ $cmd = 'git fetch --all';
|
|
|
+ $output = $ret = null;
|
|
|
+ exec($cmd, $output, $ret);
|
|
|
+ echo implode("\n", $output) . "\n";
|
|
|
+ if ($ret) {
|
|
|
+ gexit('git failed: ' . print_r($output, 1) . ' ' . $ret);
|
|
|
+ }
|
|
|
+ $output = $ret = null;
|
|
|
+ $cmd = 'git reset --hard origin/' . $branch;
|
|
|
+ exec($cmd, $output, $ret);
|
|
|
+ echo implode("\n", $output) . "\n";
|
|
|
+ if ($ret) {
|
|
|
+ gexit('git failed: ' . print_r($output, 1) . ' ' . $ret);
|
|
|
+ }
|
|
|
+ $output = $ret = null;
|
|
|
+ $cmd = 'git pull';
|
|
|
+ exec($cmd, $output, $ret);
|
|
|
+ echo implode("\n", $output) . "\n";
|
|
|
+ if ($ret) {
|
|
|
+ gexit('git failed: ' . print_r($output, 1) . ' ' . $ret);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+$syncFromPath = $tmpPath . (!empty($confs['sync']['fromPath']) ? '/' . $confs['sync']['fromPath'] : '');
|
|
|
+$syncToPath = $confs['sync']['toPath'];
|
|
|
+
|
|
|
+echo $syncFromPath . ' > ' . $syncToPath . "\n";
|
|
|
+echo "...\n";
|
|
|
+
|
|
|
+$_ENV['_md5data_root_'] = $syncFromPath;
|
|
|
+$_ENV['_md5data_'] = [];
|
|
|
+$_ENV['_skipdata_'] = $confs['sync']['skipFiles'] ?? [];
|
|
|
+fetchFiles($syncFromPath);
|
|
|
+copyFiles($syncToPath);
|
|
|
+if (!empty($confs['sync']['clearPaths'])) {
|
|
|
+ $_ENV['_todata_root_'] = $confs['sync']['toPath'];
|
|
|
+ foreach ($confs['sync']['clearPaths'] as $path) {
|
|
|
+ if (substr($path, 0, 1) == ':') {
|
|
|
+ $path = substr($path, 1);
|
|
|
+ $recursion = false;
|
|
|
+ } else {
|
|
|
+ $recursion = true;
|
|
|
+ }
|
|
|
+ $path == '/' && $path = '';
|
|
|
+ clearFiles($confs['sync']['toPath'] . $path, $recursion);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+gexit('Done');
|
|
|
+
|
|
|
+function help() {
|
|
|
+
|
|
|
+ echo <<<Help
|
|
|
+Git 文件同步工具 1.0
|
|
|
+====================
|
|
|
+用法: php gitsync.php [-?/--help/-h] [--conf <file>]
|
|
|
+选项:
|
|
|
+ -?/--help/-h 帮助
|
|
|
+ -conf <file> 配置文件。默认文件名为 gitsync.ini,可放置在当前目录或者脚本所在目录
|
|
|
+
|
|
|
+gitsync.ini 说明
|
|
|
+--------------------
|
|
|
+[git]
|
|
|
+path = "path"
|
|
|
+url = "https://gitUrl"
|
|
|
+user = "user"
|
|
|
+password = "password"
|
|
|
+branch = "branch1"
|
|
|
+
|
|
|
+[sync]
|
|
|
+fromPath = "path1"
|
|
|
+toPath = "path2"
|
|
|
+skipFiles[] = "/file1"
|
|
|
+clearPaths[] = "/path1"
|
|
|
+--------------------
|
|
|
+git.path (可选)git 仓库的根目录,必须是包含 .git 文件夹的目录
|
|
|
+git.url (可选)git 仓库的地址,如果填写了 git.path 可不填写此参数
|
|
|
+git.user (可选)git 仓库的用户名,如果 git.path 或者 git.url 包含了账号信息可不填写此参数
|
|
|
+git.password (可选)git 仓库的密码,填写了 git.user 则必须填写此参数
|
|
|
+git.branch (可选)git 仓库的分支,默认为 master
|
|
|
+sync.fromPath (可选)如果从 git 仓库根目录开始同步则可不填写此参数
|
|
|
+sync.toPath 同步的目标文件夹,绝对路径
|
|
|
+sync.skipFiles (可选)同步时跳过的文件,"/" 开头
|
|
|
+sync.clearPaths (可选)清理文件夹中的额外文件夹,"/" 开头,不递归查找以 ":" 开头,如:":/" 为仅清理根文件夹
|
|
|
+Help;
|
|
|
+
|
|
|
+ gexit(0);
|
|
|
+}
|
|
|
+
|
|
|
+function gexit($code = '') {
|
|
|
+ echo $code;
|
|
|
+ echo "\n\n";
|
|
|
+ exit;
|
|
|
+}
|
|
|
+
|
|
|
+function fetchFiles($path) {
|
|
|
+ foreach (glob($path . '/*') as $file) {
|
|
|
+ if (is_dir($file)) {
|
|
|
+ fetchFiles($file);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $_ENV['_md5data_'][str_replace($_ENV['_md5data_root_'], '', $file)] = md5_file($file);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function copyFiles($path) {
|
|
|
+ foreach ($_ENV['_md5data_'] as $k => $md5) {
|
|
|
+ if (!empty($_ENV['_skipdata_']) && in_array($k, $_ENV['_skipdata_'])) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $file = $_ENV['_md5data_root_'] . $k;
|
|
|
+ $toFile = $path . $k;
|
|
|
+ if (!file_exists($toFile)) {
|
|
|
+ @mkdir(dirname($toFile), 0777, true);
|
|
|
+ $_dir = dirname($toFile);
|
|
|
+ if (!is_dir($_dir) || !is_writable($_dir)) {
|
|
|
+ gexit('copy file failed, dir not writable: ' . $toFile);
|
|
|
+ }
|
|
|
+ copy($file, $toFile);
|
|
|
+ } else {
|
|
|
+ $toMd5 = md5_file($toFile);
|
|
|
+ if ($md5 != $toMd5) {
|
|
|
+ copy($file, $toFile);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $toMd5 = md5_file($toFile);
|
|
|
+ if ($md5 != $toMd5) {
|
|
|
+ unlink($toFile);
|
|
|
+ copy($file, $toFile);
|
|
|
+ $toMd5 = md5_file($toFile);
|
|
|
+ }
|
|
|
+ if ($md5 != $toMd5) {
|
|
|
+ $toFileBackup = $toFile . '._bak_';
|
|
|
+ @unlink($toFileBackup);
|
|
|
+ rename($toFile, $toFileBackup);
|
|
|
+ if (!file_exists($toFileBackup)) {
|
|
|
+ gexit('copy file failed, file not writable: ' . $toFileBackup);
|
|
|
+ }
|
|
|
+ copy($file, $toFile);
|
|
|
+ if (file_exists($toFile)) {
|
|
|
+ @unlink($toFileBackup);
|
|
|
+ }
|
|
|
+ $toMd5 = md5_file($toFile);
|
|
|
+ }
|
|
|
+ if ($md5 != $toMd5) {
|
|
|
+ gexit('copy file failed, md5 not match: ' . $toFile);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function clearFiles($path, $recursion = true) {
|
|
|
+ foreach (glob($path . '/*') as $file) {
|
|
|
+ if (is_dir($file)) {
|
|
|
+ $recursion && clearFiles($file);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $key = str_replace($_ENV['_todata_root_'], '', $file);
|
|
|
+ if (empty($_ENV['_md5data_'][$key])) {
|
|
|
+ echo 'delete: ' . $file . "\n";
|
|
|
+ unlink($file);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|