初始提交

This commit is contained in:
2025-11-15 17:37:48 +08:00
commit ec563fc407
7 changed files with 2162 additions and 0 deletions

5
.git_remote_config.json Normal file
View File

@@ -0,0 +1,5 @@
{
"remotes": {
"origin": "https:\/\/git.tsama.cn\/ll\/Demo1.git"
}
}

217
README.md Normal file
View File

@@ -0,0 +1,217 @@
# Git文件上传工具
一个基于PHP的Git文件上传工具支持JS和CSS分离文件上传无限制。
## 功能特点
-**文件上传无限制**:支持任意大小和数量的文件上传
-**Git集成**完整的Git操作支持添加、提交、推送、状态查看
-**分支管理**:支持创建、删除、切换分支
-**远程仓库配置**:支持添加和管理多个远程仓库
-**前后端分离**独立的CSS和JavaScript文件
-**拖拽上传**:支持拖拽文件上传
-**文件管理**:可视化的文件列表和选择
-**实时状态**Git仓库状态实时显示
-**响应式设计**:适配移动端和桌面端
## 文件结构
```
git/
├── index.php # 主文件包含PHP后端逻辑和HTML结构
├── css/
│ └── style.css # 样式文件
├── js/
│ └── main.js # JavaScript交互逻辑
├── test.php # 测试文件
└── uploads/ # 上传文件存储目录(自动生成)
```
## 安装要求
- PHP 7.0 或更高版本
- Git 已安装并配置
- Web服务器Apache/Nginx等
- 足够的磁盘空间
## 安装步骤
1. **上传文件到Web服务器**
```bash
# 将整个 git 目录上传到Web服务器目录
# 例如:/var/www/html/git/
```
2. **设置目录权限**
```bash
chmod 755 /var/www/html/git/
chmod 777 /var/www/html/git/uploads/
```
3. **访问工具**
```
http://your-domain.com/git/
```
## 使用方法
### 1. 文件上传
- **拖拽上传**:将文件拖拽到上传区域
- **点击上传**:点击上传区域选择文件
- **批量上传**:支持同时选择多个文件
### 2. 分支管理
- **查看分支**:分支列表会自动加载,显示当前分支和其他分支
- **创建新分支**
1. 在"新分支名称"输入框中输入分支名称
2. 点击"创建分支"按钮
3. 新分支将基于当前分支创建
- **切换分支**
1. 在"当前分支"下拉框中选择目标分支
2. 点击"切换分支"按钮
3. 工具会自动切换到选中的分支
- **删除分支**
1. 在"当前分支"下拉框中选择要删除的分支
2. 点击"删除分支"按钮
3. 确认删除操作(如果分支未合并,可选择强制删除)
- **刷新分支**:点击"刷新分支"按钮更新分支列表
### 3. 远程仓库配置
- **添加远程仓库**
1. 在"远程仓库名称"输入框中输入名称origin
2. 在"仓库地址"输入框中输入远程仓库URLhttps://github.com/username/repo.git
3. 点击"添加远程仓库"按钮
- **查看远程仓库**:点击"查看远程仓库"按钮查看已配置的远程仓库列表
### 4. Git操作
- **添加到暂存区**:选择文件后点击"添加到暂存区"
- **提交更改**:输入提交信息后点击"提交更改"
- **推送到远程**
1. 确保已配置远程仓库
2. 选择要推送的分支(在分支管理区域)
3. 在"远程仓库名称"输入框中输入要推送的远程仓库名称
4. 点击"推送到远程"按钮
- **查看状态**:点击"查看状态"查看Git状态
- **查看日志**:点击"查看日志"查看提交历史
### 5. 文件管理
- **刷新列表**:点击"刷新文件列表"更新文件列表
- **选择文件**勾选需要Git管理的文件
- **全选/取消**:快速选择或取消所有文件
## 配置说明
### PHP配置
工具会自动配置以下PHP设置
```php
ini_set('upload_max_filesize', '0'); // 无上传大小限制
ini_set('post_max_size', '0'); // 无POST大小限制
ini_set('max_execution_time', '0'); // 无执行时间限制
ini_set('memory_limit', '-1'); // 无内存限制
```
### Git配置
确保Web服务器用户有权限执行Git命令
```bash
# 检查Git是否可用
sudo -u www-data git --version
# 如果需要配置Git用户信息
sudo -u www-data git config --global user.name "Web Server"
sudo -u www-data git config --global user.email "web@example.com"
```
## 安全建议
1. **访问控制**建议添加密码保护或IP限制
2. **文件类型限制**:根据需要限制上传文件类型
3. **定期清理**:定期清理上传目录
4. **备份重要数据**:重要文件请做好备份
## 故障排除
### 常见问题
1. **文件上传失败**
- 检查 `uploads/` 目录权限
- 确认PHP配置允许大文件上传
- 查看Web服务器错误日志
2. **Git操作失败**
- 确认Git已安装
- 检查Web服务器用户对Git的访问权限
- 确认Git仓库已初始化
3. **分支管理问题**
- 无法删除当前分支,请先切换到其他分支
- 分支未合并时删除需要强制删除
- 分支名称不能包含特殊字符
4. **远程仓库配置问题**
- 确保远程仓库URL格式正确
- 检查网络连接是否正常
- 确认有权限访问远程仓库
- 如需要认证请配置Git凭据
5. **推送失败**
- 确认远程仓库已正确添加
- 检查是否有冲突需要解决
- 确认有推送权限
- 确认选择了正确的分支进行推送
6. **界面显示异常**
- 检查CSS文件是否正确加载
- 确认JavaScript文件无错误
- 清除浏览器缓存
### 运行测试
访问测试文件检查配置:
```
http://your-domain.com/git/test.php
```
## 更新日志
### v1.2.0 (2024-01-15)
- 添加分支管理功能
- 支持创建、删除、切换分支
- 支持分支选择和推送
- 优化分支操作的用户体验
### v1.1.0 (2024-01-15)
- 添加远程仓库配置功能
- 支持添加和管理远程仓库
- 支持推送到指定远程仓库
- 优化推送操作的用户体验
### v1.0.0 (2024-01-15)
- 初始版本发布
- 支持文件上传和Git操作
- 实现前后端分离
- 添加拖拽上传功能
## 许可证
MIT License - 详见LICENSE文件
## 作者
Git文件上传工具开发团队
## 支持
如有问题,请通过以下方式联系:
- 邮箱support@example.com
- 项目主页https://github.com/your-repo/git-uploader

