Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added sparse checkout support #30

Merged
merged 3 commits into from
Jan 12, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.jenkinsci.plugins.gitclient;

import java.util.Collections;
import java.util.List;

/**
* @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a>
*/
Expand All @@ -8,6 +11,7 @@ public abstract class CheckoutCommand implements GitCommand {
public String ref;
public String branch;
public boolean deleteBranch;
public List<String> sparseCheckoutPaths = Collections.emptyList();

public CheckoutCommand ref(String ref) {
this.ref = ref;
Expand All @@ -23,4 +27,9 @@ public CheckoutCommand deleteBranchIfExist(boolean deleteBranch) {
this.deleteBranch = deleteBranch;
return this;
}

public CheckoutCommand sparseCheckoutPaths(List<String> sparseCheckoutPaths) {
this.sparseCheckoutPaths = sparseCheckoutPaths == null ? Collections.<String>emptyList() : sparseCheckoutPaths;
return this;
}
}
70 changes: 69 additions & 1 deletion src/main/java/org/jenkinsci/plugins/gitclient/CliGitAPIImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
import com.google.common.collect.Lists;
import hudson.*;
import hudson.Launcher.LocalLauncher;
import hudson.model.TaskListener;
Expand Down Expand Up @@ -45,6 +46,8 @@
*/
public class CliGitAPIImpl extends LegacyCompatibleGitAPIImpl {

static final String SPARSE_CHECKOUT_FILE_PATH = ".git/info/sparse-checkout";

Launcher launcher;
TaskListener listener;
String gitExe;
Expand Down Expand Up @@ -213,7 +216,7 @@ public CloneCommand clone_() {
String url;
String origin;
String reference;
boolean shallow,shared;
boolean shallow,shared,noCheckout;

public CloneCommand url(String url) {
this.url = url;
Expand All @@ -235,6 +238,11 @@ public CloneCommand shallow() {
return this;
}

public CloneCommand noCheckout() {
this.noCheckout = true;
return this;
}

public CloneCommand reference(String reference) {
this.reference = reference;
return this;
Expand Down Expand Up @@ -272,6 +280,7 @@ else if (!referencePath.isDirectory())
if (shared)
args.add("--shared");
if(shallow) args.add("--depth", "1");
if(noCheckout) args.add("--no-checkout");

StandardCredentials cred = credentials.get(url);
if (cred == null) cred = defaultCredentials;
Expand Down Expand Up @@ -1009,6 +1018,10 @@ public CheckoutCommand checkout() {

public void execute() throws GitException, InterruptedException {
try {

// Will activate or deactivate sparse checkout depending on the given paths
sparseCheckout(sparseCheckoutPaths);

if (branch!=null && deleteBranch) {
// First, checkout to detached HEAD, so we can delete the branch.
launchCommand("checkout", "-f", ref);
Expand All @@ -1029,6 +1042,61 @@ public void execute() throws GitException, InterruptedException {
}

}

private void sparseCheckout(List<String> paths) throws GitException, InterruptedException {

boolean coreSparseCheckoutConfigEnable;
try {
coreSparseCheckoutConfigEnable = launchCommand("config", "core.sparsecheckout").contains("true");
} catch (GitException ge) {
coreSparseCheckoutConfigEnable = false;
}

boolean deactivatingSparseCheckout = false;
if(paths.isEmpty() && ! coreSparseCheckoutConfigEnable) { // Nothing to do
return;
} else if(paths.isEmpty() && coreSparseCheckoutConfigEnable) { // deactivating sparse checkout needed
deactivatingSparseCheckout = true;
paths = Lists.newArrayList("/*");
} else if(! coreSparseCheckoutConfigEnable) { // activating sparse checkout
launchCommand( "config", "core.sparsecheckout", "true" );
}

File sparseCheckoutFile = new File(workspace, SPARSE_CHECKOUT_FILE_PATH);
PrintWriter writer;
try {
writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(sparseCheckoutFile, false), "UTF-8"));
} catch (IOException ex){
throw new GitException("Impossible to open sparse checkout file " + sparseCheckoutFile.getAbsolutePath());
}

for(String path : paths) {
writer.println(path);
}

try {
writer.close();
} catch (Exception ex) {
throw new GitException("Impossible to close sparse checkout file " + sparseCheckoutFile.getAbsolutePath());
}


try {
launchCommand( "read-tree", "-mu", "HEAD" );
} catch (GitException ge) {
// Normal return code if sparse checkout path has never exist on the current checkout branch
String normalReturnCode = "128";
if(ge.getMessage().contains(normalReturnCode)) {
listener.getLogger().println(ge.getMessage());
} else {
throw ge;
}
}

if(deactivatingSparseCheckout) {
launchCommand( "config", "core.sparsecheckout", "false" );
}
}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ public interface CloneCommand extends GitCommand {
CloneCommand shared();

CloneCommand reference(String reference);

/**
* When we just need to clone repository without populating the workspace (for instance when sparse checkouts are used)
*/
CloneCommand noCheckout();
}
15 changes: 15 additions & 0 deletions src/main/java/org/jenkinsci/plugins/gitclient/JGitAPIImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ public CheckoutCommand checkout() {
return new CheckoutCommand() {

public void execute() throws GitException, InterruptedException {

if(! sparseCheckoutPaths.isEmpty()) {
listener.getLogger().println("[ERROR] JGit doesn't support sparse checkout.");
throw new UnsupportedOperationException("not implemented yet");
}

if (branch == null)
doCheckout(ref);
else if (deleteBranch)
Expand Down Expand Up @@ -454,6 +460,10 @@ public void setRemoteUrl(String name, String url) throws GitException {
}
}

public List<String> retrieveSparseCheckoutPaths() throws GitException, InterruptedException {
listener.getLogger().println("[WARNING] JGit doesn't support sparse checkout.");
return Collections.emptyList();
}

public void addNote(String note, String namespace) throws GitException {
try {
Expand Down Expand Up @@ -705,6 +715,11 @@ public CloneCommand reference(String reference) {
return this;
}

public CloneCommand noCheckout() {
base.setNoCheckout(true);
return this;
}

public void execute() throws GitException, InterruptedException {
try {
// the directory needs to be clean or else JGit complains
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import hudson.Launcher;
import hudson.Util;
import hudson.model.TaskListener;
Expand Down Expand Up @@ -176,7 +177,7 @@ WorkingArea clone(String src) throws Exception {
return new WorkingArea(x.repo);
}



@Override
protected void setUp() throws Exception {
Expand Down Expand Up @@ -547,6 +548,60 @@ public void test_getSubmodules() throws Exception {
);
}

@NotImplementedInJGit
public void test_sparse_checkout() throws Exception {
// Create a repo for cloning purpose
w.init();
w.commit("init");
w.file("dir1").mkdir();
w.touch("dir1/file1");
w.file("dir2").mkdir();
w.touch("dir2/file2");
w.file("dir3").mkdir();
w.touch("dir3/file3");
w.add("dir1/file1");
w.add("dir2/file2");
w.add("dir3/file3");
w.commit("commit");

// Clone it
WorkingArea workingArea = clone(w.repoPath());

workingArea.git.checkout().ref("origin/master").branch("master").deleteBranchIfExist(true).sparseCheckoutPaths(Lists.newArrayList("dir1")).execute();
assertTrue(workingArea.exists("dir1"));
assertFalse(workingArea.exists("dir2"));
assertFalse(workingArea.exists("dir3"));

workingArea.git.checkout().ref("origin/master").branch("master").deleteBranchIfExist(true).sparseCheckoutPaths(Lists.newArrayList("dir2")).execute();
assertFalse(workingArea.exists("dir1"));
assertTrue(workingArea.exists("dir2"));
assertFalse(workingArea.exists("dir3"));

workingArea.git.checkout().ref("origin/master").branch("master").deleteBranchIfExist(true).sparseCheckoutPaths(Lists.newArrayList("dir1", "dir2")).execute();
assertTrue(workingArea.exists("dir1"));
assertTrue(workingArea.exists("dir2"));
assertFalse(workingArea.exists("dir3"));

workingArea.git.checkout().ref("origin/master").branch("master").deleteBranchIfExist(true).sparseCheckoutPaths(Collections.<String>emptyList()).execute();
assertTrue(workingArea.exists("dir1"));
assertTrue(workingArea.exists("dir2"));
assertTrue(workingArea.exists("dir3"));
}

public void test_clone_no_checkout() throws Exception {
// Create a repo for cloning purpose
WorkingArea repoToClone = new WorkingArea();
repoToClone.init();
repoToClone.commit("init");
repoToClone.touch("file1");
repoToClone.add("file1");
repoToClone.commit("commit");

// Clone it with no checkout
w.git.clone_().url(repoToClone.repoPath()).repositoryName("origin").noCheckout().execute();
assertFalse(w.exists("file1"));
}

public void test_hasSubmodules() throws Exception {
w.init();

Expand Down