monkeye 1 рік тому
коміт
6ea646c2ab
2 змінених файлів з 272 додано та 0 видалено
  1. 12 0
      gitsync.ini
  2. 260 0
      gitsync.php

+ 12 - 0
gitsync.ini

@@ -0,0 +1,12 @@
+[git]
+path = "path"
+url = "https://gitUrl"
+user = "user"
+password = "password"
+branch = "branch1"
+
+[sync]
+fromPath = "path1"
+toPath = "path2"
+skipFiles[] = "/file1"
+clearPaths[] = "/path1"

+ 260 - 0
gitsync.php

@@ -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);
+		}
+	}
+}