From 62bffa2211522bad148c8dd39ee52daa4e5771fd Mon Sep 17 00:00:00 2001 From: Offer Shostak Date: Thu, 2 Jun 2016 14:58:37 +0300 Subject: [PATCH] Add Push to Gerrit functionality: 1. Fix storing issue in JGitConfigImpl 2. Commit command scenario - Get gerrit changid property from configuration and set the commit command to set insert change ID to work with Gerrit 3. Push command scenario - Create PushResponse with details regarding updates and refs and use it in push command Change-Id: Ic54109810cdc6ba026447f691759ef0fc94924d0 Signed-off-by: Offer Shostak --- .../che/api/git/shared/PushResponse.java | 10 ++ .../org/eclipse/che/git/impl/PushTest.java | 1 - .../che/git/impl/jgit/JGitConfigImpl.java | 12 +++ .../che/git/impl/jgit/JGitConnection.java | 101 +++++++++++++++--- 4 files changed, 107 insertions(+), 17 deletions(-) diff --git a/wsagent/che-core-api-git-shared/src/main/java/org/eclipse/che/api/git/shared/PushResponse.java b/wsagent/che-core-api-git-shared/src/main/java/org/eclipse/che/api/git/shared/PushResponse.java index 7492f1e36d..bd30b830e5 100644 --- a/wsagent/che-core-api-git-shared/src/main/java/org/eclipse/che/api/git/shared/PushResponse.java +++ b/wsagent/che-core-api-git-shared/src/main/java/org/eclipse/che/api/git/shared/PushResponse.java @@ -11,6 +11,8 @@ package org.eclipse.che.api.git.shared; import org.eclipse.che.dto.shared.DTO; +import java.util.List; +import java.util.Map; /** * Info received from push response @@ -27,4 +29,12 @@ public interface PushResponse { String getCommandOutput(); PushResponse withCommandOutput(String commandOutput); + + /** set list of push updates */ + void setUpdates(List> updates); + + /** @return list of push updates */ + List> getUpdates(); + + PushResponse withUpdates(List> updates); } diff --git a/wsagent/che-core-api-git/src/test/java/org/eclipse/che/git/impl/PushTest.java b/wsagent/che-core-api-git/src/test/java/org/eclipse/che/git/impl/PushTest.java index 104d23dd96..115196f9ab 100644 --- a/wsagent/che-core-api-git/src/test/java/org/eclipse/che/git/impl/PushTest.java +++ b/wsagent/che-core-api-git/src/test/java/org/eclipse/che/git/impl/PushTest.java @@ -103,7 +103,6 @@ public class PushTest { assertEquals(branchesAfter - 1, branchesBefore); } - @Test(dataProvider = "GitConnectionFactory", dataProviderClass = GitConnectionFactoryProvider.class, expectedExceptions = GitException.class, expectedExceptionsMessageRegExp = "No remote repository specified. " + "Please, specify either a URL or a remote name from which new revisions should be fetched in request.") diff --git a/wsagent/che-core-git-impl-jgit/src/main/java/org/eclipse/che/git/impl/jgit/JGitConfigImpl.java b/wsagent/che-core-git-impl-jgit/src/main/java/org/eclipse/che/git/impl/jgit/JGitConfigImpl.java index 886005f021..551165ea4f 100644 --- a/wsagent/che-core-git-impl-jgit/src/main/java/org/eclipse/che/git/impl/jgit/JGitConfigImpl.java +++ b/wsagent/che-core-git-impl-jgit/src/main/java/org/eclipse/che/git/impl/jgit/JGitConfigImpl.java @@ -16,6 +16,7 @@ import org.eclipse.che.api.git.GitException; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -29,6 +30,7 @@ import static java.lang.System.lineSeparator; * @author Tareq Sharafy (tareq.sha@sap.com) */ class JGitConfigImpl extends Config { + private final Repository repository; JGitConfigImpl(Repository repository) throws GitException { @@ -96,6 +98,11 @@ class JGitConfigImpl extends Config { public Config set(String name, String value) throws GitException { ConfigKey key = parseName(name); repository.getConfig().setString(key.section, key.subsection, key.name, value); + try { + this.repository.getConfig().save(); + } catch (IOException e) { + throw new GitException(e.getMessage(), e); + } return this; } @@ -108,6 +115,11 @@ class JGitConfigImpl extends Config { public Config unset(String name) throws GitException { ConfigKey key = parseName(name); repository.getConfig().unset(key.section, key.subsection, key.name); + try { + this.repository.getConfig().save(); + } catch (IOException e) { + throw new GitException(e.getMessage(), e); + } return this; } diff --git a/wsagent/che-core-git-impl-jgit/src/main/java/org/eclipse/che/git/impl/jgit/JGitConnection.java b/wsagent/che-core-git-impl-jgit/src/main/java/org/eclipse/che/git/impl/jgit/JGitConnection.java index 73f0762359..4739e23dcb 100644 --- a/wsagent/che-core-git-impl-jgit/src/main/java/org/eclipse/che/git/impl/jgit/JGitConnection.java +++ b/wsagent/che-core-git-impl-jgit/src/main/java/org/eclipse/che/git/impl/jgit/JGitConnection.java @@ -125,6 +125,7 @@ import org.eclipse.jgit.transport.PushResult; import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.jgit.transport.RemoteRefUpdate; +import org.eclipse.jgit.transport.TrackingRefUpdate; import org.eclipse.jgit.transport.SshSessionFactory; import org.eclipse.jgit.transport.SshTransport; import org.eclipse.jgit.transport.Transport; @@ -150,6 +151,7 @@ import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -176,6 +178,21 @@ class JGitConnection implements GitConnection { private static final String REBASE_OPERATION_ABORT = "ABORT"; private static final String ADD_ALL_OPTION = "all"; + // Push Response Constants + private static final String BRANCH_REFSPEC_SEPERATOR = " -> "; + private static final String REFSPEC_COLON = ":"; + private static final String KEY_COMMIT_MESSAGE = "Message"; + private static final String KEY_RESULT = "Result"; + private static final String KEY_REMOTENAME = "RemoteName"; + private static final String KEY_LOCALNAME = "LocalName"; + private static final String INFO_PUSH_ATTEMPT_IGNORED_UP_TO_DATE = "Could not push because the repository is up-to-date."; + private static final String ERROR_PUSH_ATTEMPT_FAILED_WITHMSG = "Could not push refspec {0} to the branch {1}. Try to merge " + + "the remote changes using pull, and then push again. \n Error " + + "Status: {2}, Error message {3}. \n"; + private static final String ERROR_PUSH_ATTEMPT_FAILED_WITHOUTMSG = "Could not push refspec {0} to the branch {1}. Try to merge " + + "the remote changes using pull, and then push again. \n " + + "Error Status: {2}. \n"; + private static final String ERROR_UPDATE_REMOTE_NAME_MISSING = "Update operation failed, remote name is required."; private static final String ERROR_ADD_REMOTE_NAME_MISSING = "Add operation failed, remote name is required."; private static final String ERROR_REMOTE_NAME_ALREADY_EXISTS = "Add Remote operation failed, remote name %s already exists."; @@ -491,6 +508,12 @@ class JGitConnection implements GitConnection { .setAll(request.isAll()) .setAmend(request.isAmend()); + // Check if repository is configured with Gerrit Support + String gerritSupportConfigValue = repository.getConfig().getString( + ConfigConstants.CONFIG_GERRIT_SECTION, null, + ConfigConstants.CONFIG_KEY_CREATECHANGEID); + boolean isGerritSupportConfigured = gerritSupportConfigValue != null ? Boolean.valueOf(gerritSupportConfigValue) : false; + commitCommand.setInsertChangeId(isGerritSupportConfigured); RevCommit result = commitCommand.call(); GitUser gitUser = newDto(GitUser.class).withName(committerName).withEmail(committerEmail); @@ -957,8 +980,8 @@ class JGitConnection implements GitConnection { public PushResponse push(PushRequest request) throws GitException, UnauthorizedException { String remoteName = request.getRemote(); String remoteUri = getRepository().getConfig().getString(ConfigConstants.CONFIG_REMOTE_SECTION, remoteName, - ConfigConstants.CONFIG_KEY_URL); - + ConfigConstants.CONFIG_KEY_URL); + PushResponse pushResponseDto = newDto(PushResponse.class); try { PushCommand pushCommand = getGit().push(); @@ -980,19 +1003,9 @@ class JGitConnection implements GitConnection { } @SuppressWarnings("unchecked") - Iterable list = (Iterable)executeRemoteCommand(remoteUri, pushCommand); - for (PushResult pushResult : list) { - Collection refUpdates = pushResult.getRemoteUpdates(); - for (RemoteRefUpdate remoteRefUpdate : refUpdates) { - if (!RemoteRefUpdate.Status.OK.equals(remoteRefUpdate.getStatus())) { - if (RemoteRefUpdate.Status.UP_TO_DATE.equals(remoteRefUpdate.getStatus())) { - return newDto(PushResponse.class).withCommandOutput("Everything up-to-date"); - } else { - throw new GitException(remoteRefUpdate.getStatus().toString()); - } - } - } - } + Iterable pushResults = (Iterable)executeRemoteCommand(remoteUri, pushCommand); + PushResult pushResult = pushResults.iterator().next(); + return addCommandOutputUpdates(pushResponseDto, request, pushResult); } catch (GitAPIException exception) { if ("origin: not found.".equals(exception.getMessage())) { throw new GitException(ERROR_NO_REMOTE_REPOSITORY, exception); @@ -1000,7 +1013,63 @@ class JGitConnection implements GitConnection { throw new GitException(exception.getMessage(), exception); } } - return newDto(PushResponse.class).withCommandOutput("Successfully pushed to " + remoteUri); + } + + private PushResponse addCommandOutputUpdates(PushResponse pushResponseDto, final PushRequest request, + final PushResult result) throws GitException { + String commandOutput = result.getMessages(); + final Collection refUpdates = result.getRemoteUpdates(); + final List> updates = new ArrayList<>(); + final String currentBranch = getCurrentBranch(); + + for (RemoteRefUpdate remoteRefUpdate : refUpdates) { + final String remoteRefName = remoteRefUpdate.getRemoteName(); + // check status only for branch given in the URL or tags - (handle special "refs/for" case) + String shortenRefFor = remoteRefName.startsWith("refs/for/") ? + remoteRefName.substring("refs/for/".length()) : + remoteRefName; + if (currentBranch.equals(Repository.shortenRefName(remoteRefName)) || currentBranch.equals(shortenRefFor) + || remoteRefName.startsWith(Constants.R_TAGS)) { + Map update = new HashMap<>(); + RemoteRefUpdate.Status status = remoteRefUpdate.getStatus(); + if (status != RemoteRefUpdate.Status.UP_TO_DATE || !remoteRefName.startsWith(Constants.R_TAGS)) { + update.put(KEY_COMMIT_MESSAGE, remoteRefUpdate.getMessage()); + update.put(KEY_RESULT, status.name()); + TrackingRefUpdate refUpdate = remoteRefUpdate.getTrackingRefUpdate(); + if (refUpdate != null) { + update.put(KEY_REMOTENAME, Repository.shortenRefName(refUpdate.getLocalName())); + update.put(KEY_LOCALNAME, Repository.shortenRefName(refUpdate.getRemoteName())); + } else { + update.put(KEY_REMOTENAME, Repository.shortenRefName(remoteRefUpdate.getSrcRef())); + update.put(KEY_LOCALNAME, Repository.shortenRefName(remoteRefUpdate.getRemoteName())); + } + updates.add(update); + } + if (status != RemoteRefUpdate.Status.OK) { + commandOutput = buildPushFailedMessage(request, remoteRefUpdate, currentBranch); + } + } + } + return pushResponseDto.withCommandOutput(commandOutput).withUpdates(updates); + } + + private String buildPushFailedMessage(final PushRequest request, final RemoteRefUpdate remoteRefUpdate, + final String currentBranch) { + String message; + if (remoteRefUpdate.getStatus() == RemoteRefUpdate.Status.UP_TO_DATE) { + message = INFO_PUSH_ATTEMPT_IGNORED_UP_TO_DATE; + } else { + String refspec = + currentBranch + BRANCH_REFSPEC_SEPERATOR + request.getRefSpec().get(0).split(REFSPEC_COLON)[1]; + if (remoteRefUpdate.getMessage() != null) { + message = String.format(ERROR_PUSH_ATTEMPT_FAILED_WITHMSG, refspec, request.getRemote(), + remoteRefUpdate.getStatus(), remoteRefUpdate.getMessage()); + } else { + message = String.format(ERROR_PUSH_ATTEMPT_FAILED_WITHOUTMSG, refspec, request.getRemote(), + remoteRefUpdate.getStatus()); + } + } + return message; } @Override