Skip to content

Commit 7e758e4

Browse files
committed
Add basic functionality
1 parent dd2a545 commit 7e758e4

File tree

10 files changed

+539
-2
lines changed

10 files changed

+539
-2
lines changed

src/main/groovy/net/minecrell/gitpatcher/GitPatcher.groovy

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,53 @@
2121
*/
2222
package net.minecrell.gitpatcher
2323

24+
import net.minecrell.gitpatcher.task.UpdateSubmodulesTask
25+
import net.minecrell.gitpatcher.task.patch.ApplyPatchesTask
26+
import net.minecrell.gitpatcher.task.patch.MakePatchesTask
2427
import org.gradle.api.Plugin
2528
import org.gradle.api.Project
2629

2730
class GitPatcher implements Plugin<Project> {
2831

29-
private Project project
32+
protected Project project
33+
protected PatchExtension extension
3034

3135
@Override
3236
void apply(Project project) {
3337
this.project = project
38+
project.with {
39+
this.extension = extensions.create('patches', PatchExtension)
40+
extension.root = projectDir
41+
42+
task('updateSubmodules', type: UpdateSubmodulesTask)
43+
task('applyPatches', type: ApplyPatchesTask) {
44+
dependsOn 'updateSubmodules'
45+
}
46+
task('makePatches', type: MakePatchesTask)
47+
48+
afterEvaluate {
49+
// Configure the settings from our extension
50+
configure([tasks.applyPatches, tasks.makePatches]) {
51+
repo = extension.target
52+
root = extension.root
53+
submodule = extension.submodule
54+
patchDir = extension.patches
55+
}
56+
57+
tasks.updateSubmodules.with {
58+
repo = extension.root
59+
submodule = extension.submodule
60+
}
61+
}
62+
}
3463
}
3564

3665
Project getProject() {
37-
project
66+
return project
67+
}
68+
69+
PatchExtension getExtension() {
70+
return extension
3871
}
3972

4073
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package net.minecrell.gitpatcher
2+
3+
class PatchExtension {
4+
5+
File root
6+
7+
String submodule
8+
9+
File target
10+
11+
File patches
12+
13+
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package net.minecrell.gitpatcher.git
2+
3+
import groovy.transform.EqualsAndHashCode
4+
import groovy.transform.ToString
5+
import org.apache.commons.lang.StringUtils
6+
import org.eclipse.jgit.api.Git
7+
import org.eclipse.jgit.diff.DiffFormatter
8+
import org.eclipse.jgit.lib.PersonIdent
9+
import org.eclipse.jgit.lib.Repository
10+
import org.eclipse.jgit.revwalk.RevCommit
11+
import org.joda.time.DateTime
12+
import org.joda.time.DateTimeZone
13+
import org.joda.time.format.DateTimeFormat
14+
import org.joda.time.format.DateTimeFormatter
15+
16+
@EqualsAndHashCode
17+
@ToString(includePackage = false)
18+
class MailPatch {
19+
20+
private static final JGIT_VERSION = 'jgit/' + (Git.class.getPackage().getImplementationVersion() ?: 'unknown')
21+
22+
private static final DateTimeFormatter dateFormat =
23+
DateTimeFormat.forPattern("EEE, d MMM yyyy HH:mm:ss Z").withLocale(Locale.US).withOffsetParsed()
24+
25+
final PersonIdent author
26+
final String message
27+
28+
MailPatch(PersonIdent author, String message) {
29+
this.author = author
30+
this.message = message
31+
}
32+
33+
boolean represents(RevCommit commit) {
34+
return author == commit.authorIdent && message == commit.fullMessage
35+
}
36+
37+
static MailPatch parse(InputStream is) {
38+
return parse(is.newReader('UTF-8'))
39+
}
40+
41+
static MailPatch parse(BufferedReader reader) {
42+
String from = null
43+
String time = null
44+
StringBuilder builder = null
45+
46+
String line
47+
while ((line = reader.readLine()) != null) {
48+
if (line.isEmpty()) {
49+
continue
50+
}
51+
52+
if (line.startsWith('diff')) {
53+
break
54+
}
55+
56+
if (builder == null) {
57+
int pos = line.indexOf(':')
58+
if (pos == -1) {
59+
continue
60+
}
61+
62+
def header = line.substring(0, pos)
63+
def content = line.substring(pos + 2)
64+
switch (header) {
65+
case "From":
66+
from = content
67+
break
68+
case "Date":
69+
time = content
70+
break
71+
case "Subject":
72+
builder = new StringBuilder(StringUtils.removeStart(content, '[PATCH] '))
73+
}
74+
} else {
75+
builder << line.trim()
76+
}
77+
}
78+
79+
assert from != null, 'Missing commit author'
80+
assert time != null, 'Missing commit date'
81+
assert builder != null, 'Missing commit message'
82+
83+
def author = parseAuthor(from)
84+
def date = dateFormat.parseDateTime(time)
85+
def message = builder.toString()
86+
87+
return new MailPatch(new PersonIdent(author[0], author[1], date.toDate(), date.getZone().toTimeZone()), message)
88+
}
89+
90+
private static String[] parseAuthor(String author) {
91+
int start = author.lastIndexOf('<')
92+
assert start != -1, 'Incomplete commit author'
93+
94+
def name = unquote(author.substring(0, start - 1))
95+
96+
def end = author.lastIndexOf('>')
97+
assert start != -1, 'Invalid commit author'
98+
99+
100+
return [name, author.substring(start + 1, end)]
101+
}
102+
103+
private static String unquote(String s) {
104+
if (s.length() > 2) {
105+
if (s.charAt(0) == '"' as char && s.charAt(s.length() - 1) == '"' as char) {
106+
return s.substring(1, s.length() - 1)
107+
}
108+
}
109+
110+
return s
111+
}
112+
113+
static void writeHeader(RevCommit commit, Writer writer) {
114+
writer << 'From ' << commit.id.name << ' Mon Sep 17 00:00:00 2001\n'
115+
writer << 'From: ' << commit.authorIdent.name << ' <' << commit.authorIdent.emailAddress << '>\n'
116+
117+
writer << 'Date: '
118+
dateFormat.printTo writer, new DateTime(commit.authorIdent.getWhen(), DateTimeZone.forTimeZone(commit.authorIdent.timeZone))
119+
writer << '\n'
120+
121+
writer << 'Subject: [PATCH] ' << commit.fullMessage << '\n\n\n'
122+
writer.flush()
123+
}
124+
125+
static void writePatch(Repository repo, RevCommit commit, OutputStream out) {
126+
def writer = out.newWriter('UTF-8')
127+
writeHeader(commit, writer)
128+
129+
def formatter = new DiffFormatter(out)
130+
formatter.repository = repo
131+
formatter.format(commit.getParent(0).tree, commit.tree)
132+
133+
writer << '-- \n' << JGIT_VERSION << '\n'
134+
writer.flush()
135+
}
136+
137+
138+
static String suggestFileName(RevCommit commit, int num) {
139+
def result = new StringBuilder(String.format("%04d-", num))
140+
for (char c : commit.shortMessage.chars) {
141+
if (Character.isLetter(c) || Character.isDigit(c)) {
142+
result << c
143+
} else if (Character.isWhitespace(c) || c == '/' as char) {
144+
result << '-' as char
145+
}
146+
}
147+
148+
result << '.patch'
149+
return result.toString()
150+
}
151+
152+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package net.minecrell.gitpatcher.git
2+
3+
import static org.eclipse.jgit.revwalk.RevSort.REVERSE
4+
import static org.eclipse.jgit.revwalk.RevSort.TOPO
5+
6+
import groovy.transform.stc.ClosureParams
7+
import groovy.transform.stc.FirstParam
8+
import groovy.transform.stc.SimpleType
9+
import org.eclipse.jgit.api.Git
10+
import org.eclipse.jgit.revwalk.RevWalk
11+
12+
final class Patcher {
13+
14+
private Patcher() {
15+
}
16+
17+
static Git openGit(File repo, @ClosureParams(value = SimpleType, options = "org.eclipse.jgit.api.Git") @DelegatesTo(Git) Closure action) {
18+
withGit(Git.open(repo), action)
19+
}
20+
21+
static Git withGit(Git git, @ClosureParams(FirstParam) @DelegatesTo(Git) Closure action) {
22+
action.delegate = git
23+
24+
try {
25+
action.call(git)
26+
return git
27+
} finally {
28+
git.close()
29+
}
30+
}
31+
32+
static RevWalk log(Git git, String start) {
33+
RevWalk walk = git.log().not(git.repository.resolve(start)).call()
34+
walk.sort(TOPO)
35+
walk.sort(REVERSE, true)
36+
return walk
37+
}
38+
39+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* PoreGradle
3+
* Copyright (c) 2015, Lapis <https://github.com/LapisBlue>
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
package net.minecrell.gitpatcher.task
24+
25+
import org.gradle.api.DefaultTask
26+
27+
abstract class GitTask extends DefaultTask {
28+
29+
File repo
30+
31+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* PoreGradle
3+
* Copyright (c) 2015, Lapis <https://github.com/LapisBlue>
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
package net.minecrell.gitpatcher.task
24+
25+
abstract class SubmoduleTask extends GitTask {
26+
27+
String submodule
28+
29+
{
30+
onlyIf { submodule != null }
31+
}
32+
33+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* PoreGradle
3+
* Copyright (c) 2015, Lapis <https://github.com/LapisBlue>
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
package net.minecrell.gitpatcher.task
24+
25+
import static net.minecrell.gitpatcher.git.Patcher.openGit
26+
27+
import org.eclipse.jgit.submodule.SubmoduleStatus
28+
import org.eclipse.jgit.submodule.SubmoduleStatusType
29+
import org.gradle.api.tasks.TaskAction
30+
31+
class UpdateSubmodulesTask extends SubmoduleTask {
32+
33+
@TaskAction
34+
void updateSubmodules() {
35+
openGit(repo) {
36+
SubmoduleStatus status = submoduleStatus().addPath(submodule).call().get(submodule);
37+
if (status.type == SubmoduleStatusType.INITIALIZED) {
38+
didWork = false
39+
return
40+
}
41+
42+
if (status.type == SubmoduleStatusType.MISSING) {
43+
submoduleInit().addPath(submodule).call()
44+
} else if (status.type == SubmoduleStatusType.REV_CHECKED_OUT) {
45+
logger.warn "Resetting submodule \"{}\" from {} back to {}", submodule, status.headId, status.indexId
46+
}
47+
48+
submoduleUpdate().addPath(submodule).call()
49+
}
50+
}
51+
52+
}

0 commit comments

Comments
 (0)