Hexo 博客源文件自动备份与一键推送指南

本文章介绍如何通过脚本自动备份并提交Hexo源码文件,包括 sourcescaffoldsthemes目录文件,及

package.json_config.yml等文件,当然,需要的话也可备份博客主目录下所有目录及文件。如果想要了解更多关于Hexo目录结构的内容,可以参阅文章Hexo 目录结构

自动备份

创建一个私有仓库用于存放Hexo源码文件,例如 blog_source

在博客主目录创建一个.ps1(powershell脚本)后缀的文件,例如 backup.ps1,粘贴下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# 设置输出编码为 UTF-8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

$HEXO_SOURCE_DIR = "D:\WorkSpace\Blog\blog"
$BACKUP_DIR = "D:\WorkSpace\Github\shiguang-coding\blog_source"

$startTime = Get-Date
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Hexo 博客源文件备份 " -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "开始时间:$($startTime.ToString('yyyy-MM-dd HH:mm:ss'))" -ForegroundColor Yellow
Write-Host ""

Write-Host "源目录:$HEXO_SOURCE_DIR" -ForegroundColor Green
Write-Host "备份到:$BACKUP_DIR" -ForegroundColor Green
Write-Host ""

Write-Host "正在检查备份目录..." -ForegroundColor White
if (!(Test-Path -Path $BACKUP_DIR)) {
Write-Host " -> 目录不存在,正在创建..." -ForegroundColor Gray
New-Item -ItemType Directory -Path $BACKUP_DIR -Force | Out-Null
Write-Host " [完成] 目录已创建" -ForegroundColor Green
} else {
Write-Host " [完成] 目录已存在" -ForegroundColor Green
}
Write-Host ""

# 排除目录和文件
$excludeDirs = @("node_modules", ".deploy_git", ".idea", "public", ".git")

function Get-FileList($dir) {
Get-ChildItem -Path $dir -Recurse -File -ErrorAction SilentlyContinue |
Where-Object {
$rel = $_.FullName.Substring($dir.Length + 1)
$excluded = $false
foreach ($d in $excludeDirs) {
if ($rel -eq "db.json" -or $rel.StartsWith($d + [IO.Path]::DirectorySeparatorChar) -or $rel.StartsWith($d + "/")) {
$excluded = $true
break
}
}
-not $excluded
} | ForEach-Object {
$rel = $_.FullName.Substring($dir.Length + 1)
[PSCustomObject]@{
Path = $rel
Size = $_.Length
Time = $_.LastWriteTime
}
}
}

Write-Host "正在检查变更..." -ForegroundColor White
Write-Host ""

# 同步前:分别获取源目录和备份目录的文件列表
$srcFiles = Get-FileList $HEXO_SOURCE_DIR
$bakFiles = Get-FileList $BACKUP_DIR

$srcMap = @{}; $srcFiles | ForEach-Object { $srcMap[$_.Path] = $_ }
$bakMap = @{}; $bakFiles | ForEach-Object { $bakMap[$_.Path] = $_ }

# 新增:源目录有,备份目录没有
$newFiles = @()
foreach ($f in $srcFiles) {
if (-not $bakMap.ContainsKey($f.Path)) {
$newFiles += $f.Path
}
}

# 删除:备份目录有,源目录没有
$deletedFiles = @()
foreach ($f in $bakFiles) {
if (-not $srcMap.ContainsKey($f.Path)) {
$deletedFiles += $f.Path
}
}

# 修改:两边都有,但大小或时间不同
$modifiedFiles = @()
foreach ($f in $srcFiles) {
if ($bakMap.ContainsKey($f.Path)) {
$bak = $bakMap[$f.Path]
if ($f.Size -ne $bak.Size -or $f.Time -ne $bak.Time) {
$modifiedFiles += $f.Path
}
}
}

