-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGitUpdateKt.main.kts
197 lines (177 loc) · 5.88 KB
/
GitUpdateKt.main.kts
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!/usr/bin/env kotlin
// 以 .main.kts 后缀才可以使用远端依赖
@file:Repository("https://maven.pkg.jetbrains.space/public/p/kotlinx-coroutines/maven")
@file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0-RC3-Beta")
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import java.io.Closeable
import java.io.File
import java.io.IOException
import java.util.Collections
private val targetDir = File("/mnt/f/open_source")
targetDir.runCommand("git", "--version").waitFor()
targetDir.runCommand("git", "config", "--global", "-l").waitFor()
println("工作目录:$targetDir")
val ignoreDirs = File(targetDir, "ignore.txt").readLines()
println("忽略的Git工程:$ignoreDirs")
runBlocking {
val files: Array<File> = targetDir.listFiles { f ->
f.isDirectory && f.name !in ignoreDirs
}!!
files.shuffle()
val filesList = Collections.synchronizedList(files.toMutableList())
files.map {
async(Dispatchers.IO) {
try {
checkToSync(GitDir(it))
} catch (e: Throwable) {
System.err.println("项目:$it 同步失败:$e")
}
filesList.remove(it)
println("剩余进度项目:${filesList.size}/${files.size}")
if (filesList.size < 10) {
println("剩余项目:$filesList")
}
}
}.forEach {
it.await()
}
}
@Throws(IOException::class, InterruptedException::class)
private fun checkToSync(gitDir: GitDir) {
println("同步Git目录:${gitDir.dir}")
syncGit(gitDir)
println("同步Git目录:${gitDir.dir} 完成")
}
@Throws(IOException::class, InterruptedException::class)
private fun syncGit(gitDir: GitDir) {
val defBranch = gitDir.defBranch
// 将未映射到本地的所有远程分支映射到本地
for (remote in defBranch) {
val branchName = remote.substring(remote.indexOf('/') + 1)
gitDir.dir.runCommand(
"git", "branch", "--track", branchName, remote
).waitFor()
}
// 更新所有本地分支列表
gitDir.reCalculateLocalBranch()
gitDir.localBranch.forEach { branch ->
syncGitSomeoneBranch(gitDir, branch)
}
// 更新当前分支
syncGitCurrentBranch(gitDir)
}
@Throws(IOException::class, InterruptedException::class)
private fun syncGitSomeoneBranch(gitDir: GitDir, branchName: String) {
println("同步Git目录:" + gitDir.dir + " 分支:" + branchName)
val code = gitDir.dir.runCommand(
"git",
"--no-optional-locks",
"-c",
"diff.mnemonicprefix=false",
"-c",
"core.quotepath=false",
"fetch",
"origin",
"$branchName:$branchName"
).waitFor()
if (code != 0) {
System.err.println("Git 目录 ${gitDir.dir} 分支:$branchName 同步失败: $code")
}
}
@Throws(IOException::class, InterruptedException::class)
private fun syncGitCurrentBranch(gitDir: GitDir) {
println("同步Git目录:" + gitDir.dir + " 当前分支")
gitDir.dir.runCommand("git", "checkout", "--", ".").waitFor()
gitDir.dir.runCommand("git", "clean", "-xdf").waitFor()
gitDir.dir.runCommand("git", "reset", "--hard").waitFor()
val code = gitDir.dir.runCommand("git", "pull", "--all").waitFor()
if (code != 0) {
System.err.println("Git 目录 ${gitDir.dir} 当前分支同步失败: $code")
}
}
fun File.runCommand(
vararg commands: String
): Process = ProcessBuilder(*commands)
.directory(this)
.redirectError(ProcessBuilder.Redirect.INHERIT)
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
.start()
fun Process.lines(): ProcessLinesSequence = ProcessLinesSequence(this)
inline fun <T> Process.use(
block: Process.() -> T
): T = try {
block()
} finally {
destroy()
}
class ProcessLinesSequence(
private val process: Process
) : Sequence<String> by process.inputLineSequence(), Closeable {
override fun close() {
process.destroy()
}
companion object {
private fun Process.inputLineSequence() =
inputStream.bufferedReader().lineSequence()
}
}
private class GitDir(
val dir: File
) {
var localBranch: List<String> = calculateLocalBranch()
var defBranch: List<String> = checkDefBranch()
fun reCalculateLocalBranch() {
localBranch = calculateLocalBranch()
}
// 读取本地分支
@Throws(IOException::class)
private fun calculateLocalBranch() =
dir.runCommand("git", "branch").lines().use { reader ->
// 替换当前分支
reader.map {
it.trim().replace("* ", "")
}.filter {
it.isNotEmpty()
}.toList()
}
@Throws(IOException::class, InterruptedException::class)
private fun checkDefBranch(): List<String> {
// 抓取远端分支
dir.runCommand(
"git",
"--no-optional-locks",
"-c",
"color.branch=false",
"-c",
"color.diff=false",
"-c",
"color.status=false",
"-c",
"diff.mnemonicprefix=false",
"-c",
"core.quotepath=false",
"-c",
"credential.helper=sourcetree",
"fetch",
"--prune",
"origin"
).waitFor()
// 读取所有本地不存在的远程分支到本地作为映射
// 读取远程分支
return dir.runCommand(
"git", "branch", "-r"
).lines().use { reader ->
// 过滤在已经映射到本地的分支
reader.map { obj: String ->
obj.trim()
}.filter { s: String ->
!s.contains("->") && s.isNotEmpty()
}.toSet()
}.filter { s: String ->
// 求差集
s.substringAfter('/') !in localBranch
}.toList()
}
}