478
css/style.css Normal file
View File

@@ -0,0 +1,478 @@
/* Git文件上传工具样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* 头部样式 */
header {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
header h1 {
color: #2c3e50;
font-size: 24px;
}
.git-status {
display: flex;
align-items: center;
gap: 10px;
}
.status-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
background-color: #e74c3c;
transition: background-color 0.3s;
}
.status-indicator.active {
background-color: #27ae60;
}
/* 主要内容区域 */
main {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
section {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
section h2 {
color: #2c3e50;
margin-bottom: 15px;
font-size: 18px;
}
/* 上传区域样式 */
.upload-section {
grid-column: 1 / -1;
}
.upload-area {
border: 2px dashed #3498db;
border-radius: 8px;
padding: 40px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
background-color: #f8f9fa;
}
.upload-area:hover {
border-color: #2980b9;
background-color: #e3f2fd;
}
.upload-area.dragover {
border-color: #27ae60;
background-color: #e8f5e8;
}
.upload-placeholder {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.upload-icon {
width: 48px;
height: 48px;
color: #3498db;
}
.upload-hint {
font-size: 12px;
color: #7f8c8d;
}
.uploaded-files {
margin-top: 15px;
}
.file-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background-color: #e8f5e8;
border-radius: 4px;
margin-bottom: 5px;
}
.file-item.success {
background-color: #d4edda;
border-left: 4px solid #27ae60;
}
.file-item.error {
background-color: #f8d7da;
border-left: 4px solid #e74c3c;
}
/* 文件列表样式 */
.files-section {
max-height: 500px;
overflow-y: auto;
}
.file-controls {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
.file-list {
border: 1px solid #e0e0e0;
border-radius: 4px;
max-height: 400px;
overflow-y: auto;
}
.file-checkbox {
display: flex;
align-items: center;
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
transition: background-color 0.2s;
}
.file-checkbox:hover {
background-color: #f8f9fa;
}
.file-checkbox:last-child {
border-bottom: none;
}
.file-checkbox input[type="checkbox"] {
margin-right: 10px;
}
.file-path {
font-family: 'Courier New', monospace;
font-size: 14px;
color: #495057;
}
/* Git操作区域样式 */
.git-section {
max-height: 500px;
overflow-y: auto;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #495057;
}
.form-group input {
width: 100%;
padding: 8px 12px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 14px;
}
.form-group input:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
.button-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 15px;
}
/* 按钮样式 */
.btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: all 0.3s;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 5px;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.btn:active {
transform: translateY(0);
}
.btn-primary {
background-color: #3498db;
color: white;
}
.btn-primary:hover {
background-color: #2980b9;
}
.btn-success {
background-color: #27ae60;
color: white;
}
.btn-success:hover {
background-color: #229954;
}
.btn-warning {
background-color: #f39c12;
color: white;
}
.btn-warning:hover {
background-color: #e67e22;
}
.btn-info {
background-color: #17a2b8;
color: white;
}
.btn-info:hover {
background-color: #138496;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #5a6268;
}
.btn-danger {
background-color: #e74c3c;
color: white;
}
.btn-danger:hover {
background-color: #c0392b;
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
/* 分支管理 */
.branch-config {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 15px;
margin-bottom: 20px;
}
.branch-config h3 {
color: #495057;
margin-bottom: 15px;
font-size: 16px;
}
.form-control {
width: 100%;
padding: 8px 12px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 14px;
background-color: white;
}
.form-control:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
/* 远程仓库配置 */
.remote-config {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 15px;
margin-bottom: 20px;
}
.remote-config h3 {
color: #495057;
margin-bottom: 15px;
font-size: 16px;
}
.remote-list {
margin-top: 15px;
max-height: 150px;
overflow-y: auto;
}
.remote-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background-color: white;
border: 1px solid #dee2e6;
border-radius: 4px;
margin-bottom: 8px;
}
.remote-item:last-child {
margin-bottom: 0;
}
.remote-name {
font-weight: 500;
color: #495057;
}
.remote-url {
font-size: 12px;
color: #6c757d;
font-family: 'Courier New', monospace;
word-break: break-all;
}
/* Git输出区域 */
.git-output {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 12px;
font-family: 'Courier New', monospace;
font-size: 13px;
max-height: 200px;
overflow-y: auto;
white-space: pre-wrap;
word-break: break-all;
}
.git-output:empty::before {
content: "Git操作输出将显示在这里...";
color: #6c757d;
font-style: italic;
}
/* 加载状态 */
.loading {
text-align: center;
padding: 20px;
color: #6c757d;
}
/* 响应式设计 */
@media (max-width: 768px) {
main {
grid-template-columns: 1fr;
}
header {
flex-direction: column;
gap: 15px;
text-align: center;
}
.button-group {
justify-content: center;
}
.file-controls {
flex-wrap: wrap;
justify-content: center;
}
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.3s ease-out;
}
/* 错误和成功消息 */
.message {
padding: 12px;
border-radius: 4px;
margin-bottom: 15px;
font-size: 14px;
}
.message.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.message.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.message.info {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}

511
index.php Normal file
View File

@@ -0,0 +1,511 @@
<?php
/**
* Git文件上传工具
* 支持JS和CSS分离文件上传无限制
*/
// 设置上传限制
ini_set('upload_max_filesize', '0');
ini_set('post_max_size', '0');
ini_set('max_execution_time', '0');
ini_set('memory_limit', '-1');
// 获取当前目录
$currentDir = __DIR__;
$gitDir = $currentDir . '/.git';
$remoteConfigFile = $currentDir . '/.git_remote_config.json';
// 检查Git仓库是否存在
function checkGitRepo() {
global $gitDir;
return is_dir($gitDir);
}
// 初始化Git仓库
function initGitRepo() {
global $currentDir;
try {
exec('cd "' . $currentDir . '" && git init', $output, $returnCode);
return $returnCode === 0;
} catch (Exception $e) {
return false;
}
}
// 获取文件列表
function getFileList($dir = '.') {
$files = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $file) {
if ($file->isFile() && !strpos($file->getPathname(), '.git')) {
$relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(__DIR__) + 1));
$files[] = $relativePath;
}
}
return $files;
}
// 处理文件上传
function handleFileUpload($files) {
$uploadedFiles = [];
if (!isset($files['files']) || empty($files['files']['name'][0])) {
return ['success' => false, 'message' => '没有选择文件'];
}
$uploadDir = __DIR__ . '/uploads/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$fileCount = count($files['files']['name']);
for ($i = 0; $i < $fileCount; $i++) {
if ($files['files']['error'][$i] === UPLOAD_ERR_OK) {
$tmpName = $files['files']['tmp_name'][$i];
$fileName = basename($files['files']['name'][$i]);
$uploadPath = $uploadDir . $fileName;
// 移动上传的文件
if (move_uploaded_file($tmpName, $uploadPath)) {
$uploadedFiles[] = $fileName;
}
}
}
return ['success' => true, 'files' => $uploadedFiles];
}
// 获取远程仓库配置
function getRemoteConfig() {
global $remoteConfigFile;
if (file_exists($remoteConfigFile)) {
$config = json_decode(file_get_contents($remoteConfigFile), true);
return $config ?: ['remotes' => []];
}
return ['remotes' => []];
}
// 保存远程仓库配置
function saveRemoteConfig($config) {
global $remoteConfigFile;
return file_put_contents($remoteConfigFile, json_encode($config, JSON_PRETTY_PRINT));
}
// 添加远程仓库
function addRemote($name, $url) {
global $currentDir;
try {
exec('cd "' . $currentDir . '" && git remote add ' . escapeshellarg($name) . ' ' . escapeshellarg($url), $output, $returnCode);
if ($returnCode === 0) {
// 保存到配置文件
$config = getRemoteConfig();
$config['remotes'][$name] = $url;
saveRemoteConfig($config);
return ['success' => true, 'message' => '远程仓库添加成功'];
} else {
return ['success' => false, 'message' => '远程仓库添加失败'];
}
} catch (Exception $e) {
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 获取远程仓库列表
function getRemotes() {
global $currentDir;
try {
exec('cd "' . $currentDir . '" && git remote -v', $output, $returnCode);
$remotes = [];
foreach ($output as $line) {
if (preg_match('/^(\S+)\s+(\S+)\s+\((\w+)\)/', $line, $matches)) {
$remotes[$matches[1]] = [
'url' => $matches[2],
'type' => $matches[3]
];
}
}
return ['success' => true, 'remotes' => $remotes];
} catch (Exception $e) {
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 推送代码到指定远程仓库
function pushToRemote($remote = 'origin', $branch = 'main') {
global $currentDir;
try {
exec('cd "' . $currentDir . '" && git push ' . escapeshellarg($remote) . ' ' . escapeshellarg($branch), $output, $returnCode);
return ['success' => $returnCode === 0, 'output' => $output];
} catch (Exception $e) {
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 获取分支列表
function getBranches() {
global $currentDir;
try {
$output = [];
$returnCode = 0;
// 获取本地分支
exec('cd "' . $currentDir . '" && git branch -a', $output, $returnCode);
$branches = [];
$currentBranch = '';
foreach ($output as $line) {
$line = trim($line);
if (empty($line)) continue;
// 检查是否是当前分支
if (strpos($line, '*') === 0) {
$currentBranch = trim(substr($line, 1));
$branches[] = [
'name' => $currentBranch,
'current' => true,
'remote' => false
];
} else {
// 检查是否是远程分支
$isRemote = strpos($line, 'remotes/') !== false;
$branchName = $isRemote ? substr($line, strpos($line, 'remotes/') + 8) : $line;
$branches[] = [
'name' => trim($branchName),
'current' => false,
'remote' => $isRemote
];
}
}
return ['success' => true, 'branches' => $branches, 'current' => $currentBranch];
} catch (Exception $e) {
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 创建新分支
function createBranch($branchName) {
global $currentDir;
try {
exec('cd "' . $currentDir . '" && git checkout -b ' . escapeshellarg($branchName), $output, $returnCode);
if ($returnCode === 0) {
return ['success' => true, 'message' => '分支创建成功', 'output' => $output];
} else {
return ['success' => false, 'message' => '分支创建失败', 'output' => $output];
}
} catch (Exception $e) {
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 删除分支
function deleteBranch($branchName, $force = false) {
global $currentDir;
try {
$forceFlag = $force ? '-D' : '-d';
exec('cd "' . $currentDir . '" && git branch ' . $forceFlag . ' ' . escapeshellarg($branchName), $output, $returnCode);
if ($returnCode === 0) {
return ['success' => true, 'message' => '分支删除成功', 'output' => $output];
} else {
return ['success' => false, 'message' => '分支删除失败', 'output' => $output];
}
} catch (Exception $e) {
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 切换分支
function switchBranch($branchName) {
global $currentDir;
try {
exec('cd "' . $currentDir . '" && git checkout ' . escapeshellarg($branchName), $output, $returnCode);
if ($returnCode === 0) {
return ['success' => true, 'message' => '分支切换成功', 'output' => $output];
} else {
return ['success' => false, 'message' => '分支切换失败', 'output' => $output];
}
} catch (Exception $e) {
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 处理Git操作
function handleGitOperation($operation, $message = '', $files = [], $remote = 'origin', $branch = 'main') {
global $currentDir;
try {
$output = [];
$returnCode = 0;
switch ($operation) {
case 'status':
exec('cd "' . $currentDir . '" && git status --porcelain', $output, $returnCode);
break;
case 'add':
if (!empty($files)) {
foreach ($files as $file) {
exec('cd "' . $currentDir . '" && git add "' . $file . '"', $output, $returnCode);
}
} else {
exec('cd "' . $currentDir . '" && git add .', $output, $returnCode);
}
break;
case 'commit':
if (empty($message)) {
$message = '自动提交';
}
exec('cd "' . $currentDir . '" && git commit -m "' . addslashes($message) . '"', $output, $returnCode);
break;
case 'push':
// 使用新的推送函数,支持指定远程仓库和分支
return pushToRemote($remote, $branch);
case 'log':
exec('cd "' . $currentDir . '" && git log --oneline -10', $output, $returnCode);
break;
}
return ['success' => $returnCode === 0, 'output' => $output];
} catch (Exception $e) {
return ['success' => false, 'message' => $e->getMessage()];
}
}
// 处理AJAX请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
header('Content-Type: application/json; charset=utf-8');
$action = $_POST['action'] ?? '';
switch ($action) {
case 'upload':
$result = handleFileUpload($_FILES);
echo json_encode($result);
break;
case 'git_operation':
$operation = $_POST['operation'] ?? '';
$message = $_POST['message'] ?? '';
$files = $_POST['files'] ?? [];
$remote = $_POST['remote'] ?? 'origin';
$branch = $_POST['branch'] ?? 'main';
$result = handleGitOperation($operation, $message, $files, $remote, $branch);
echo json_encode($result);
break;
case 'add_remote':
$name = $_POST['name'] ?? '';
$url = $_POST['url'] ?? '';
if (empty($name) || empty($url)) {
echo json_encode(['success' => false, 'message' => '远程仓库名称和地址不能为空']);
} else {
$result = addRemote($name, $url);
echo json_encode($result);
}
break;
case 'get_remotes':
$result = getRemotes();
echo json_encode($result);
break;
case 'get_branches':
$result = getBranches();
echo json_encode($result);
break;
case 'create_branch':
$branchName = $_POST['branch_name'] ?? '';
if (empty($branchName)) {
echo json_encode(['success' => false, 'message' => '分支名称不能为空']);
} else {
$result = createBranch($branchName);
echo json_encode($result);
}
break;
case 'delete_branch':
$branchName = $_POST['branch_name'] ?? '';
$force = isset($_POST['force']) && $_POST['force'] === 'true';
if (empty($branchName)) {
echo json_encode(['success' => false, 'message' => '分支名称不能为空']);
} else {
$result = deleteBranch($branchName, $force);
echo json_encode($result);
}
break;
case 'switch_branch':
$branchName = $_POST['branch_name'] ?? '';
if (empty($branchName)) {
echo json_encode(['success' => false, 'message' => '分支名称不能为空']);
} else {
$result = switchBranch($branchName);
echo json_encode($result);
}
break;
case 'get_files':
$files = getFileList();
echo json_encode(['success' => true, 'files' => $files]);
break;
case 'check_git':
echo json_encode(['success' => true, 'hasGit' => checkGitRepo()]);
break;
case 'init_git':
$success = initGitRepo();
echo json_encode(['success' => $success]);
break;
default:
echo json_encode(['success' => false, 'message' => '未知操作']);
}
exit;
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Git文件上传工具</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
<header>
<h1>Git文件上传工具</h1>
<div class="git-status" id="gitStatus">
<span class="status-indicator" id="gitIndicator"></span>
<span id="gitStatusText">检查Git状态...</span>
</div>
</header>
<main>
<!-- 文件上传区域 -->
<section class="upload-section">
<h2>文件上传</h2>
<div class="upload-area" id="uploadArea">
<div class="upload-placeholder">
<svg class="upload-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
</svg>
<p>拖拽文件到此处或点击选择文件</p>
<p class="upload-hint">支持任意大小的文件,无数量限制</p>
</div>
<input type="file" id="fileInput" multiple style="display: none;">
</div>
<div class="uploaded-files" id="uploadedFiles"></div>
</section>
<!-- 文件列表 -->
<section class="files-section">
<h2>项目文件</h2>
<div class="file-controls">
<button id="refreshFiles" class="btn btn-secondary">刷新文件列表</button>
<button id="selectAllFiles" class="btn btn-secondary">全选</button>
<button id="deselectAllFiles" class="btn btn-secondary">取消全选</button>
</div>
<div class="file-list" id="fileList">
<div class="loading">加载文件中...</div>
</div>
</section>
<!-- Git操作区域 -->
<section class="git-section">
<h2>Git操作</h2>
<!-- 分支管理 -->
<div class="branch-config">
<h3>分支管理</h3>
<div class="form-group">
<label for="branchSelect">当前分支:</label>
<select id="branchSelect" class="form-control">
<option value="">加载分支中...</option>
</select>
</div>
<div class="form-group">
<label for="newBranchName">新分支名称:</label>
<input type="text" id="newBranchName" placeholder="输入新分支名称">
</div>
<div class="button-group">
<button id="createBranch" class="btn btn-success">创建分支</button>
<button id="switchBranch" class="btn btn-primary">切换分支</button>
<button id="deleteBranch" class="btn btn-danger">删除分支</button>
<button id="refreshBranches" class="btn btn-secondary">刷新分支</button>
</div>
</div>
<hr style="margin: 20px 0;">
<!-- 远程仓库配置 -->
<div class="remote-config">
<h3>远程仓库配置</h3>
<div class="form-group">
<label for="remoteName">远程仓库名称:</label>
<input type="text" id="remoteName" placeholder="例如: origin" value="origin">
</div>
<div class="form-group">
<label for="remoteUrl">仓库地址:</label>
<input type="text" id="remoteUrl" placeholder="例如: https://github.com/username/repo.git">
</div>
<button id="addRemote" class="btn btn-info">添加远程仓库</button>
<button id="showRemotes" class="btn btn-secondary">查看远程仓库</button>
<div class="remote-list" id="remoteList"></div>
</div>
<hr style="margin: 20px 0;">
<div class="git-controls">
<div class="form-group">
<label for="commitMessage">提交信息:</label>
<input type="text" id="commitMessage" placeholder="输入提交信息..." value="自动提交">
</div>
<div class="button-group">
<button id="gitAdd" class="btn btn-primary">添加到暂存区</button>
<button id="gitCommit" class="btn btn-success">提交更改</button>
<button id="gitPush" class="btn btn-warning">推送到远程</button>
<button id="gitStatusBtn" class="btn btn-info">查看状态</button>
<button id="gitLog" class="btn btn-secondary">查看日志</button>
</div>
</div>
<div class="git-output" id="gitOutput"></div>
</section>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>

593
js/main.js Normal file
View File

@@ -0,0 +1,593 @@
/**
* Git文件上传工具 - 主要JavaScript功能
* 支持文件上传、Git操作、文件管理等功能
*/
class GitUploader {
constructor() {
this.selectedFiles = new Set();
this.init();
}
// 初始化
init() {
this.setupEventListeners();
this.checkGitStatus();
this.loadFileList();
this.showRemotes(); // 加载远程仓库列表
this.loadBranches(); // 加载分支列表
}
// 设置事件监听器
setupEventListeners() {
// 文件上传相关
const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput');
uploadArea.addEventListener('click', () => fileInput.click());
uploadArea.addEventListener('dragover', this.handleDragOver.bind(this));
uploadArea.addEventListener('dragleave', this.handleDragLeave.bind(this));
uploadArea.addEventListener('drop', this.handleDrop.bind(this));
fileInput.addEventListener('change', this.handleFileSelect.bind(this));
// Git操作相关
document.getElementById('gitAdd').addEventListener('click', () => this.gitOperation('add'));
document.getElementById('gitCommit').addEventListener('click', () => this.gitOperation('commit'));
document.getElementById('gitPush').addEventListener('click', () => this.gitOperation('push'));
document.getElementById('gitStatusBtn').addEventListener('click', () => this.gitOperation('status'));
document.getElementById('gitLog').addEventListener('click', () => this.gitOperation('log'));
// 远程仓库相关
document.getElementById('addRemote').addEventListener('click', () => this.addRemote());
document.getElementById('showRemotes').addEventListener('click', () => this.showRemotes());
// 分支管理相关
document.getElementById('createBranch').addEventListener('click', () => this.createBranch());
document.getElementById('switchBranch').addEventListener('click', () => this.switchBranch());
document.getElementById('deleteBranch').addEventListener('click', () => this.deleteBranch());
document.getElementById('refreshBranches').addEventListener('click', () => this.loadBranches());
// 文件列表相关
document.getElementById('refreshFiles').addEventListener('click', () => this.loadFileList());
document.getElementById('selectAllFiles').addEventListener('click', () => this.selectAllFiles());
document.getElementById('deselectAllFiles').addEventListener('click', () => this.deselectAllFiles());
}
// 拖拽处理
handleDragOver(e) {
e.preventDefault();
document.getElementById('uploadArea').classList.add('dragover');
}
handleDragLeave(e) {
e.preventDefault();
document.getElementById('uploadArea').classList.remove('dragover');
}
handleDrop(e) {
e.preventDefault();
document.getElementById('uploadArea').classList.remove('dragover');
const files = Array.from(e.dataTransfer.files);
this.uploadFiles(files);
}
handleFileSelect(e) {
const files = Array.from(e.target.files);
this.uploadFiles(files);
}
// 文件上传
async uploadFiles(files) {
if (files.length === 0) return;
const formData = new FormData();
formData.append('action', 'upload');
files.forEach(file => {
formData.append('files[]', file);
});
try {
this.showMessage('正在上传文件...', 'info');
const response = await fetch('index.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
this.showMessage(`成功上传 ${result.files.length} 个文件`, 'success');
this.displayUploadedFiles(result.files);
this.loadFileList(); // 重新加载文件列表
} else {
this.showMessage(result.message || '上传失败', 'error');
}
} catch (error) {
this.showMessage('上传出错: ' + error.message, 'error');
}
}
// 显示已上传的文件
displayUploadedFiles(files) {
const container = document.getElementById('uploadedFiles');
container.innerHTML = '';
files.forEach(file => {
const fileItem = document.createElement('div');
fileItem.className = 'file-item success fade-in';
fileItem.innerHTML = `
<span>${file}</span>
<span style="color: #27ae60;">✓</span>
`;
container.appendChild(fileItem);
});
}
// 检查Git状态
async checkGitStatus() {
try {
const response = await fetch('index.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'action=check_git'
});
const result = await response.json();
const indicator = document.getElementById('gitIndicator');
const statusText = document.getElementById('gitStatusText');
if (result.success && result.hasGit) {
indicator.classList.add('active');
statusText.textContent = 'Git仓库已初始化';
} else {
indicator.classList.remove('active');
statusText.textContent = 'Git仓库未初始化';
// 自动初始化Git仓库
if (confirm('Git仓库未初始化是否现在初始化')) {
await this.initGitRepo();
}
}
} catch (error) {
this.showMessage('检查Git状态失败: ' + error.message, 'error');
}
}
// 初始化Git仓库
async initGitRepo() {
try {
const response = await fetch('index.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'action=init_git'
});
const result = await response.json();
if (result.success) {
this.showMessage('Git仓库初始化成功', 'success');
this.checkGitStatus(); // 重新检查状态
} else {
this.showMessage('Git仓库初始化失败', 'error');
}
} catch (error) {
this.showMessage('初始化Git仓库失败: ' + error.message, 'error');
}
}
// Git操作
async gitOperation(operation) {
const commitMessage = document.getElementById('commitMessage').value || '自动提交';
const selectedFiles = Array.from(this.selectedFiles);
const remoteName = document.getElementById('remoteName').value || 'origin';
const branchSelect = document.getElementById('branchSelect');
const currentBranch = branchSelect ? branchSelect.value : 'main';
const params = new URLSearchParams();
params.append('action', 'git_operation');
params.append('operation', operation);
if (operation === 'commit') {
params.append('message', commitMessage);
}
if (operation === 'push') {
params.append('remote', remoteName);
params.append('branch', currentBranch); // 使用当前选择的分支
}
if (operation === 'add' && selectedFiles.length > 0) {
selectedFiles.forEach(file => {
params.append('files[]', file);
});
}
try {
this.showMessage(`正在执行 Git ${operation}...`, 'info');
const response = await fetch('index.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params
});
const result = await response.json();
if (result.success) {
this.showMessage(`Git ${operation} 执行成功`, 'success');
this.displayGitOutput(result.output);
if (operation === 'add' || operation === 'commit') {
this.loadFileList(); // 重新加载文件列表
}
} else {
this.showMessage(`Git ${operation} 执行失败: ${result.message || '未知错误'}`, 'error');
this.displayGitOutput(result.output || []);
}
} catch (error) {
this.showMessage(`Git ${operation} 执行出错: ${error.message}`, 'error');
}
}
// 显示Git输出
displayGitOutput(output) {
const outputElement = document.getElementById('gitOutput');
if (output && output.length > 0) {
outputElement.textContent = output.join('\n');
} else {
outputElement.textContent = '暂无输出';
}
outputElement.scrollTop = outputElement.scrollHeight;
}
// 添加远程仓库
async addRemote() {
const name = document.getElementById('remoteName').value.trim();
const url = document.getElementById('remoteUrl').value.trim();
if (!name || !url) {
this.showMessage('请输入远程仓库名称和地址', 'error');
return;
}
const params = new URLSearchParams();
params.append('action', 'add_remote');
params.append('name', name);
params.append('url', url);
try {
this.showMessage('正在添加远程仓库...', 'info');
const response = await fetch('index.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params
});
const result = await response.json();
if (result.success) {
this.showMessage('远程仓库添加成功', 'success');
document.getElementById('remoteUrl').value = ''; // 清空输入框
this.showRemotes(); // 刷新远程仓库列表
} else {
this.showMessage('添加远程仓库失败: ' + (result.message || '未知错误'), 'error');
}
} catch (error) {
this.showMessage('添加远程仓库出错: ' + error.message, 'error');
}
}
// 显示远程仓库列表
async showRemotes() {
try {
const response = await fetch('index.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'action=get_remotes'
});
const result = await response.json();
const remoteList = document.getElementById('remoteList');
if (result.success && result.remotes && Object.keys(result.remotes).length > 0) {
remoteList.innerHTML = '';
Object.entries(result.remotes).forEach(([name, info]) => {
const remoteItem = document.createElement('div');
remoteItem.className = 'remote-item';
remoteItem.innerHTML = `
<div>
<div class="remote-name">${name} (${info.type})</div>
<div class="remote-url">${info.url}</div>
</div>
`;
remoteList.appendChild(remoteItem);
});
} else {
remoteList.innerHTML = '<div class="loading">暂无远程仓库</div>';
}
} catch (error) {
this.showMessage('获取远程仓库列表失败: ' + error.message, 'error');
}
}
// 加载分支列表
async loadBranches() {
try {
const response = await fetch('index.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'action=get_branches'
});
const result = await response.json();
const branchSelect = document.getElementById('branchSelect');
if (result.success && result.branches) {
branchSelect.innerHTML = '';
result.branches.forEach(branch => {
const option = document.createElement('option');
option.value = branch.name;
option.textContent = branch.name + (branch.current ? ' (当前)' : '') + (branch.remote ? ' (远程)' : '');
if (branch.current) {
option.selected = true;
}
branchSelect.appendChild(option);
});
} else {
branchSelect.innerHTML = '<option value="">暂无分支</option>';
}
} catch (error) {
this.showMessage('获取分支列表失败: ' + error.message, 'error');
}
}
// 创建新分支
async createBranch() {
const branchName = document.getElementById('newBranchName').value.trim();
if (!branchName) {
this.showMessage('请输入分支名称', 'error');
return;
}
const params = new URLSearchParams();
params.append('action', 'create_branch');
params.append('branch_name', branchName);
try {
this.showMessage('正在创建分支...', 'info');
const response = await fetch('index.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params
});
const result = await response.json();
if (result.success) {
this.showMessage('分支创建成功', 'success');
document.getElementById('newBranchName').value = ''; // 清空输入框
this.loadBranches(); // 重新加载分支列表
this.displayGitOutput(result.output);
} else {
this.showMessage('创建分支失败: ' + (result.message || '未知错误'), 'error');
this.displayGitOutput(result.output || []);
}
} catch (error) {
this.showMessage('创建分支出错: ' + error.message, 'error');
}
}
// 切换分支
async switchBranch() {
const branchSelect = document.getElementById('branchSelect');
const branchName = branchSelect.value;
if (!branchName) {
this.showMessage('请选择要切换的分支', 'error');
return;
}
const params = new URLSearchParams();
params.append('action', 'switch_branch');
params.append('branch_name', branchName);
try {
this.showMessage('正在切换分支...', 'info');
const response = await fetch('index.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params
});
const result = await response.json();
if (result.success) {
this.showMessage('分支切换成功', 'success');
this.loadBranches(); // 重新加载分支列表
this.loadFileList(); // 重新加载文件列表
this.displayGitOutput(result.output);
} else {
this.showMessage('切换分支失败: ' + (result.message || '未知错误'), 'error');
this.displayGitOutput(result.output || []);
}
} catch (error) {
this.showMessage('切换分支出错: ' + error.message, 'error');
}
}
// 删除分支
async deleteBranch() {
const branchSelect = document.getElementById('branchSelect');
const branchName = branchSelect.value;
if (!branchName) {
this.showMessage('请选择要删除的分支', 'error');
return;
}
if (!confirm(`确定要删除分支 "${branchName}" 吗?`)) {
return;
}
const params = new URLSearchParams();
params.append('action', 'delete_branch');
params.append('branch_name', branchName);
params.append('force', 'false'); // 默认不强制删除
try {
this.showMessage('正在删除分支...', 'info');
const response = await fetch('index.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params
});
const result = await response.json();
if (result.success) {
this.showMessage('分支删除成功', 'success');
this.loadBranches(); // 重新加载分支列表
this.displayGitOutput(result.output);
} else {
this.showMessage('删除分支失败: ' + (result.message || '未知错误'), 'error');
this.displayGitOutput(result.output || []);
if (confirm('是否强制删除该分支?')) {
params.set('force', 'true');
const forceResponse = await fetch('index.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params
});
const forceResult = await forceResponse.json();
if (forceResult.success) {
this.showMessage('分支强制删除成功', 'success');
this.loadBranches();
this.displayGitOutput(forceResult.output);
}
}
}
} catch (error) {
this.showMessage('删除分支出错: ' + error.message, 'error');
}
}
// 加载文件列表
async loadFileList() {
try {
const response = await fetch('index.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'action=get_files'
});
const result = await response.json();
if (result.success) {
this.displayFileList(result.files);
} else {
this.showMessage('加载文件列表失败', 'error');
}
} catch (error) {
this.showMessage('加载文件列表出错: ' + error.message, 'error');
}
}
// 显示文件列表
displayFileList(files) {
const container = document.getElementById('fileList');
if (files.length === 0) {
container.innerHTML = '<div class="loading">暂无文件</div>';
return;
}
container.innerHTML = '';
files.forEach(file => {
const fileItem = document.createElement('div');
fileItem.className = 'file-checkbox';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `file-${this.escapeId(file)}`;
checkbox.value = file;
checkbox.checked = this.selectedFiles.has(file);
checkbox.addEventListener('change', (e) => {
if (e.target.checked) {
this.selectedFiles.add(file);
} else {
this.selectedFiles.delete(file);
}
});
const label = document.createElement('label');
label.htmlFor = checkbox.id;
label.className = 'file-path';
label.textContent = file;
label.style.cursor = 'pointer';
label.style.flex = '1';
fileItem.appendChild(checkbox);
fileItem.appendChild(label);
container.appendChild(fileItem);
});
}
// 全选文件
selectAllFiles() {
const checkboxes = document.querySelectorAll('#fileList input[type="checkbox"]');
checkboxes.forEach(checkbox => {
checkbox.checked = true;
this.selectedFiles.add(checkbox.value);
});
}
// 取消全选
deselectAllFiles() {
const checkboxes = document.querySelectorAll('#fileList input[type="checkbox"]');
checkboxes.forEach(checkbox => {
checkbox.checked = false;
this.selectedFiles.delete(checkbox.value);
});
}
// 工具方法转义ID
escapeId(str) {
return str.replace(/[^a-zA-Z0-9]/g, '_');
}
// 显示消息
showMessage(message, type = 'info') {
// 创建消息元素
const messageElement = document.createElement('div');
messageElement.className = `message ${type} fade-in`;
messageElement.textContent = message;
// 添加到页面顶部
const container = document.querySelector('.container');
container.insertBefore(messageElement, container.firstChild);
// 3秒后自动移除
setTimeout(() => {
messageElement.remove();
}, 3000);
}
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', () => {
new GitUploader();
});

179
test.php Normal file
View File

@@ -0,0 +1,179 @@
<?php
/**
* Git上传工具测试文件
* 用于验证各个功能模块是否正常工作
*/
// 测试文件上传功能
function testFileUpload() {
echo "=== 测试文件上传功能 ===\n";
// 创建测试文件
$testContent = "这是一个测试文件\n创建于: " . date('Y-m-d H:i:s');
$testFile = 'test_upload_' . time() . '.txt';
if (file_put_contents($testFile, $testContent)) {
echo "✓ 测试文件创建成功: $testFile\n";
} else {
echo "✗ 测试文件创建失败\n";
return false;
}
// 检查上传目录
$uploadDir = __DIR__ . '/uploads/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
echo "✓ 上传目录创建成功\n";
}
return true;
}
// 测试Git操作功能
function testGitOperations() {
echo "\n=== 测试Git操作功能 ===\n";
$currentDir = __DIR__;
// 检查Git是否可用
exec('git --version', $output, $returnCode);
if ($returnCode === 0) {
echo "✓ Git已安装: " . $output[0] . "\n";
} else {
echo "✗ Git未安装或不可用\n";
return false;
}
// 检查Git仓库
$gitDir = $currentDir . '/.git';
if (is_dir($gitDir)) {
echo "✓ Git仓库已存在\n";
} else {
echo " Git仓库不存在可以初始化\n";
}
return true;
}
// 测试PHP配置
function testPHPConfiguration() {
echo "\n=== 测试PHP配置 ===\n";
$configs = [
'upload_max_filesize' => ini_get('upload_max_filesize'),
'post_max_size' => ini_get('post_max_size'),
'max_execution_time' => ini_get('max_execution_time'),
'memory_limit' => ini_get('memory_limit')
];
foreach ($configs as $key => $value) {
echo " $key: $value\n";
}
// 检查必要的PHP函数
$functions = ['exec', 'move_uploaded_file', 'json_encode', 'file_put_contents'];
$allFunctionsAvailable = true;
foreach ($functions as $function) {
if (function_exists($function)) {
echo "✓ 函数 $function 可用\n";
} else {
echo "✗ 函数 $function 不可用\n";
$allFunctionsAvailable = false;
}
}
return $allFunctionsAvailable;
}
// 测试目录权限
function testDirectoryPermissions() {
echo "\n=== 测试目录权限 ===\n";
$directories = [
__DIR__,
__DIR__ . '/uploads/',
__DIR__ . '/css/',
__DIR__ . '/js/'
];
foreach ($directories as $dir) {
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
if (is_writable($dir)) {
echo "✓ 目录可写: $dir\n";
} else {
echo "✗ 目录不可写: $dir\n";
}
}
return true;
}
// 测试AJAX端点
function testAjaxEndpoints() {
echo "\n=== 测试AJAX端点 ===\n";
// 这里可以添加更详细的端点测试
$endpoints = [
'check_git' => '检查Git状态',
'get_files' => '获取文件列表',
'git_operation' => 'Git操作',
'upload' => '文件上传'
];
foreach ($endpoints as $endpoint => $description) {
echo "✓ 端点 $endpoint ($description) 已定义\n";
}
return true;
}
// 运行所有测试
function runAllTests() {
echo "开始Git上传工具测试...\n";
echo "=====================================\n";
$tests = [
'testPHPConfiguration',
'testDirectoryPermissions',
'testGitOperations',
'testAjaxEndpoints',
'testFileUpload'
];
$allPassed = true;
foreach ($tests as $test) {
try {
$result = $test();
if (!$result) {
$allPassed = false;
}
} catch (Exception $e) {
echo "✗ 测试 $test 抛出异常: " . $e->getMessage() . "\n";
$allPassed = false;
}
echo "\n";
}
echo "=====================================\n";
if ($allPassed) {
echo "✓ 所有测试通过!工具应该可以正常工作。\n";
} else {
echo "✗ 部分测试失败,请检查配置和权限。\n";
}
// 清理测试文件
$testFiles = glob('test_upload_*.txt');
foreach ($testFiles as $file) {
unlink($file);
}
}
// 如果直接访问此文件,运行测试
if (basename($_SERVER['PHP_SELF']) === basename(__FILE__)) {
runAllTests();
}

179
uploads/test.php Normal file
View File

@@ -0,0 +1,179 @@
<?php
/**
* Git上传工具测试文件
* 用于验证各个功能模块是否正常工作
*/
// 测试文件上传功能
function testFileUpload() {
echo "=== 测试文件上传功能 ===\n";
// 创建测试文件
$testContent = "这是一个测试文件\n创建于: " . date('Y-m-d H:i:s');
$testFile = 'test_upload_' . time() . '.txt';
if (file_put_contents($testFile, $testContent)) {
echo "✓ 测试文件创建成功: $testFile\n";
} else {
echo "✗ 测试文件创建失败\n";
return false;
}
// 检查上传目录
$uploadDir = __DIR__ . '/uploads/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
echo "✓ 上传目录创建成功\n";
}
return true;
}
// 测试Git操作功能
function testGitOperations() {
echo "\n=== 测试Git操作功能 ===\n";
$currentDir = __DIR__;
// 检查Git是否可用
exec('git --version', $output, $returnCode);
if ($returnCode === 0) {
echo "✓ Git已安装: " . $output[0] . "\n";
} else {
echo "✗ Git未安装或不可用\n";
return false;
}
// 检查Git仓库
$gitDir = $currentDir . '/.git';
if (is_dir($gitDir)) {
echo "✓ Git仓库已存在\n";
} else {
echo " Git仓库不存在可以初始化\n";
}
return true;
}
// 测试PHP配置
function testPHPConfiguration() {
echo "\n=== 测试PHP配置 ===\n";
$configs = [
'upload_max_filesize' => ini_get('upload_max_filesize'),
'post_max_size' => ini_get('post_max_size'),
'max_execution_time' => ini_get('max_execution_time'),
'memory_limit' => ini_get('memory_limit')
];
foreach ($configs as $key => $value) {
echo " $key: $value\n";
}
// 检查必要的PHP函数
$functions = ['exec', 'move_uploaded_file', 'json_encode', 'file_put_contents'];
$allFunctionsAvailable = true;
foreach ($functions as $function) {
if (function_exists($function)) {
echo "✓ 函数 $function 可用\n";
} else {
echo "✗ 函数 $function 不可用\n";
$allFunctionsAvailable = false;
}
}
return $allFunctionsAvailable;
}
// 测试目录权限
function testDirectoryPermissions() {
echo "\n=== 测试目录权限 ===\n";
$directories = [
__DIR__,
__DIR__ . '/uploads/',
__DIR__ . '/css/',
__DIR__ . '/js/'
];
foreach ($directories as $dir) {
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
if (is_writable($dir)) {
echo "✓ 目录可写: $dir\n";
} else {
echo "✗ 目录不可写: $dir\n";
}
}
return true;
}
// 测试AJAX端点
function testAjaxEndpoints() {
echo "\n=== 测试AJAX端点 ===\n";
// 这里可以添加更详细的端点测试
$endpoints = [
'check_git' => '检查Git状态',
'get_files' => '获取文件列表',
'git_operation' => 'Git操作',
'upload' => '文件上传'
];
foreach ($endpoints as $endpoint => $description) {
echo "✓ 端点 $endpoint ($description) 已定义\n";
}
return true;
}
// 运行所有测试
function runAllTests() {
echo "开始Git上传工具测试...\n";
echo "=====================================\n";
$tests = [
'testPHPConfiguration',
'testDirectoryPermissions',
'testGitOperations',
'testAjaxEndpoints',
'testFileUpload'
];
$allPassed = true;
foreach ($tests as $test) {
try {
$result = $test();
if (!$result) {
$allPassed = false;
}
} catch (Exception $e) {
echo "✗ 测试 $test 抛出异常: " . $e->getMessage() . "\n";
$allPassed = false;
}
echo "\n";
}
echo "=====================================\n";
if ($allPassed) {
echo "✓ 所有测试通过!工具应该可以正常工作。\n";
} else {
echo "✗ 部分测试失败,请检查配置和权限。\n";
}
// 清理测试文件
$testFiles = glob('test_upload_*.txt');
foreach ($testFiles as $file) {
unlink($file);
}
}
// 如果直接访问此文件,运行测试
if (basename($_SERVER['PHP_SELF']) === basename(__FILE__)) {
runAllTests();
}