# 显示变更
if ($newFiles.Count -gt 0) {
foreach ($f in $newFiles) {
Write-Host " [新增] $f" -ForegroundColor Green
}
}
if ($modifiedFiles.Count -gt 0) {
foreach ($f in $modifiedFiles) {
Write-Host " [修改] $f" -ForegroundColor Yellow
}
}
if ($deletedFiles.Count -gt 0) {
foreach ($f in $deletedFiles) {
Write-Host " [删除] $f" -ForegroundColor Red
}
}
if ($newFiles.Count -eq 0 -and $modifiedFiles.Count -eq 0 -and $deletedFiles.Count -eq 0) {
Write-Host " 没有检测到变更" -ForegroundColor Gray
}

Write-Host ""
Write-Host "正在同步文件..." -ForegroundColor White
Write-Host " 排除:node_modules、.deploy_git、.idea、public、.git、db.json" -ForegroundColor Gray
Write-Host ""

# 执行 robocopy 同步(静默模式)
& robocopy $HEXO_SOURCE_DIR $BACKUP_DIR /E /PURGE /XD node_modules .deploy_git .idea public .git /XF db.json /NJH /NJS /NP /NFL /NDL | Out-Null

Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
if ($LASTEXITCODE -le 8) {
$endTime = Get-Date
$duration = $endTime - $startTime
Write-Host "[完成] 备份成功!" -ForegroundColor Green
Write-Host " 结果:新增 $($newFiles.Count) 个 / 修改 $($modifiedFiles.Count) 个 / 删除 $($deletedFiles.Count) 个" -ForegroundColor White
Write-Host " 耗时:$($duration.TotalSeconds.ToString('F1')) 秒" -ForegroundColor Yellow
} else {
Write-Host "[失败] 备份出错!" -ForegroundColor Red
Write-Host " 错误码:$LASTEXITCODE" -ForegroundColor Red
}
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""

修改脚本中Hexo博客源码目录 $HEXO_SOURCE_DIR和 备份目录 $BACKUP_DIR

打开 powershell 终端 ,执行 backup.ps1 如果当前目录已存在则覆盖(只会覆盖目录中已存在的文件,源码目录原来存在,但是博客主目录不存在的文件不受影响)

image-20241117004459663

控制台会打印备份的文件详情及相关统计信息

image-20241117004540894

自动提交

在源码仓库所在目录创建 push.ps1文件,粘贴以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# 获取脚本所在目录
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path

$repoPath = "D:\WorkSpace\Github\shiguang-coding\blog_source"

$startTime = Get-Date
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " Hexo 博客源文件推送 " -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "开始时间:$($startTime.ToString('yyyy-MM-dd HH:mm:ss'))" -ForegroundColor Yellow
Write-Host ""

Write-Host "仓库目录:$repoPath" -ForegroundColor Green
Write-Host ""

Write-Host "正在切换到仓库目录..." -ForegroundColor White
Set-Location -Path $repoPath
Write-Host " [完成] 目录已切换" -ForegroundColor Green
Write-Host ""

Write-Host "正在检查变更..." -ForegroundColor White
git config core.quotepath false
$status = git status --porcelain

