From 8051a29d8b3826772e5be1851eb72cd52ab5080c Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Fri, 13 Jan 2012 08:04:06 -0500 Subject: [PATCH] Fixed upgrade bug in user service (issue 41) --- src/com/gitblit/utils/JGitUtils.java | 295 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 246 insertions(+), 49 deletions(-) diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java index b6b13ab..a540c2a 100644 --- a/src/com/gitblit/utils/JGitUtils.java +++ b/src/com/gitblit/utils/JGitUtils.java @@ -37,6 +37,8 @@ import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.FetchCommand; import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.ResetCommand; +import org.eclipse.jgit.api.ResetCommand.ResetType; import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffEntry.ChangeType; import org.eclipse.jgit.diff.DiffFormatter; @@ -45,21 +47,27 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.StopWalkException; +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.ObjectLoader; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.RefUpdate.Result; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryCache.FileKey; import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.lib.TreeFormatter; import org.eclipse.jgit.revwalk.RevBlob; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevObject; import org.eclipse.jgit.revwalk.RevSort; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter; import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.storage.file.FileRepository; import org.eclipse.jgit.transport.CredentialsProvider; @@ -138,6 +146,7 @@ * Encapsulates the result of cloning or pulling from a repository. */ public static class CloneResult { + public String name; public FetchResult fetchResult; public boolean createdRepository; } @@ -155,7 +164,7 @@ */ public static CloneResult cloneRepository(File repositoriesFolder, String name, String fromUrl) throws Exception { - return cloneRepository(repositoriesFolder, name, fromUrl, null); + return cloneRepository(repositoriesFolder, name, fromUrl, true, null); } /** @@ -166,16 +175,27 @@ * @param repositoriesFolder * @param name * @param fromUrl + * @param bare * @param credentialsProvider * @return CloneResult * @throws Exception */ public static CloneResult cloneRepository(File repositoriesFolder, String name, String fromUrl, - CredentialsProvider credentialsProvider) throws Exception { + boolean bare, CredentialsProvider credentialsProvider) throws Exception { CloneResult result = new CloneResult(); - if (!name.toLowerCase().endsWith(Constants.DOT_GIT_EXT)) { - name += Constants.DOT_GIT_EXT; + if (bare) { + // bare repository, ensure .git suffix + if (!name.toLowerCase().endsWith(Constants.DOT_GIT_EXT)) { + name += Constants.DOT_GIT_EXT; + } + } else { + // normal repository, strip .git suffix + if (name.toLowerCase().endsWith(Constants.DOT_GIT_EXT)) { + name = name.substring(0, name.indexOf(Constants.DOT_GIT_EXT)); + } } + result.name = name; + File folder = new File(repositoriesFolder, name); if (folder.exists()) { File gitDir = FileKey.resolve(new File(repositoriesFolder, name), FS.DETECTED); @@ -184,7 +204,7 @@ repository.close(); } else { CloneCommand clone = new CloneCommand(); - clone.setBare(true); + clone.setBare(bare); clone.setCloneAllBranches(true); clone.setURI(fromUrl); clone.setDirectory(folder); @@ -229,7 +249,28 @@ fetch.setCredentialsProvider(credentialsProvider); } fetch.setRefSpecs(specs); - FetchResult result = fetch.call(); + FetchResult fetchRes = fetch.call(); + return fetchRes; + } + + /** + * Reset HEAD to the latest remote tracking commit. + * + * @param repository + * @param remoteRef + * the remote tracking reference (e.g. origin/master) + * @return Ref + * @throws Exception + */ + public static Ref resetHEAD(Repository repository, String remoteRef) throws Exception { + if (!remoteRef.startsWith(Constants.R_REMOTES)) { + remoteRef = Constants.R_REMOTES + remoteRef; + } + Git git = new Git(repository); + ResetCommand reset = git.reset(); + reset.setMode(ResetType.SOFT); + reset.setRef(remoteRef); + Ref result = reset.call(); return result; } @@ -264,7 +305,7 @@ } list.addAll(getRepositoryList(repositoriesFolder.getAbsolutePath(), repositoriesFolder, exportAll, searchSubfolders)); - Collections.sort(list); + StringUtils.sortRepositorynames(list); return list; } @@ -432,6 +473,19 @@ return new Date(0); } return new Date(commit.getCommitTime() * 1000L); + } + + /** + * Retrieves a Java Date from a Git commit. + * + * @param commit + * @return date of the commit or Date(0) if the commit is null + */ + public static Date getAuthorDate(RevCommit commit) { + if (commit == null) { + return new Date(0); + } + return commit.getAuthorIdent().getWhen(); } /** @@ -789,6 +843,45 @@ } /** + * Returns a list of commits since the minimum date starting from the + * specified object id. + * + * @param repository + * @param objectId + * if unspecified, HEAD is assumed. + * @param minimumDate + * @return list of commits + */ + public static List<RevCommit> getRevLog(Repository repository, String objectId, Date minimumDate) { + List<RevCommit> list = new ArrayList<RevCommit>(); + if (!hasCommits(repository)) { + return list; + } + try { + // resolve branch + ObjectId branchObject; + if (StringUtils.isEmpty(objectId)) { + branchObject = getDefaultBranch(repository); + } else { + branchObject = repository.resolve(objectId); + } + + RevWalk rw = new RevWalk(repository); + rw.markStart(rw.parseCommit(branchObject)); + rw.setRevFilter(CommitTimeRevFilter.after(minimumDate)); + Iterable<RevCommit> revlog = rw; + for (RevCommit rev : revlog) { + list.add(rev); + } + rw.dispose(); + } catch (Throwable t) { + error(t, repository, "{0} failed to get {1} revlog for minimum date {2}", objectId, + minimumDate); + } + return list; + } + + /** * Returns a list of commits starting from HEAD and working backwards. * * @param repository @@ -890,24 +983,47 @@ } /** - * Enumeration of the search types. + * Returns a list of commits for the repository within the range specified + * by startRangeId and endRangeId. If the repository does not exist or is + * empty, an empty list is returned. + * + * @param repository + * @param startRangeId + * the first commit (not included in results) + * @param endRangeId + * the end commit (included in results) + * @return a list of commits */ - public static enum SearchType { - AUTHOR, COMMITTER, COMMIT; + public static List<RevCommit> getRevLog(Repository repository, String startRangeId, + String endRangeId) { + List<RevCommit> list = new ArrayList<RevCommit>(); + if (!hasCommits(repository)) { + return list; + } + try { + ObjectId endRange = repository.resolve(endRangeId); + ObjectId startRange = repository.resolve(startRangeId); - public static SearchType forName(String name) { - for (SearchType type : values()) { - if (type.name().equalsIgnoreCase(name)) { - return type; - } + RevWalk rw = new RevWalk(repository); + rw.markStart(rw.parseCommit(endRange)); + if (startRange.equals(ObjectId.zeroId())) { + // maybe this is a tag or an orphan branch + list.add(rw.parseCommit(endRange)); + rw.dispose(); + return list; + } else { + rw.markUninteresting(rw.parseCommit(startRange)); } - return COMMIT; - } - @Override - public String toString() { - return name().toLowerCase(); + Iterable<RevCommit> revlog = rw; + for (RevCommit rev : revlog) { + list.add(rev); + } + rw.dispose(); + } catch (Throwable t) { + error(t, repository, "{0} failed to get revlog for {1}..{2}", startRangeId, endRangeId); } + return list; } /** @@ -928,7 +1044,7 @@ * @return matching list of commits */ public static List<RevCommit> searchRevlogs(Repository repository, String objectId, - String value, final SearchType type, int offset, int maxCount) { + String value, final com.gitblit.Constants.SearchType type, int offset, int maxCount) { final String lcValue = value.toLowerCase(); List<RevCommit> list = new ArrayList<RevCommit>(); if (maxCount == 0) { @@ -1173,6 +1289,50 @@ } /** + * Returns a RefModel for the gh-pages branch in the repository. If the + * branch can not be found, null is returned. + * + * @param repository + * @return a refmodel for the gh-pages branch or null + */ + public static RefModel getPagesBranch(Repository repository) { + return getBranch(repository, "gh-pages"); + } + + /** + * Returns a RefModel for a specific branch name in the repository. If the + * branch can not be found, null is returned. + * + * @param repository + * @return a refmodel for the branch or null + */ + public static RefModel getBranch(Repository repository, String name) { + RefModel branch = null; + try { + // search for the branch in local heads + for (RefModel ref : JGitUtils.getLocalBranches(repository, false, -1)) { + if (ref.displayName.endsWith(name)) { + branch = ref; + break; + } + } + + // search for the branch in remote heads + if (branch == null) { + for (RefModel ref : JGitUtils.getRemoteBranches(repository, false, -1)) { + if (ref.displayName.endsWith(name)) { + branch = ref; + break; + } + } + } + } catch (Throwable t) { + LOGGER.error(MessageFormat.format("Failed to find {0} branch!", name), t); + } + return branch; + } + + /** * Returns the list of notes entered about the commit from the refs/notes * namespace. If the repository does not exist or is empty, an empty list is * returned. @@ -1205,37 +1365,74 @@ } /** - * Create an orphaned branch in a repository. This code does not work. + * Create an orphaned branch in a repository. * * @param repository - * @param name - * @return + * @param branchName + * @param author + * if unspecified, Gitblit will be the author of this new branch + * @return true if successful */ - public static boolean createOrphanBranch(Repository repository, String name) { - return true; - // boolean success = false; - // try { - // ObjectId prev = repository.resolve(Constants.HEAD + "^1"); - // // create the orphan branch - // RefUpdate orphanRef = repository.updateRef(Constants.R_HEADS + name); - // orphanRef.setNewObjectId(prev); - // orphanRef.setExpectedOldObjectId(ObjectId.zeroId()); - // Result updateResult = orphanRef.update(); - // - // switch (updateResult) { - // case NEW: - // success = true; - // break; - // case NO_CHANGE: - // default: - // break; - // } - // - // } catch (Throwable t) { - // error(t, repository, "{0} failed to create orphaned branch {1}", - // name); - // } - // return success; + public static boolean createOrphanBranch(Repository repository, String branchName, + PersonIdent author) { + boolean success = false; + String message = "Created branch " + branchName; + if (author == null) { + author = new PersonIdent("Gitblit", "gitblit@localhost"); + } + try { + ObjectInserter odi = repository.newObjectInserter(); + try { + // Create a blob object to insert into a tree + ObjectId blobId = odi.insert(Constants.OBJ_BLOB, + message.getBytes(Constants.CHARACTER_ENCODING)); + + // Create a tree object to reference from a commit + TreeFormatter tree = new TreeFormatter(); + tree.append("NEWBRANCH", FileMode.REGULAR_FILE, blobId); + ObjectId treeId = odi.insert(tree); + + // Create a commit object + CommitBuilder commit = new CommitBuilder(); + commit.setAuthor(author); + commit.setCommitter(author); + commit.setEncoding(Constants.CHARACTER_ENCODING); + commit.setMessage(message); + commit.setTreeId(treeId); + + // Insert the commit into the repository + ObjectId commitId = odi.insert(commit); + odi.flush(); + + RevWalk revWalk = new RevWalk(repository); + try { + RevCommit revCommit = revWalk.parseCommit(commitId); + if (!branchName.startsWith("refs/")) { + branchName = "refs/heads/" + branchName; + } + RefUpdate ru = repository.updateRef(branchName); + ru.setNewObjectId(commitId); + ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false); + Result rc = ru.forceUpdate(); + switch (rc) { + case NEW: + case FORCED: + case FAST_FORWARD: + success = true; + break; + default: + success = false; + } + } finally { + revWalk.release(); + } + } finally { + odi.release(); + } + } catch (Throwable t) { + error(t, repository, "Failed to create orphan branch {1} in repository {0}", branchName); + } + return success; } /** -- Gitblit v1.9.1