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