if ($status) {
$changeCount = ($status | Measure-Object -Line).Lines
Write-Host " 发现 $changeCount 个文件有变更" -ForegroundColor Yellow
Write-Host ""

# 分类显示变更文件
$newFiles = @()
$modFiles = @()
$delFiles = @()

foreach ($line in $status) {
$code = $line.Substring(0, 2).Trim()
$file = $line.Substring(3)
switch ($code) {
"A" { $newFiles += $file }
"M" { $modFiles += $file }
"D" { $delFiles += $file }
"?" { $newFiles += $file }
default {
if ($code -match "A") { $newFiles += $file }
elseif ($code -match "D") { $delFiles += $file }
else { $modFiles += $file }
}
}
}

if ($newFiles.Count -gt 0) {
Write-Host " 新增($($newFiles.Count) 个):" -ForegroundColor Green
$newFiles | Select-Object -First 15 | ForEach-Object {
Write-Host " + $_" -ForegroundColor Green
}
if ($newFiles.Count -gt 15) {
Write-Host " ... 还有 $($newFiles.Count - 15) 个" -ForegroundColor Gray
}
}
if ($modFiles.Count -gt 0) {
Write-Host " 修改($($modFiles.Count) 个):" -ForegroundColor Yellow
$modFiles | Select-Object -First 15 | ForEach-Object {
Write-Host " ~ $_" -ForegroundColor Yellow
}
if ($modFiles.Count -gt 15) {
Write-Host " ... 还有 $($modFiles.Count - 15) 个" -ForegroundColor Gray
}
}
if ($delFiles.Count -gt 0) {
Write-Host " 删除($($delFiles.Count) 个):" -ForegroundColor Red
$delFiles | Select-Object -First 15 | ForEach-Object {
Write-Host " - $_" -ForegroundColor Red
}
if ($delFiles.Count -gt 15) {
Write-Host " ... 还有 $($delFiles.Count - 15) 个" -ForegroundColor Gray
}
}
Write-Host ""

Write-Host "正在暂存文件..." -ForegroundColor White
git add . 2>$null
Write-Host " [完成] 文件已暂存" -ForegroundColor Green
Write-Host ""

$currentTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$commitMessage = "Auto commit at $currentTime"

Write-Host "正在提交..." -ForegroundColor White
git commit -m $commitMessage 2>$null
Write-Host " [完成] 已提交" -ForegroundColor Green
Write-Host " 提交信息:$commitMessage" -ForegroundColor Yellow
Write-Host ""

Write-Host "正在推送到远程..." -ForegroundColor White
git push origin main 2>$null
Write-Host " [完成] 已推送" -ForegroundColor Green
} else {
Write-Host " 没有检测到变更" -ForegroundColor Yellow
Write-Host " 所有文件已是最新状态" -ForegroundColor Gray
}

Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
$endTime = Get-Date
$duration = $endTime - $startTime
Write-Host "[完成] 操作结束" -ForegroundColor Green
Write-Host " 耗时:$($duration.TotalSeconds.ToString('F1')) 秒" -ForegroundColor Yellow
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""

Set-Location -Path $scriptPath

当然,你也可以指定仓库目录,这样就可以在任意位置执行脚本了

1
2
3
4
5
6
7
8
9
# 获取脚本所在目录
# $scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path

# # 设置Git仓库路径为脚本所在目录
# $repoPath = $scriptPath


# 设置仓库所在目录
$repoPath = "D:\WorkSpace\Github\shiguang-coding\blog_source"

直接执行push.ps1可执行文件即可

image-20241117004702049

这段脚本其实就是执行了 git add .git commit -m [commitMessage]git push origin main

如果你的仓库住分支不是main分支,可自行调整

执行该脚本将自动提交代码到远程仓库

数据恢复

  1. 克隆源码仓库到本地
  2. 执行npm install 或者 cnpm install 安装依赖
  3. 执行 hexo clean && hexo g && hexo s 生成网页文件并运行
  4. 浏览器访问http://localhost:4000

小贴士

如果执行过程中报错,可尝试清除npm缓存并删除node_moduls目录,重新执行npm install安装依赖

清除npm命令如下

1
npm cache clean --force

脚本升级说明

相比初版脚本,当前版本做了以下改进:

backup.ps1 升级:

  • 所有输出改为中文,方便理解
  • 同步前自动检测源目录与备份目录的差异
  • [新增][修改][删除] 三种类型分类显示变更文件
  • 显示完整的相对路径(如 source/_posts/xxx.md
  • 结束时统计新增、修改、删除数量和耗时
  • robocopy 使用静默模式,不输出冗余的目录层级信息

push.ps1 升级:

  • 所有输出改为中文
  • 设置 core.quotepath false 解决中文文件名显示为转义字符的问题
  • 将变更文件按 新增(+)、修改(~)、删除(-)分类显示
  • 每类最多显示 15 个文件,超出部分显示省略
  • 添加耗时统计