Java tutorial
/* * Copyright 2016 the original author or authors. * * 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.mooregreatsoftware.gitprocess.lib.config; import com.mooregreatsoftware.gitprocess.config.BranchConfig; import com.mooregreatsoftware.gitprocess.config.RemoteConfig; import com.mooregreatsoftware.gitprocess.lib.Branch; import com.mooregreatsoftware.gitprocess.lib.Branches; import com.mooregreatsoftware.gitprocess.lib.ExecUtils; import com.mooregreatsoftware.gitprocess.lib.StreamUtils; import org.checkerframework.checker.nullness.qual.Nullable; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Iterator; import java.util.Optional; import java.util.stream.Collectors; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_BRANCH_SECTION; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_MERGE; import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_REMOTE; import static org.eclipse.jgit.lib.Constants.MASTER; @SuppressWarnings({ "ConstantConditions", "RedundantTypeArguments" }) public class StoredBranchConfig extends AbstractStoredConfig implements BranchConfig { private static final Logger LOG = LoggerFactory.getLogger(StoredBranchConfig.class); private final RemoteConfig remoteConfig; private final Branches branches; public StoredBranchConfig(StoredConfig storedConfig, RemoteConfig remoteConfig, Branches branches) { super(storedConfig); this.remoteConfig = remoteConfig; this.branches = branches; } /** * Returns the integration branch to use. * <p> * If one is explicitly defined in "gitProcess.integrationBranch" then always use that. * If there are remotes defined, then gets the "master" branch from the default remote. * Otherwise checks to see if "master" exists locally. * * @return null if it can't compute a reasonable value * @see RemoteConfig#remoteName() */ @Override @Nullable public Branch integrationBranch() { final Branch configIntegrationBranch = integrationBranchFromGitConfig(); if (configIntegrationBranch != null) return configIntegrationBranch; return remoteConfig.hasRemotes() ? integrationBranchFromRemoteBranches() : integrationBranchFromLocalBranches(); } /** * Set (and writes to git configuration) the integration branch to use */ @Override public BranchConfig integrationBranch(Branch branch) { if (branch == null) throw new IllegalArgumentException("branch == null"); LOG.debug("Setting integration branch to {}", branch.name()); setString(GIT_PROCESS_SECTION_NAME, null, INTEGRATION_BRANCH_KEY, branch.name()); return this; } @Nullable private Branch integrationBranchFromRemoteBranches() { // TODO: Figure out how to get the remote HEAD. FETCH_HEAD? // Appears to be indirectly in FetchResult.getAdvertisedRefs() return ExecUtils.<@Nullable Branch>e(() -> { // if remote has "master", assume that's the integration branch. // otherwise give up and return empty() final String remoteName = remoteConfig.remoteName(); if (remoteName == null) return null; final String lookFor = remoteName + "/" + MASTER; final Branch branch = searchForBranch(branches.remoteBranches(), lookFor); logIntegrationBranch(lookFor, branch, branches.remoteBranches()); return branch; }); } @Nullable private Branch integrationBranchFromLocalBranches() { return ExecUtils.<@Nullable Branch>e(() -> { // if have "master", assume that's the integration branch. // otherwise give up and return empty() final String lookFor = MASTER; final Branch branch = searchForBranch(branches.localBranches(), lookFor); logIntegrationBranch(lookFor, branch, branches.localBranches()); return branch; }); } @Nullable private Branch searchForBranch(Iterator<Branch> branches, String branchName) { return StreamUtils.stream(branches).filter(branch -> branch.shortName().equals(branchName)).findFirst() .orElse(null); } private static void logIntegrationBranch(String branchName, @Nullable Branch branch, Iterator<Branch> branches) { if (branch != null) { LOG.debug("integrationBranch(): have a \"{}\" branch", branch.shortName()); } else { LOG.warn("Do not have a \"{}\" branch: [{}]\n" + "Fix with `git config {}.{} [branch_name]`", branchName, StreamUtils.stream(branches).map(Branch::shortName).collect(Collectors.joining(", ")), GIT_PROCESS_SECTION_NAME, INTEGRATION_BRANCH_KEY); } } @Nullable private Branch integrationBranchFromGitConfig() { final String branchName = getString(GIT_PROCESS_SECTION_NAME, null, INTEGRATION_BRANCH_KEY); if (branchName != null) { LOG.debug("integrationBranch(): {}.{} has a value of \"{}\" so using that", GIT_PROCESS_SECTION_NAME, INTEGRATION_BRANCH_KEY, branchName); return branches.branch(branchName); } return null; } @Override public BranchConfig setUpstream(Branch branch, Branch upstream) { final String branchShortName = branch.shortName(); Optional<String> remoteName = upstream.remoteName(); if (remoteName.isPresent()) { String upstreamBranchName = upstream.shortName(); setString(CONFIG_BRANCH_SECTION, branchShortName, CONFIG_KEY_REMOTE, remoteName.get()); final String upstreamNameWithoutRemote = upstreamBranchName.substring(remoteName.get().length() + 1); setString(CONFIG_BRANCH_SECTION, branchShortName, CONFIG_KEY_MERGE, Constants.R_HEADS + upstreamNameWithoutRemote); LOG.info("Setting upstream for \"{}\" to remote \"{}\" on \"{}\"", branch.shortName(), upstreamNameWithoutRemote, remoteName.get()); } else { setString(CONFIG_BRANCH_SECTION, branchShortName, CONFIG_KEY_REMOTE, "."); setString(CONFIG_BRANCH_SECTION, branchShortName, CONFIG_KEY_MERGE, upstream.shortName()); LOG.info("Setting upstream for \"{}\" to local \"{}\"", branch.shortName(), upstream.shortName()); } return this; } @Override @Nullable @SuppressWarnings("RedundantCast") public Branch getUpstream(Branch branch) { final String remoteName = getString(CONFIG_BRANCH_SECTION, branch.shortName(), CONFIG_KEY_REMOTE); if (remoteName == null) { LOG.debug("There is no upstream set for \"{}\"", branch.shortName()); return null; } final String remotePath = (remoteName.equals(".") ? "" : remoteName + "/"); final String upstreamBranchName = getString(CONFIG_BRANCH_SECTION, branch.shortName(), CONFIG_KEY_MERGE); if (upstreamBranchName == null) { throw new IllegalStateException("There is no configuration value for \"" + CONFIG_BRANCH_SECTION + '.' + branch.shortName() + '.' + CONFIG_KEY_MERGE + "\", which is an invalid Git state"); } final String fullRemotePath = remotePath + Repository.shortenRefName(upstreamBranchName); final Branch upstream = branches.branch(fullRemotePath); if (upstream == null) { throw new IllegalArgumentException( "The upstream for \"" + branch.shortName() + "\" (\"" + fullRemotePath + "\") does not exist"); } return upstream; } }