1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.scm.provider.git.jgit.command.branch;
20
21 import java.util.ArrayList;
22 import java.util.EnumSet;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.function.BiFunction;
29
30 import org.apache.maven.scm.ScmException;
31 import org.apache.maven.scm.ScmFile;
32 import org.apache.maven.scm.ScmFileSet;
33 import org.apache.maven.scm.ScmFileStatus;
34 import org.apache.maven.scm.ScmResult;
35 import org.apache.maven.scm.command.branch.AbstractBranchCommand;
36 import org.apache.maven.scm.command.branch.BranchScmResult;
37 import org.apache.maven.scm.provider.ScmProviderRepository;
38 import org.apache.maven.scm.provider.git.command.GitCommand;
39 import org.apache.maven.scm.provider.git.jgit.command.CustomizableSshSessionFactoryCommand;
40 import org.apache.maven.scm.provider.git.jgit.command.JGitTransportConfigCallback;
41 import org.apache.maven.scm.provider.git.jgit.command.JGitUtils;
42 import org.apache.maven.scm.provider.git.jgit.command.PushException;
43 import org.apache.maven.scm.provider.git.jgit.command.ScmProviderAwareSshdSessionFactory;
44 import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
45 import org.eclipse.jgit.api.Git;
46 import org.eclipse.jgit.api.TransportConfigCallback;
47 import org.eclipse.jgit.api.errors.GitAPIException;
48 import org.eclipse.jgit.lib.Constants;
49 import org.eclipse.jgit.lib.Ref;
50 import org.eclipse.jgit.lib.Repository;
51 import org.eclipse.jgit.revwalk.RevCommit;
52 import org.eclipse.jgit.revwalk.RevWalk;
53 import org.eclipse.jgit.transport.RefSpec;
54 import org.eclipse.jgit.transport.RemoteRefUpdate;
55 import org.eclipse.jgit.treewalk.TreeWalk;
56 import org.slf4j.Logger;
57
58
59
60
61
62 public class JGitBranchCommand extends AbstractBranchCommand
63 implements GitCommand, CustomizableSshSessionFactoryCommand {
64
65 private BiFunction<GitScmProviderRepository, Logger, ScmProviderAwareSshdSessionFactory> sshSessionFactorySupplier;
66
67 public JGitBranchCommand() {
68 sshSessionFactorySupplier = ScmProviderAwareSshdSessionFactory::new;
69 }
70
71 @Override
72 public void setSshSessionFactorySupplier(
73 BiFunction<GitScmProviderRepository, Logger, ScmProviderAwareSshdSessionFactory>
74 sshSessionFactorySupplier) {
75 this.sshSessionFactorySupplier = sshSessionFactorySupplier;
76 }
77
78
79
80
81 @Override
82 protected ScmResult executeBranchCommand(
83 ScmProviderRepository repo, ScmFileSet fileSet, String branch, String message) throws ScmException {
84 if (branch == null || branch.trim().isEmpty()) {
85 throw new ScmException("branch name must be specified");
86 }
87
88 if (!fileSet.getFileList().isEmpty()) {
89 throw new ScmException("This provider doesn't support branching subsets of a directory");
90 }
91 Git git = null;
92 try {
93 git = JGitUtils.openRepo(fileSet.getBasedir());
94 Ref branchResult = git.branchCreate().setName(branch).call();
95 logger.info("created [" + branchResult.getName() + "]");
96
97 if (logger.isDebugEnabled()) {
98 for (String branchName : getShortLocalBranchNames(git)) {
99 logger.debug("local branch available: " + branchName);
100 }
101 }
102
103 if (repo.isPushChanges()) {
104 logger.info("push branch [" + branch + "] to remote...");
105 TransportConfigCallback transportConfigCallback = new JGitTransportConfigCallback(
106 sshSessionFactorySupplier.apply((GitScmProviderRepository) repo, logger));
107
108 JGitUtils.push(
109 git,
110 (GitScmProviderRepository) repo,
111 new RefSpec(Constants.R_HEADS + branch),
112 EnumSet.of(RemoteRefUpdate.Status.OK, RemoteRefUpdate.Status.UP_TO_DATE),
113 Optional.of(transportConfigCallback));
114 }
115
116
117 final RevWalk revWalk = new RevWalk(git.getRepository());
118 RevCommit commit = revWalk.parseCommit(branchResult.getObjectId());
119 revWalk.close();
120
121 final TreeWalk walk = new TreeWalk(git.getRepository());
122 walk.reset();
123 walk.setRecursive(true);
124 walk.addTree(commit.getTree());
125
126 List<ScmFile> files = new ArrayList<>();
127 while (walk.next()) {
128 files.add(new ScmFile(walk.getPathString(), ScmFileStatus.CHECKED_OUT));
129 }
130 walk.close();
131
132 return new BranchScmResult("JGit branch", files);
133
134 } catch (PushException e) {
135 logger.debug("Failed to push branch", e);
136 return new BranchScmResult("JGit branch", "Failed to push changes: " + e.getMessage(), "", false);
137 } catch (Exception e) {
138 throw new ScmException("JGit branch failed!", e);
139 } finally {
140 JGitUtils.closeRepo(git);
141 }
142 }
143
144
145
146
147
148
149
150
151 public static Set<String> getShortLocalBranchNames(Git git) throws GitAPIException {
152 Set<String> branches = new HashSet<>();
153 Iterator<Ref> iter = git.branchList().call().iterator();
154 while (iter.hasNext()) {
155 branches.add(Repository.shortenRefName(iter.next().getName()));
156 }
157 return branches;
158 }
159 }