gitsync.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <?php
  2. $confFile = __DIR__ . '/gitsync.ini';
  3. $argk = '';
  4. foreach ($argv as $k => $v) {
  5. if ($k == 0) {
  6. continue;
  7. }
  8. switch (strtolower($v)) {
  9. case '-?':
  10. case '--help':
  11. case '-h':
  12. help();
  13. case '--conf':
  14. $argk = 'confFile';
  15. break;
  16. default:
  17. $confs[$argk] = $v;
  18. }
  19. }
  20. if (!empty($confs['confFile'])) {
  21. $confFile = $confs['confFile'];
  22. } elseif (!file_exists($confFile)) {
  23. $confFile = 'gitsync.ini';
  24. if (!file_exists($confFile)) {
  25. help();
  26. }
  27. }
  28. $confs = parse_ini_file($confFile, true);
  29. if (empty($confs['sync']['toPath']) || !file_exists($confs['sync']['toPath']) || !is_dir($confs['sync']['toPath'])) {
  30. gexit('sync.toPath not found');
  31. }
  32. $tmpPath = __DIR__;
  33. if (!is_writable($tmpPath)) {
  34. gexit('system path not writable');
  35. }
  36. if (!empty($confs['git']['path'])) {
  37. if (!file_exists($confs['git']['path']) || !is_dir($confs['git']['path'])) {
  38. gexit('git.path not found');
  39. }
  40. $dotGitPath = $confs['git']['path'] . '/.git';
  41. if (!file_exists($dotGitPath) || !is_dir($dotGitPath)) {
  42. gexit('git.path: ".git" not found');
  43. }
  44. $gitConfig = $dotGitPath . '/config';
  45. if (!file_exists($gitConfig)) {
  46. gexit('git.path: ".git/config" not found');
  47. }
  48. $gitConfs = parse_ini_file($gitConfig, true);
  49. if (empty($gitConfs['remote origin']['url'])) {
  50. gexit('git.path: git url not found in ".git/config"');
  51. }
  52. $gitUrl = $gitConfs['remote origin']['url'];
  53. } elseif (!empty($confs['git']['url'])) {
  54. $gitUrl = $confs['git']['url'];
  55. } else {
  56. gexit('git url not found');
  57. }
  58. if (!empty($confs['git']['user']) && !empty($confs['git']['password'])) {
  59. $e = parse_url($gitUrl);
  60. $gitUrl = $e['scheme'] . '://' . rawurlencode($confs['git']['user']) . ':' . rawurlencode($confs['git']['password']) . '@' . $e['host'] . $e['path'];
  61. }
  62. if (!function_exists('exec')) {
  63. gexit('exec() function not found');
  64. }
  65. $branch = !empty($confs['git']['branch']) ? $confs['git']['branch'] : 'master';
  66. $tmpPath = $tmpPath . '/gitsync/' . md5($gitUrl . '.' . $branch);
  67. if (!is_dir($tmpPath)) {
  68. @mkdir($tmpPath, 0777, true);
  69. if (!file_exists($tmpPath)) {
  70. gexit('system temp path not found');
  71. }
  72. if (!is_writable($tmpPath)) {
  73. gexit('system temp path not writable');
  74. }
  75. $output = $ret = null;
  76. $cmd = 'git clone -b ' . $branch . ' ' . $gitUrl . ' ' . $tmpPath;
  77. exec($cmd, $output, $ret) . "\n";
  78. if ($ret) {
  79. gexit('git failed: ' . print_r($output, 1) . ' ' . $ret);
  80. }
  81. } else {
  82. chdir($tmpPath);
  83. $cmd = 'git fetch --all';
  84. $output = $ret = null;
  85. exec($cmd, $output, $ret);
  86. echo implode("\n", $output) . "\n";
  87. if ($ret) {
  88. gexit('git failed: ' . print_r($output, 1) . ' ' . $ret);
  89. }
  90. $output = $ret = null;
  91. $cmd = 'git reset --hard origin/' . $branch;
  92. exec($cmd, $output, $ret);
  93. echo implode("\n", $output) . "\n";
  94. if ($ret) {
  95. gexit('git failed: ' . print_r($output, 1) . ' ' . $ret);
  96. }
  97. $output = $ret = null;
  98. $cmd = 'git pull';
  99. exec($cmd, $output, $ret);
  100. echo implode("\n", $output) . "\n";
  101. if ($ret) {
  102. gexit('git failed: ' . print_r($output, 1) . ' ' . $ret);
  103. }
  104. }
  105. $syncFromPath = $tmpPath . (!empty($confs['sync']['fromPath']) ? '/' . $confs['sync']['fromPath'] : '');
  106. $syncToPath = $confs['sync']['toPath'];
  107. echo $syncFromPath . ' > ' . $syncToPath . "\n";
  108. echo "...\n";
  109. $_ENV['_md5data_root_'] = $syncFromPath;
  110. $_ENV['_md5data_'] = [];
  111. $_ENV['_skipdata_'] = $confs['sync']['skipFiles'] ?? [];
  112. fetchFiles($syncFromPath);
  113. copyFiles($syncToPath);
  114. if (!empty($confs['sync']['clearPaths'])) {
  115. $_ENV['_todata_root_'] = $confs['sync']['toPath'];
  116. foreach ($confs['sync']['clearPaths'] as $path) {
  117. if (substr($path, 0, 1) == ':') {
  118. $path = substr($path, 1);
  119. $recursion = false;
  120. } else {
  121. $recursion = true;
  122. }
  123. $path == '/' && $path = '';
  124. clearFiles($confs['sync']['toPath'] . $path, $recursion);
  125. }
  126. }
  127. gexit('Done');
  128. function help() {
  129. echo <<<Help
  130. Git 文件同步工具 1.0
  131. ====================
  132. 用法: php gitsync.php [-?/--help/-h] [--conf <file>]
  133. 选项:
  134. -?/--help/-h 帮助
  135. -conf <file> 配置文件。默认文件名为 gitsync.ini,可放置在当前目录或者脚本所在目录
  136. gitsync.ini 说明
  137. --------------------
  138. [git]
  139. path = "path"
  140. url = "https://gitUrl"
  141. user = "user"
  142. password = "password"
  143. branch = "branch1"
  144. [sync]
  145. fromPath = "path1"
  146. toPath = "path2"
  147. skipFiles[] = "/file1"
  148. clearPaths[] = "/path1"
  149. --------------------
  150. git.path (可选)git 仓库的根目录,必须是包含 .git 文件夹的目录
  151. git.url (可选)git 仓库的地址,如果填写了 git.path 可不填写此参数
  152. git.user (可选)git 仓库的用户名,如果 git.path 或者 git.url 包含了账号信息可不填写此参数
  153. git.password (可选)git 仓库的密码,填写了 git.user 则必须填写此参数
  154. git.branch (可选)git 仓库的分支,默认为 master
  155. sync.fromPath (可选)如果从 git 仓库根目录开始同步则可不填写此参数
  156. sync.toPath 同步的目标文件夹,绝对路径
  157. sync.skipFiles (可选)同步时跳过的文件,"/" 开头
  158. sync.clearPaths (可选)清理文件夹中的额外文件夹,"/" 开头,不递归查找以 ":" 开头,如:":/" 为仅清理根文件夹
  159. Help;
  160. gexit(0);
  161. }
  162. function gexit($code = '') {
  163. echo $code;
  164. echo "\n\n";
  165. exit;
  166. }
  167. function fetchFiles($path) {
  168. foreach (glob($path . '/*') as $file) {
  169. if (is_dir($file)) {
  170. fetchFiles($file);
  171. continue;
  172. }
  173. $_ENV['_md5data_'][str_replace($_ENV['_md5data_root_'], '', $file)] = md5_file($file);
  174. }
  175. }
  176. function copyFiles($path) {
  177. foreach ($_ENV['_md5data_'] as $k => $md5) {
  178. if (!empty($_ENV['_skipdata_']) && in_array($k, $_ENV['_skipdata_'])) {
  179. continue;
  180. }
  181. $file = $_ENV['_md5data_root_'] . $k;
  182. $toFile = $path . $k;
  183. if (!file_exists($toFile)) {
  184. @mkdir(dirname($toFile), 0777, true);
  185. $_dir = dirname($toFile);
  186. if (!is_dir($_dir) || !is_writable($_dir)) {
  187. gexit('copy file failed, dir not writable: ' . $toFile);
  188. }
  189. copy($file, $toFile);
  190. } else {
  191. $toMd5 = md5_file($toFile);
  192. if ($md5 != $toMd5) {
  193. copy($file, $toFile);
  194. }
  195. }
  196. $toMd5 = md5_file($toFile);
  197. if ($md5 != $toMd5) {
  198. unlink($toFile);
  199. copy($file, $toFile);
  200. $toMd5 = md5_file($toFile);
  201. }
  202. if ($md5 != $toMd5) {
  203. $toFileBackup = $toFile . '._bak_';
  204. @unlink($toFileBackup);
  205. rename($toFile, $toFileBackup);
  206. if (!file_exists($toFileBackup)) {
  207. gexit('copy file failed, file not writable: ' . $toFileBackup);
  208. }
  209. copy($file, $toFile);
  210. if (file_exists($toFile)) {
  211. @unlink($toFileBackup);
  212. }
  213. $toMd5 = md5_file($toFile);
  214. }
  215. if ($md5 != $toMd5) {
  216. gexit('copy file failed, md5 not match: ' . $toFile);
  217. }
  218. }
  219. }
  220. function clearFiles($path, $recursion = true) {
  221. foreach (glob($path . '/*') as $file) {
  222. if (is_dir($file)) {
  223. $recursion && clearFiles($file);
  224. continue;
  225. }
  226. $key = str_replace($_ENV['_todata_root_'], '', $file);
  227. if (empty($_ENV['_md5data_'][$key])) {
  228. echo 'delete: ' . $file . "\n";
  229. unlink($file);
  230. }
  231. }
  232. }