@@ -88,6 +88,8 @@ public class Constants { | |||
public static final String ISO8601 = "yyyy-MM-dd'T'HH:mm:ssZ"; | |||
public static final String R_GITBLIT = "refs/gitblit/"; | |||
public static String getGitBlitVersion() { | |||
return NAME + " v" + VERSION; | |||
} |
@@ -29,6 +29,7 @@ import java.util.Collection; | |||
import java.util.Enumeration; | |||
import java.util.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import javax.servlet.ServletConfig; | |||
@@ -37,7 +38,9 @@ import javax.servlet.ServletException; | |||
import javax.servlet.http.HttpServletRequest; | |||
import org.eclipse.jgit.http.server.resolver.DefaultReceivePackFactory; | |||
import org.eclipse.jgit.http.server.resolver.DefaultUploadPackFactory; | |||
import org.eclipse.jgit.lib.PersonIdent; | |||
import org.eclipse.jgit.lib.Ref; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
import org.eclipse.jgit.transport.PostReceiveHook; | |||
@@ -45,6 +48,8 @@ import org.eclipse.jgit.transport.PreReceiveHook; | |||
import org.eclipse.jgit.transport.ReceiveCommand; | |||
import org.eclipse.jgit.transport.ReceiveCommand.Result; | |||
import org.eclipse.jgit.transport.ReceivePack; | |||
import org.eclipse.jgit.transport.RefFilter; | |||
import org.eclipse.jgit.transport.UploadPack; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; | |||
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; | |||
import org.slf4j.Logger; | |||
@@ -55,7 +60,9 @@ import com.gitblit.models.RepositoryModel; | |||
import com.gitblit.models.UserModel; | |||
import com.gitblit.utils.ClientLogger; | |||
import com.gitblit.utils.HttpUtils; | |||
import com.gitblit.utils.IssueUtils; | |||
import com.gitblit.utils.JGitUtils; | |||
import com.gitblit.utils.PushLogUtils; | |||
import com.gitblit.utils.StringUtils; | |||
/** | |||
@@ -131,6 +138,35 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { | |||
return rp; | |||
} | |||
}); | |||
// override the default upload pack to exclude gitblit refs | |||
setUploadPackFactory(new DefaultUploadPackFactory() { | |||
@Override | |||
public UploadPack create(final HttpServletRequest req, final Repository db) | |||
throws ServiceNotEnabledException, ServiceNotAuthorizedException { | |||
UploadPack up = super.create(req, db); | |||
RefFilter refFilter = new RefFilter() { | |||
@Override | |||
public Map<String, Ref> filter(Map<String, Ref> refs) { | |||
// admin accounts can access all refs | |||
UserModel user = GitBlit.self().authenticate(req); | |||
if (user == null) { | |||
user = UserModel.ANONYMOUS; | |||
} | |||
if (user.canAdmin()) { | |||
return refs; | |||
} | |||
// normal users can not clone gitblit refs | |||
refs.remove(IssueUtils.GB_ISSUES); | |||
refs.remove(PushLogUtils.GB_PUSHES); | |||
return refs; | |||
} | |||
}; | |||
up.setRefFilter(refFilter); | |||
return up; | |||
} | |||
}); | |||
super.init(new GitblitServletConfig(config)); | |||
} | |||
@@ -264,12 +300,11 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { | |||
logger.info("skipping post-receive hooks, no refs created, updated, or removed"); | |||
return; | |||
} | |||
RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName); | |||
Set<String> scripts = new LinkedHashSet<String>(); | |||
scripts.addAll(GitBlit.self().getPostReceiveScriptsInherited(repository)); | |||
scripts.addAll(repository.postReceiveScripts); | |||
UserModel user = getUserModel(rp); | |||
runGroovy(repository, user, commands, rp, scripts); | |||
RepositoryModel repository = GitBlit.self().getRepositoryModel(repositoryName); | |||
// log ref changes | |||
for (ReceiveCommand cmd : commands) { | |||
if (Result.OK.equals(cmd.getResult())) { | |||
// add some logging for important ref changes | |||
@@ -288,6 +323,16 @@ public class GitServlet extends org.eclipse.jgit.http.server.GitServlet { | |||
} | |||
} | |||
} | |||
// update push log | |||
PushLogUtils.updatePushLog(user, rp.getRepository(), commands); | |||
logger.info(MessageFormat.format("{0} push log updated", repository.name)); | |||
// run Groovy hook scripts | |||
Set<String> scripts = new LinkedHashSet<String>(); | |||
scripts.addAll(GitBlit.self().getPostReceiveScriptsInherited(repository)); | |||
scripts.addAll(repository.postReceiveScripts); | |||
runGroovy(repository, user, commands, rp, scripts); | |||
// Experimental | |||
// runNativeScript(rp, "hooks/post-receive", commands); |
@@ -25,7 +25,6 @@ import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.eclipse.jgit.lib.PersonIdent; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
import com.gitblit.utils.StringUtils; | |||
@@ -127,86 +126,4 @@ public class Activity implements Serializable, Comparable<Activity> { | |||
// reverse chronological order | |||
return o.startDate.compareTo(startDate); | |||
} | |||
/** | |||
* Model class to represent a RevCommit, it's source repository, and the | |||
* branch. This class is used by the activity page. | |||
* | |||
* @author James Moger | |||
*/ | |||
public static class RepositoryCommit implements Serializable, Comparable<RepositoryCommit> { | |||
private static final long serialVersionUID = 1L; | |||
public final String repository; | |||
public final String branch; | |||
private final RevCommit commit; | |||
private List<RefModel> refs; | |||
public RepositoryCommit(String repository, String branch, RevCommit commit) { | |||
this.repository = repository; | |||
this.branch = branch; | |||
this.commit = commit; | |||
} | |||
public void setRefs(List<RefModel> refs) { | |||
this.refs = refs; | |||
} | |||
public List<RefModel> getRefs() { | |||
return refs; | |||
} | |||
public String getName() { | |||
return commit.getName(); | |||
} | |||
public String getShortName() { | |||
return commit.getName().substring(0, 8); | |||
} | |||
public String getShortMessage() { | |||
return commit.getShortMessage(); | |||
} | |||
public int getParentCount() { | |||
return commit.getParentCount(); | |||
} | |||
public PersonIdent getAuthorIdent() { | |||
return commit.getAuthorIdent(); | |||
} | |||
public PersonIdent getCommitterIdent() { | |||
return commit.getCommitterIdent(); | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (o instanceof RepositoryCommit) { | |||
RepositoryCommit commit = (RepositoryCommit) o; | |||
return repository.equals(commit.repository) && getName().equals(commit.getName()); | |||
} | |||
return false; | |||
} | |||
@Override | |||
public int hashCode() { | |||
return (repository + commit).hashCode(); | |||
} | |||
@Override | |||
public int compareTo(RepositoryCommit o) { | |||
// reverse-chronological order | |||
if (commit.getCommitTime() > o.commit.getCommitTime()) { | |||
return -1; | |||
} else if (commit.getCommitTime() < o.commit.getCommitTime()) { | |||
return 1; | |||
} | |||
return 0; | |||
} | |||
} | |||
} |
@@ -0,0 +1,166 @@ | |||
/* | |||
* Copyright 2013 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit.models; | |||
import java.io.Serializable; | |||
import java.text.MessageFormat; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Date; | |||
import java.util.HashSet; | |||
import java.util.LinkedHashSet; | |||
import java.util.List; | |||
import java.util.Set; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
/** | |||
* Model class to represent a push into a repository. | |||
* | |||
* @author James Moger | |||
*/ | |||
public class PushLogEntry implements Serializable, Comparable<PushLogEntry> { | |||
private static final long serialVersionUID = 1L; | |||
public final String repository; | |||
public final Date date; | |||
public final UserModel user; | |||
private final Set<RepositoryCommit> commits; | |||
/** | |||
* Constructor for specified duration of push from start date. | |||
* | |||
* @param repository | |||
* the repository that received the push | |||
* @param date | |||
* the date of the push | |||
* @param user | |||
* the user who pushed | |||
*/ | |||
public PushLogEntry(String repository, Date date, UserModel user) { | |||
this.repository = repository; | |||
this.date = date; | |||
this.user = user; | |||
this.commits = new LinkedHashSet<RepositoryCommit>(); | |||
} | |||
/** | |||
* Adds a commit to the push entry object as long as the commit is not a | |||
* duplicate. | |||
* | |||
* @param branch | |||
* @param commit | |||
* @return a RepositoryCommit, if one was added. Null if this is duplicate | |||
* commit | |||
*/ | |||
public RepositoryCommit addCommit(String branch, RevCommit commit) { | |||
RepositoryCommit commitModel = new RepositoryCommit(repository, branch, commit); | |||
if (commits.add(commitModel)) { | |||
return commitModel; | |||
} | |||
return null; | |||
} | |||
/** | |||
* Returns the list of branches changed by the push. | |||
* | |||
* @return a list of branches | |||
*/ | |||
public List<String> getChangedBranches() { | |||
return getChangedRefs(Constants.R_HEADS); | |||
} | |||
/** | |||
* Returns the list of tags changed by the push. | |||
* | |||
* @return a list of tags | |||
*/ | |||
public List<String> getChangedTags() { | |||
return getChangedRefs(Constants.R_TAGS); | |||
} | |||
/** | |||
* Gets the changed refs in the push. | |||
* | |||
* @param baseRef | |||
* @return the changed refs | |||
*/ | |||
protected List<String> getChangedRefs(String baseRef) { | |||
Set<String> refs = new HashSet<String>(); | |||
for (RepositoryCommit commit : commits) { | |||
if (baseRef == null || commit.branch.startsWith(baseRef)) { | |||
refs.add(commit.branch); | |||
} | |||
} | |||
List<String> list = new ArrayList<String>(refs); | |||
Collections.sort(list); | |||
return list; | |||
} | |||
/** | |||
* The total number of commits in the push. | |||
* | |||
* @return the number of commits in the push | |||
*/ | |||
public int getCommitCount() { | |||
return commits.size(); | |||
} | |||
/** | |||
* Returns all commits in the push. | |||
* | |||
* @return a list of commits | |||
*/ | |||
public List<RepositoryCommit> getCommits() { | |||
List<RepositoryCommit> list = new ArrayList<RepositoryCommit>(commits); | |||
Collections.sort(list); | |||
return list; | |||
} | |||
/** | |||
* Returns all commits that belong to a particular ref | |||
* | |||
* @param ref | |||
* @return a list of commits | |||
*/ | |||
public List<RepositoryCommit> getCommits(String ref) { | |||
List<RepositoryCommit> list = new ArrayList<RepositoryCommit>(); | |||
for (RepositoryCommit commit : commits) { | |||
if (commit.branch.equals(ref)) { | |||
list.add(commit); | |||
} | |||
} | |||
Collections.sort(list); | |||
return list; | |||
} | |||
@Override | |||
public int compareTo(PushLogEntry o) { | |||
// reverse chronological order | |||
return o.date.compareTo(date); | |||
} | |||
@Override | |||
public String toString() { | |||
return MessageFormat.format("{0,date,yyyy-MM-dd HH:mm}: {1} pushed {2,number,0} commit{3} to {4} ", | |||
date, user.getDisplayName(), commits.size(), commits.size() == 1 ? "":"s", repository); | |||
} | |||
} |
@@ -0,0 +1,104 @@ | |||
/* | |||
* Copyright 2011 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit.models; | |||
import java.io.Serializable; | |||
import java.util.List; | |||
import org.eclipse.jgit.lib.PersonIdent; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
/** | |||
* Model class to represent a RevCommit, it's source repository, and the branch. | |||
* This class is used by the activity page. | |||
* | |||
* @author James Moger | |||
*/ | |||
public class RepositoryCommit implements Serializable, Comparable<RepositoryCommit> { | |||
private static final long serialVersionUID = 1L; | |||
public final String repository; | |||
public final String branch; | |||
private final RevCommit commit; | |||
private List<RefModel> refs; | |||
public RepositoryCommit(String repository, String branch, RevCommit commit) { | |||
this.repository = repository; | |||
this.branch = branch; | |||
this.commit = commit; | |||
} | |||
public void setRefs(List<RefModel> refs) { | |||
this.refs = refs; | |||
} | |||
public List<RefModel> getRefs() { | |||
return refs; | |||
} | |||
public String getName() { | |||
return commit.getName(); | |||
} | |||
public String getShortName() { | |||
return commit.getName().substring(0, 8); | |||
} | |||
public String getShortMessage() { | |||
return commit.getShortMessage(); | |||
} | |||
public int getParentCount() { | |||
return commit.getParentCount(); | |||
} | |||
public PersonIdent getAuthorIdent() { | |||
return commit.getAuthorIdent(); | |||
} | |||
public PersonIdent getCommitterIdent() { | |||
return commit.getCommitterIdent(); | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (o instanceof RepositoryCommit) { | |||
RepositoryCommit commit = (RepositoryCommit) o; | |||
return repository.equals(commit.repository) && getName().equals(commit.getName()); | |||
} | |||
return false; | |||
} | |||
@Override | |||
public int hashCode() { | |||
return (repository + commit).hashCode(); | |||
} | |||
@Override | |||
public int compareTo(RepositoryCommit o) { | |||
// reverse-chronological order | |||
if (commit.getCommitTime() > o.commit.getCommitTime()) { | |||
return -1; | |||
} else if (commit.getCommitTime() < o.commit.getCommitTime()) { | |||
return 1; | |||
} | |||
return 0; | |||
} | |||
} |
@@ -36,9 +36,9 @@ import org.eclipse.jgit.revwalk.RevCommit; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.models.Activity; | |||
import com.gitblit.models.Activity.RepositoryCommit; | |||
import com.gitblit.models.GravatarProfile; | |||
import com.gitblit.models.RefModel; | |||
import com.gitblit.models.RepositoryCommit; | |||
import com.gitblit.models.RepositoryModel; | |||
import com.google.gson.reflect.TypeToken; | |||
@@ -76,9 +76,9 @@ public class IssueUtils { | |||
public abstract boolean accept(IssueModel issue); | |||
} | |||
public static final String GB_ISSUES = "refs/heads/gb-issues"; | |||
public static final String GB_ISSUES = "refs/gitblit/issues"; | |||
static final Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class); | |||
static final Logger LOGGER = LoggerFactory.getLogger(IssueUtils.class); | |||
/** | |||
* Log an error message and exception. | |||
@@ -111,7 +111,13 @@ public class IssueUtils { | |||
* @return a refmodel for the gb-issues branch or null | |||
*/ | |||
public static RefModel getIssuesBranch(Repository repository) { | |||
return JGitUtils.getBranch(repository, "gb-issues"); | |||
List<RefModel> refs = JGitUtils.getRefs(repository, com.gitblit.Constants.R_GITBLIT); | |||
for (RefModel ref : refs) { | |||
if (ref.reference.getName().equals(GB_ISSUES)) { | |||
return ref; | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
@@ -394,7 +400,7 @@ public class IssueUtils { | |||
public static IssueModel createIssue(Repository repository, Change change) { | |||
RefModel issuesBranch = getIssuesBranch(repository); | |||
if (issuesBranch == null) { | |||
JGitUtils.createOrphanBranch(repository, "gb-issues", null); | |||
JGitUtils.createOrphanBranch(repository, GB_ISSUES, null); | |||
} | |||
if (StringUtils.isEmpty(change.author)) { | |||
@@ -471,7 +477,7 @@ public class IssueUtils { | |||
RefModel issuesBranch = getIssuesBranch(repository); | |||
if (issuesBranch == null) { | |||
throw new RuntimeException("gb-issues branch does not exist!"); | |||
throw new RuntimeException(GB_ISSUES + " does not exist!"); | |||
} | |||
if (StringUtils.isEmpty(issueId)) { |
@@ -1457,6 +1457,20 @@ public class JGitUtils { | |||
int maxCount) { | |||
return getRefs(repository, Constants.R_NOTES, fullName, maxCount); | |||
} | |||
/** | |||
* Returns the list of refs in the specified base ref. If repository does | |||
* not exist or is empty, an empty list is returned. | |||
* | |||
* @param repository | |||
* @param fullName | |||
* if true, /refs/yadayadayada is returned. If false, | |||
* yadayadayada is returned. | |||
* @return list of refs | |||
*/ | |||
public static List<RefModel> getRefs(Repository repository, String baseRef) { | |||
return getRefs(repository, baseRef, true, -1); | |||
} | |||
/** | |||
* Returns a list of references in the repository matching "refs". If the |
@@ -0,0 +1,344 @@ | |||
/* | |||
* Copyright 2013 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit.utils; | |||
import java.io.IOException; | |||
import java.text.MessageFormat; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Set; | |||
import java.util.TreeSet; | |||
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; | |||
import org.eclipse.jgit.api.errors.JGitInternalException; | |||
import org.eclipse.jgit.dircache.DirCache; | |||
import org.eclipse.jgit.dircache.DirCacheBuilder; | |||
import org.eclipse.jgit.dircache.DirCacheEntry; | |||
import org.eclipse.jgit.internal.JGitText; | |||
import org.eclipse.jgit.lib.CommitBuilder; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.FileMode; | |||
import org.eclipse.jgit.lib.ObjectId; | |||
import org.eclipse.jgit.lib.ObjectInserter; | |||
import org.eclipse.jgit.lib.PersonIdent; | |||
import org.eclipse.jgit.lib.RefUpdate; | |||
import org.eclipse.jgit.lib.RefUpdate.Result; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
import org.eclipse.jgit.revwalk.RevWalk; | |||
import org.eclipse.jgit.transport.ReceiveCommand; | |||
import org.eclipse.jgit.treewalk.CanonicalTreeParser; | |||
import org.eclipse.jgit.treewalk.TreeWalk; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import com.gitblit.models.PathModel.PathChangeModel; | |||
import com.gitblit.models.PushLogEntry; | |||
import com.gitblit.models.RefModel; | |||
import com.gitblit.models.UserModel; | |||
/** | |||
* Utility class for maintaining a pushlog within a git repository on an | |||
* orphan branch. | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
public class PushLogUtils { | |||
public static final String GB_PUSHES = "refs/gitblit/pushes"; | |||
static final Logger LOGGER = LoggerFactory.getLogger(PushLogUtils.class); | |||
/** | |||
* Log an error message and exception. | |||
* | |||
* @param t | |||
* @param repository | |||
* if repository is not null it MUST be the {0} parameter in the | |||
* pattern. | |||
* @param pattern | |||
* @param objects | |||
*/ | |||
private static void error(Throwable t, Repository repository, String pattern, Object... objects) { | |||
List<Object> parameters = new ArrayList<Object>(); | |||
if (objects != null && objects.length > 0) { | |||
for (Object o : objects) { | |||
parameters.add(o); | |||
} | |||
} | |||
if (repository != null) { | |||
parameters.add(0, repository.getDirectory().getAbsolutePath()); | |||
} | |||
LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t); | |||
} | |||
/** | |||
* Returns a RefModel for the gb-pushes branch in the repository. If the | |||
* branch can not be found, null is returned. | |||
* | |||
* @param repository | |||
* @return a refmodel for the gb-pushes branch or null | |||
*/ | |||
public static RefModel getPushLogBranch(Repository repository) { | |||
List<RefModel> refs = JGitUtils.getRefs(repository, com.gitblit.Constants.R_GITBLIT); | |||
for (RefModel ref : refs) { | |||
if (ref.reference.getName().equals(GB_PUSHES)) { | |||
return ref; | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* Updates a push log. | |||
* | |||
* @param user | |||
* @param repository | |||
* @param commands | |||
* @return true, if the update was successful | |||
*/ | |||
public static boolean updatePushLog(UserModel user, Repository repository, | |||
Collection<ReceiveCommand> commands) { | |||
RefModel pushlogBranch = getPushLogBranch(repository); | |||
if (pushlogBranch == null) { | |||
JGitUtils.createOrphanBranch(repository, GB_PUSHES, null); | |||
} | |||
boolean success = false; | |||
String message = "push"; | |||
try { | |||
ObjectId headId = repository.resolve(GB_PUSHES + "^{commit}"); | |||
ObjectInserter odi = repository.newObjectInserter(); | |||
try { | |||
// Create the in-memory index of the push log entry | |||
DirCache index = createIndex(repository, headId, commands); | |||
ObjectId indexTreeId = index.writeTree(odi); | |||
PersonIdent ident = new PersonIdent(user.getDisplayName(), | |||
user.emailAddress == null ? user.username:user.emailAddress); | |||
// Create a commit object | |||
CommitBuilder commit = new CommitBuilder(); | |||
commit.setAuthor(ident); | |||
commit.setCommitter(ident); | |||
commit.setEncoding(Constants.CHARACTER_ENCODING); | |||
commit.setMessage(message); | |||
commit.setParentId(headId); | |||
commit.setTreeId(indexTreeId); | |||
// Insert the commit into the repository | |||
ObjectId commitId = odi.insert(commit); | |||
odi.flush(); | |||
RevWalk revWalk = new RevWalk(repository); | |||
try { | |||
RevCommit revCommit = revWalk.parseCommit(commitId); | |||
RefUpdate ru = repository.updateRef(GB_PUSHES); | |||
ru.setNewObjectId(commitId); | |||
ru.setExpectedOldObjectId(headId); | |||
ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false); | |||
Result rc = ru.forceUpdate(); | |||
switch (rc) { | |||
case NEW: | |||
case FORCED: | |||
case FAST_FORWARD: | |||
success = true; | |||
break; | |||
case REJECTED: | |||
case LOCK_FAILURE: | |||
throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD, | |||
ru.getRef(), rc); | |||
default: | |||
throw new JGitInternalException(MessageFormat.format( | |||
JGitText.get().updatingRefFailed, GB_PUSHES, commitId.toString(), | |||
rc)); | |||
} | |||
} finally { | |||
revWalk.release(); | |||
} | |||
} finally { | |||
odi.release(); | |||
} | |||
} catch (Throwable t) { | |||
error(t, repository, "Failed to commit pushlog entry to {0}"); | |||
} | |||
return success; | |||
} | |||
/** | |||
* Creates an in-memory index of the push log entry. | |||
* | |||
* @param repo | |||
* @param headId | |||
* @param commands | |||
* @return an in-memory index | |||
* @throws IOException | |||
*/ | |||
private static DirCache createIndex(Repository repo, ObjectId headId, | |||
Collection<ReceiveCommand> commands) throws IOException { | |||
DirCache inCoreIndex = DirCache.newInCore(); | |||
DirCacheBuilder dcBuilder = inCoreIndex.builder(); | |||
ObjectInserter inserter = repo.newObjectInserter(); | |||
long now = System.currentTimeMillis(); | |||
Set<String> ignorePaths = new TreeSet<String>(); | |||
try { | |||
// add receive commands to the temporary index | |||
for (ReceiveCommand command : commands) { | |||
// use the ref names as the path names | |||
String path = command.getRefName(); | |||
ignorePaths.add(path); | |||
StringBuilder change = new StringBuilder(); | |||
change.append(command.getType().name()).append(' '); | |||
switch (command.getType()) { | |||
case CREATE: | |||
change.append(ObjectId.zeroId().getName()); | |||
change.append(' '); | |||
change.append(command.getNewId().getName()); | |||
break; | |||
case UPDATE: | |||
case UPDATE_NONFASTFORWARD: | |||
change.append(command.getOldId().getName()); | |||
change.append(' '); | |||
change.append(command.getNewId().getName()); | |||
break; | |||
case DELETE: | |||
change = null; | |||
break; | |||
} | |||
if (change == null) { | |||
// ref deleted | |||
continue; | |||
} | |||
String content = change.toString(); | |||
// create an index entry for this attachment | |||
final DirCacheEntry dcEntry = new DirCacheEntry(path); | |||
dcEntry.setLength(content.length()); | |||
dcEntry.setLastModified(now); | |||
dcEntry.setFileMode(FileMode.REGULAR_FILE); | |||
// insert object | |||
dcEntry.setObjectId(inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))); | |||
// add to temporary in-core index | |||
dcBuilder.add(dcEntry); | |||
} | |||
// Traverse HEAD to add all other paths | |||
TreeWalk treeWalk = new TreeWalk(repo); | |||
int hIdx = -1; | |||
if (headId != null) | |||
hIdx = treeWalk.addTree(new RevWalk(repo).parseTree(headId)); | |||
treeWalk.setRecursive(true); | |||
while (treeWalk.next()) { | |||
String path = treeWalk.getPathString(); | |||
CanonicalTreeParser hTree = null; | |||
if (hIdx != -1) | |||
hTree = treeWalk.getTree(hIdx, CanonicalTreeParser.class); | |||
if (!ignorePaths.contains(path)) { | |||
// add entries from HEAD for all other paths | |||
if (hTree != null) { | |||
// create a new DirCacheEntry with data retrieved from | |||
// HEAD | |||
final DirCacheEntry dcEntry = new DirCacheEntry(path); | |||
dcEntry.setObjectId(hTree.getEntryObjectId()); | |||
dcEntry.setFileMode(hTree.getEntryFileMode()); | |||
// add to temporary in-core index | |||
dcBuilder.add(dcEntry); | |||
} | |||
} | |||
} | |||
// release the treewalk | |||
treeWalk.release(); | |||
// finish temporary in-core index used for this commit | |||
dcBuilder.finish(); | |||
} finally { | |||
inserter.release(); | |||
} | |||
return inCoreIndex; | |||
} | |||
public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository) { | |||
return getPushLog(repositoryName, repository, null, -1); | |||
} | |||
public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository, int maxCount) { | |||
return getPushLog(repositoryName, repository, null, maxCount); | |||
} | |||
public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository, Date minimumDate) { | |||
return getPushLog(repositoryName, repository, minimumDate, -1); | |||
} | |||
public static List<PushLogEntry> getPushLog(String repositoryName, Repository repository, Date minimumDate, int maxCount) { | |||
List<PushLogEntry> list = new ArrayList<PushLogEntry>(); | |||
RefModel ref = getPushLogBranch(repository); | |||
if (ref == null) { | |||
return list; | |||
} | |||
List<RevCommit> pushes; | |||
if (minimumDate == null) { | |||
pushes = JGitUtils.getRevLog(repository, GB_PUSHES, 0, maxCount); | |||
} else { | |||
pushes = JGitUtils.getRevLog(repository, GB_PUSHES, minimumDate); | |||
} | |||
for (RevCommit push : pushes) { | |||
if (push.getAuthorIdent().getName().equalsIgnoreCase("gitblit")) { | |||
// skip gitblit/internal commits | |||
continue; | |||
} | |||
Date date = push.getAuthorIdent().getWhen(); | |||
UserModel user = new UserModel(push.getAuthorIdent().getEmailAddress()); | |||
user.displayName = push.getAuthorIdent().getName(); | |||
PushLogEntry log = new PushLogEntry(repositoryName, date, user); | |||
list.add(log); | |||
List<PathChangeModel> changedRefs = JGitUtils.getFilesInCommit(repository, push); | |||
for (PathChangeModel change : changedRefs) { | |||
switch (change.changeType) { | |||
case DELETE: | |||
break; | |||
case ADD: | |||
case MODIFY: | |||
String content = JGitUtils.getStringContent(repository, push.getTree(), change.path); | |||
String [] fields = content.split(" "); | |||
String oldId = fields[1]; | |||
String newId = fields[2]; | |||
List<RevCommit> pushedCommits = JGitUtils.getRevLog(repository, oldId, newId); | |||
for (RevCommit pushedCommit : pushedCommits) { | |||
log.addCommit(change.path, pushedCommit); | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
} | |||
Collections.sort(list); | |||
return list; | |||
} | |||
} |
@@ -27,7 +27,7 @@ import com.gitblit.Constants; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.Keys; | |||
import com.gitblit.models.Activity; | |||
import com.gitblit.models.Activity.RepositoryCommit; | |||
import com.gitblit.models.RepositoryCommit; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.pages.CommitDiffPage; |
@@ -129,8 +129,14 @@ public class RefsPanel extends Panel { | |||
name = name.substring(Constants.R_TAGS.length()); | |||
cssClass = "tagRef"; | |||
} else if (name.startsWith(Constants.R_NOTES)) { | |||
// codereview refs | |||
linkClass = CommitPage.class; | |||
cssClass = "otherRef"; | |||
} else if (name.startsWith(com.gitblit.Constants.R_GITBLIT)) { | |||
// gitblit refs | |||
linkClass = LogPage.class; | |||
cssClass = "otherRef"; | |||
name = name.substring(com.gitblit.Constants.R_GITBLIT.length()); | |||
} | |||
Component c = new LinkPanel("refName", null, name, linkClass, |
@@ -7,9 +7,11 @@ import static org.junit.Assert.assertTrue; | |||
import java.io.BufferedWriter; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.OutputStreamWriter; | |||
import java.text.MessageFormat; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.concurrent.atomic.AtomicBoolean; | |||
import org.eclipse.jgit.api.CloneCommand; | |||
@@ -18,6 +20,7 @@ import org.eclipse.jgit.api.ResetCommand.ResetType; | |||
import org.eclipse.jgit.api.errors.GitAPIException; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
import org.eclipse.jgit.storage.file.FileRepository; | |||
import org.eclipse.jgit.transport.CredentialsProvider; | |||
import org.eclipse.jgit.transport.PushResult; | |||
import org.eclipse.jgit.transport.RefSpec; | |||
@@ -34,9 +37,11 @@ import com.gitblit.Constants.AccessRestrictionType; | |||
import com.gitblit.Constants.AuthorizationControl; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.Keys; | |||
import com.gitblit.models.PushLogEntry; | |||
import com.gitblit.models.RepositoryModel; | |||
import com.gitblit.models.UserModel; | |||
import com.gitblit.utils.JGitUtils; | |||
import com.gitblit.utils.PushLogUtils; | |||
public class GitServletTest { | |||
@@ -756,4 +761,14 @@ public class GitServletTest { | |||
GitBlitSuite.close(git); | |||
GitBlit.self().deleteUser(user.username); | |||
} | |||
@Test | |||
public void testPushLog() throws IOException { | |||
String name = "refchecks/ticgit.git"; | |||
File refChecks = new File(GitBlitSuite.REPOSITORIES, name); | |||
FileRepository repository = new FileRepository(refChecks); | |||
List<PushLogEntry> pushes = PushLogUtils.getPushLog(name, repository); | |||
GitBlitSuite.close(repository); | |||
assertTrue("Repository has an empty push log!", pushes.size() > 0); | |||
} | |||
} |
@@ -0,0 +1,37 @@ | |||
/* | |||
* Copyright 2013 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit.tests; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.util.List; | |||
import org.eclipse.jgit.storage.file.FileRepository; | |||
import org.junit.Test; | |||
import com.gitblit.models.PushLogEntry; | |||
import com.gitblit.utils.PushLogUtils; | |||
public class PushLogTest { | |||
@Test | |||
public void testPushLog() throws IOException { | |||
String name = "~james/helloworld.git"; | |||
FileRepository repository = new FileRepository(new File(GitBlitSuite.REPOSITORIES, name)); | |||
List<PushLogEntry> pushes = PushLogUtils.getPushLog(name, repository); | |||
GitBlitSuite.close(repository); | |||
} | |||
} |