package hudson.plugins.git; import static hudson.Util.fixEmpty; import hudson.model.User; import hudson.scm.ChangeLogSet; import hudson.scm.ChangeLogSet.AffectedFile; import hudson.scm.EditType; import hudson.tasks.Mailer; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import java.io.IOException; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Represents a change set. * @author Nigel Magnay */ public class GitChangeSet extends ChangeLogSet.Entry { private static final Pattern FILE_LOG_ENTRY = Pattern.compile("^:[0-9]{6} [0-9]{6} ([0-9a-f]{40}) ([0-9a-f]{40}) ([ACDMRTUX])(?>[0-9]+)?\t(.*)$"); private static final String NULL_HASH = "0000000000000000000000000000000000000000"; private String author; private String authorEmail; private String comment; private String title; private String id; private String parentCommit; private Collection<Path> paths = new HashSet<Path>(); public GitChangeSet(List<String> lines) { if (lines.size() > 0) { parseCommit(lines); } } private void parseCommit(List<String> lines) { String message = ""; for (String line : lines) { if (line.length() > 0) { if (line.startsWith("commit ")) { this.id = line.split(" ")[1]; } else if (line.startsWith("tree ")) { } else if (line.startsWith("parent ")) { this.parentCommit = line.split(" ")[1]; } else if (line.startsWith("committer ")) { this.author = line.substring(10, line.indexOf(" <")); this.authorEmail = line.substring(line.indexOf(" <") + 2, line.indexOf("> ")); } else if (line.startsWith("author ")) { } else if (line.startsWith(" ")) { message += line.substring(4) + "\n"; } else if (':' == line.charAt(0)) { Matcher fileMatcher = FILE_LOG_ENTRY.matcher(line); if (fileMatcher.matches() && fileMatcher.groupCount() >= 4) { String mode = fileMatcher.group(3); if (mode.length() == 1) { String src = null; String dst = null; char editMode = mode.charAt(0); if (editMode == 'M' || editMode == 'A' || editMode == 'D') { src = parseHash(fileMatcher.group(1)); dst = parseHash(fileMatcher.group(2)); } String path = fileMatcher.group(4); this.paths.add(new Path(src, dst, editMode, path, this)); } } } else { // Ignore } } } this.comment = message; int endOfFirstLine = this.comment.indexOf('\n'); if (endOfFirstLine == -1) { this.title = this.comment; } else { this.title = this.comment.substring(0, endOfFirstLine); } } private String parseHash(String hash) { return NULL_HASH.equals(hash) ? null : hash; } @Override public void setParent(ChangeLogSet parent) { super.setParent(parent); } public String getParentCommit() { return parentCommit; } public Collection<Path> getPaths() { return this.paths; } @Override @Exported public Collection<String> getAffectedPaths() { Collection<String> affectedPaths = new HashSet<String>(this.paths.size()); for (Path file : this.paths) { affectedPaths.add(file.getPath()); } return affectedPaths; } @Override @Exported public User getAuthor() { if (this.author == null) { throw new RuntimeException("No author in this changeset!"); } User user = User.get(this.author, true); // set email address for user if needed if (fixEmpty(this.authorEmail) != null && user.getProperty(Mailer.UserProperty.class) == null) { try { user.addProperty(new Mailer.UserProperty(this.authorEmail)); } catch (IOException e) { // ignore error } } return user; } @Override @Exported public String getMsg() { return this.title; } @Exported public String getId() { return this.id; } @Exported public String getComment() { return this.comment; } public static class Path implements AffectedFile { private String src; private String dst; private char action; private String path; private GitChangeSet changeSet; private Path(String source, String destination, char action, String filePath, GitChangeSet changeSet) { this.src = source; this.dst = destination; this.action = action; this.path = filePath; this.changeSet = changeSet; } public String getSrc() { return src; } public String getDst() { return dst; } public String getPath() { return path; } public GitChangeSet getChangeSet() { return changeSet; } public EditType getEditType() { switch (action) { case 'A': return EditType.ADD; case 'D': return EditType.DELETE; default: return EditType.EDIT; } } } }