Skip to content

Commit

Permalink
Merge pull request #30 from Tetradeus/sparse-checkout
Browse files Browse the repository at this point in the history
Added sparse checkout support
  • Loading branch information
ndeloof committed Jan 12, 2014
2 parents 0b64dfc + df74073 commit a00dff2
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 2 deletions.
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

1 comment on commit a00dff2

@Tetradeus
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Nicolas,

When do you think to merge this branch into master ?
I also have corresponding modifications in git-plugin and with time I'll have to fight again with conflicts.

Thanks for your reply.

Thomas

Please sign in to comment.