Package org.eclipse.jgit.api

Source Code of org.eclipse.jgit.api.PullCommandTest

/*
* Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.com>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
*   notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
*   copyright notice, this list of conditions and the following
*   disclaimer in the documentation and/or other materials provided
*   with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
*   names of its contributors may be used to endorse or promote
*   products derived from this software without specific prior
*   written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.api;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.Callable;

import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
import org.eclipse.jgit.api.MergeResult.MergeStatus;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
import org.junit.Before;
import org.junit.Test;

public class PullCommandTest extends RepositoryTestCase {
  /** Second Test repository */
  protected Repository dbTarget;

  private Git source;

  private Git target;

  private File sourceFile;

  private File targetFile;

  @Test
  public void testPullFastForward() throws Exception {
    PullResult res = target.pull().call();
    // nothing to update since we don't have different data yet
    assertTrue(res.getFetchResult().getTrackingRefUpdates().isEmpty());
    assertTrue(res.getMergeResult().getMergeStatus().equals(
        MergeStatus.ALREADY_UP_TO_DATE));

    assertFileContentsEqual(targetFile, "Hello world");

    // change the source file
    writeToFile(sourceFile, "Another change");
    source.add().addFilepattern("SomeFile.txt").call();
    source.commit().setMessage("Some change in remote").call();

    res = target.pull().call();

    assertFalse(res.getFetchResult().getTrackingRefUpdates().isEmpty());
    assertEquals(res.getMergeResult().getMergeStatus(),
        MergeStatus.FAST_FORWARD);
    assertFileContentsEqual(targetFile, "Another change");
    assertEquals(RepositoryState.SAFE, target.getRepository()
        .getRepositoryState());

    res = target.pull().call();
    assertEquals(res.getMergeResult().getMergeStatus(),
        MergeStatus.ALREADY_UP_TO_DATE);
  }

  @Test
  public void testPullMerge() throws Exception {
    PullResult res = target.pull().call();
    // nothing to update since we don't have different data yet
    assertTrue(res.getFetchResult().getTrackingRefUpdates().isEmpty());
    assertTrue(res.getMergeResult().getMergeStatus()
        .equals(MergeStatus.ALREADY_UP_TO_DATE));

    writeToFile(sourceFile, "Source change");
    source.add().addFilepattern("SomeFile.txt");
    RevCommit sourceCommit = source.commit()
        .setMessage("Source change in remote").call();

    File targetFile2 = new File(dbTarget.getWorkTree(), "OtherFile.txt");
    writeToFile(targetFile2, "Unconflicting change");
    target.add().addFilepattern("OtherFile.txt").call();
    RevCommit targetCommit = target.commit()
        .setMessage("Unconflicting change in local").call();

    res = target.pull().call();

    MergeResult mergeResult = res.getMergeResult();
    ObjectId[] mergedCommits = mergeResult.getMergedCommits();
    assertEquals(targetCommit.getId(), mergedCommits[0]);
    assertEquals(sourceCommit.getId(), mergedCommits[1]);
    RevCommit mergeCommit = new RevWalk(dbTarget).parseCommit(mergeResult
        .getNewHead());
    String message = "Merge branch 'master' of "
        + db.getWorkTree().getAbsolutePath();
    assertEquals(message, mergeCommit.getShortMessage());
  }

  @Test
  public void testPullConflict() throws Exception {
    PullResult res = target.pull().call();
    // nothing to update since we don't have different data yet
    assertTrue(res.getFetchResult().getTrackingRefUpdates().isEmpty());
    assertTrue(res.getMergeResult().getMergeStatus().equals(
        MergeStatus.ALREADY_UP_TO_DATE));

    assertFileContentsEqual(targetFile, "Hello world");

    // change the source file
    writeToFile(sourceFile, "Source change");
    source.add().addFilepattern("SomeFile.txt").call();
    source.commit().setMessage("Source change in remote").call();

    // change the target file
    writeToFile(targetFile, "Target change");
    target.add().addFilepattern("SomeFile.txt").call();
    target.commit().setMessage("Target change in local").call();

    res = target.pull().call();

    String sourceChangeString = "Source change\n>>>>>>> branch 'master' of "
        + target.getRepository().getConfig().getString("remote",
            "origin", "url");

    assertFalse(res.getFetchResult().getTrackingRefUpdates().isEmpty());
    assertEquals(res.getMergeResult().getMergeStatus(),
        MergeStatus.CONFLICTING);
    String result = "<<<<<<< HEAD\nTarget change\n=======\n"
        + sourceChangeString + "\n";
    assertFileContentsEqual(targetFile, result);
    assertEquals(RepositoryState.MERGING, target.getRepository()
        .getRepositoryState());
  }

  @Test
  public void testPullLocalConflict() throws Exception {
    target.branchCreate().setName("basedOnMaster").setStartPoint(
        "refs/heads/master").setUpstreamMode(SetupUpstreamMode.TRACK)
        .call();
    target.getRepository().updateRef(Constants.HEAD).link(
        "refs/heads/basedOnMaster");
    PullResult res = target.pull().call();
    // nothing to update since we don't have different data yet
    assertNull(res.getFetchResult());
    assertTrue(res.getMergeResult().getMergeStatus().equals(
        MergeStatus.ALREADY_UP_TO_DATE));

    assertFileContentsEqual(targetFile, "Hello world");

    // change the file in master
    target.getRepository().updateRef(Constants.HEAD).link(
        "refs/heads/master");
    writeToFile(targetFile, "Master change");
    target.add().addFilepattern("SomeFile.txt").call();
    target.commit().setMessage("Source change in master").call();

    // change the file in slave
    target.getRepository().updateRef(Constants.HEAD).link(
        "refs/heads/basedOnMaster");
    writeToFile(targetFile, "Slave change");
    target.add().addFilepattern("SomeFile.txt").call();
    target.commit().setMessage("Source change in based on master").call();

    res = target.pull().call();

    String sourceChangeString = "Master change\n>>>>>>> branch 'master' of local repository";

    assertNull(res.getFetchResult());
    assertEquals(res.getMergeResult().getMergeStatus(),
        MergeStatus.CONFLICTING);
    String result = "<<<<<<< HEAD\nSlave change\n=======\n"
        + sourceChangeString + "\n";
    assertFileContentsEqual(targetFile, result);
    assertEquals(RepositoryState.MERGING, target.getRepository()
        .getRepositoryState());
  }

  @Test(expected = NoHeadException.class)
  public void testPullEmptyRepository() throws Exception {
    Repository empty = createWorkRepository();
    RefUpdate delete = empty.updateRef(Constants.HEAD, true);
    delete.setForceUpdate(true);
    delete.delete();
    Git.wrap(empty).pull().call();
  }

  @Test
  public void testPullMergeProgrammaticConfiguration() throws Exception {
    // create another commit on another branch in source
    source.checkout().setCreateBranch(true).setName("other").call();
    sourceFile = new File(db.getWorkTree(), "file2.txt");
    writeToFile(sourceFile, "content");
    source.add().addFilepattern("file2.txt").call();
    RevCommit sourceCommit = source.commit()
        .setMessage("source commit on branch other").call();

    File targetFile2 = new File(dbTarget.getWorkTree(), "OtherFile.txt");
    writeToFile(targetFile2, "Unconflicting change");
    target.add().addFilepattern("OtherFile.txt").call();
    RevCommit targetCommit = target.commit()
        .setMessage("Unconflicting change in local").call();

    PullResult res = target.pull().setRemote("origin")
        .setRemoteBranchName("other")
        .setRebase(false).call();

    MergeResult mergeResult = res.getMergeResult();
    ObjectId[] mergedCommits = mergeResult.getMergedCommits();
    assertEquals(targetCommit.getId(), mergedCommits[0]);
    assertEquals(sourceCommit.getId(), mergedCommits[1]);
    RevCommit mergeCommit = new RevWalk(dbTarget).parseCommit(mergeResult
        .getNewHead());
    String message = "Merge branch 'other' of "
        + db.getWorkTree().getAbsolutePath();
    assertEquals(message, mergeCommit.getShortMessage());
  }

  @Test
  public void testPullMergeProgrammaticConfigurationImpliedTargetBranch()
      throws Exception {
    // create another commit on another branch in source
    source.checkout().setCreateBranch(true).setName("other").call();
    sourceFile = new File(db.getWorkTree(), "file2.txt");
    writeToFile(sourceFile, "content");
    source.add().addFilepattern("file2.txt").call();
    RevCommit sourceCommit = source.commit()
        .setMessage("source commit on branch other").call();

    target.checkout().setCreateBranch(true).setName("other").call();
    File targetFile2 = new File(dbTarget.getWorkTree(), "OtherFile.txt");
    writeToFile(targetFile2, "Unconflicting change");
    target.add().addFilepattern("OtherFile.txt").call();
    RevCommit targetCommit = target.commit()
        .setMessage("Unconflicting change in local").call();

    // the source branch "other" matching the target branch should be
    // implied
    PullResult res = target.pull().setRemote("origin").setRebase(false)
        .call();

    MergeResult mergeResult = res.getMergeResult();
    ObjectId[] mergedCommits = mergeResult.getMergedCommits();
    assertEquals(targetCommit.getId(), mergedCommits[0]);
    assertEquals(sourceCommit.getId(), mergedCommits[1]);
    RevCommit mergeCommit = new RevWalk(dbTarget).parseCommit(mergeResult
        .getNewHead());
    String message = "Merge branch 'other' of "
        + db.getWorkTree().getAbsolutePath() + " into other";
    assertEquals(message, mergeCommit.getShortMessage());
  }

  private enum TestPullMode {
    MERGE, REBASE, REBASE_PREASERVE
  }

  @Test
  /** global rebase config should be respected */
  public void testPullWithRebasePreserve1Config() throws Exception {
    Callable<PullResult> setup = new Callable<PullResult>() {
      public PullResult call() throws Exception {
        StoredConfig config = dbTarget.getConfig();
        config.setString("pull", null, "rebase", "preserve");
        config.save();
        return target.pull().call();
      }
    };
    doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
  }

  @Test
  /** the branch-local config should win over the global config */
  public void testPullWithRebasePreserveConfig2() throws Exception {
    Callable<PullResult> setup = new Callable<PullResult>() {
      public PullResult call() throws Exception {
        StoredConfig config = dbTarget.getConfig();
        config.setString("pull", null, "rebase", "false");
        config.setString("branch", "master", "rebase", "preserve");
        config.save();
        return target.pull().call();
      }
    };
    doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
  }

  @Test
  /** the branch-local config should be respected */
  public void testPullWithRebasePreserveConfig3() throws Exception {
    Callable<PullResult> setup = new Callable<PullResult>() {
      public PullResult call() throws Exception {
        StoredConfig config = dbTarget.getConfig();
        config.setString("branch", "master", "rebase", "preserve");
        config.save();
        return target.pull().call();
      }
    };
    doTestPullWithRebase(setup, TestPullMode.REBASE_PREASERVE);
  }

  @Test
  /** global rebase config should be respected */
  public void testPullWithRebaseConfig1() throws Exception {
    Callable<PullResult> setup = new Callable<PullResult>() {
      public PullResult call() throws Exception {
        StoredConfig config = dbTarget.getConfig();
        config.setString("pull", null, "rebase", "true");
        config.save();
        return target.pull().call();
      }
    };
    doTestPullWithRebase(setup, TestPullMode.REBASE);
  }

  @Test
  /** the branch-local config should win over the global config */
  public void testPullWithRebaseConfig2() throws Exception {
    Callable<PullResult> setup = new Callable<PullResult>() {
      public PullResult call() throws Exception {
        StoredConfig config = dbTarget.getConfig();
        config.setString("pull", null, "rebase", "preserve");
        config.setString("branch", "master", "rebase", "true");
        config.save();
        return target.pull().call();
      }
    };
    doTestPullWithRebase(setup, TestPullMode.REBASE);
  }

  @Test
  /** the branch-local config should be respected */
  public void testPullWithRebaseConfig3() throws Exception {
    Callable<PullResult> setup = new Callable<PullResult>() {
      public PullResult call() throws Exception {
        StoredConfig config = dbTarget.getConfig();
        config.setString("branch", "master", "rebase", "true");
        config.save();
        return target.pull().call();
      }
    };
    doTestPullWithRebase(setup, TestPullMode.REBASE);
  }

  @Test
  /** without config it should merge */
  public void testPullWithoutConfig() throws Exception {
    Callable<PullResult> setup = new Callable<PullResult>() {
      public PullResult call() throws Exception {
        return target.pull().call();
      }
    };
    doTestPullWithRebase(setup, TestPullMode.MERGE);
  }

  @Test
  /** the branch local config should win over the global config */
  public void testPullWithMergeConfig() throws Exception {
    Callable<PullResult> setup = new Callable<PullResult>() {
      public PullResult call() throws Exception {
        StoredConfig config = dbTarget.getConfig();
        config.setString("pull", null, "rebase", "true");
        config.setString("branch", "master", "rebase", "false");
        config.save();
        return target.pull().call();
      }
    };
    doTestPullWithRebase(setup, TestPullMode.MERGE);
  }

  @Test
  /** the branch local config should win over the global config */
  public void testPullWithMergeConfig2() throws Exception {
    Callable<PullResult> setup = new Callable<PullResult>() {
      public PullResult call() throws Exception {
        StoredConfig config = dbTarget.getConfig();
        config.setString("pull", null, "rebase", "false");
        config.save();
        return target.pull().call();
      }
    };
    doTestPullWithRebase(setup, TestPullMode.MERGE);
  }

  private void doTestPullWithRebase(Callable<PullResult> pullSetup,
      TestPullMode expectedPullMode) throws Exception {
    // simple upstream change
    writeToFile(sourceFile, "content");
    source.add().addFilepattern(sourceFile.getName()).call();
    RevCommit sourceCommit = source.commit().setMessage("source commit")
        .call();

    // create a merge commit in target
    File loxalFile = new File(dbTarget.getWorkTree(), "local.txt");
    writeToFile(loxalFile, "initial\n");
    target.add().addFilepattern("local.txt").call();
    RevCommit t1 = target.commit().setMessage("target commit 1").call();

    target.checkout().setCreateBranch(true).setName("side").call();

    String newContent = "initial\n" + "and more\n";
    writeToFile(loxalFile, newContent);
    target.add().addFilepattern("local.txt").call();
    RevCommit t2 = target.commit().setMessage("target commit 2").call();

    target.checkout().setName("master").call();

    MergeResult mergeResult = target.merge()
        .setFastForward(MergeCommand.FastForwardMode.NO_FF).include(t2)
        .call();
    assertEquals(MergeStatus.MERGED, mergeResult.getMergeStatus());
    assertFileContentsEqual(loxalFile, newContent);
    ObjectId merge = mergeResult.getNewHead();

    // pull
    PullResult res = pullSetup.call();
    assertNotNull(res.getFetchResult());

    if (expectedPullMode == TestPullMode.MERGE) {
      assertEquals(MergeStatus.MERGED, res.getMergeResult()
          .getMergeStatus());
      assertNull(res.getRebaseResult());
    } else {
      assertNull(res.getMergeResult());
      assertEquals(RebaseResult.OK_RESULT, res.getRebaseResult());
    }
    assertFileContentsEqual(sourceFile, "content");

    RevWalk rw = new RevWalk(dbTarget);
    rw.sort(RevSort.TOPO);
    rw.markStart(rw.parseCommit(dbTarget.resolve("refs/heads/master")));

    RevCommit next;
    if (expectedPullMode == TestPullMode.MERGE) {
      next = rw.next();
      assertEquals(2, next.getParentCount());
      assertEquals(merge, next.getParent(0));
      assertEquals(sourceCommit, next.getParent(1));
      // since both parents are known do no further checks here
    } else {
      if (expectedPullMode == TestPullMode.REBASE_PREASERVE) {
        next = rw.next();
        assertEquals(2, next.getParentCount());
      }
      next = rw.next();
      assertEquals(t2.getShortMessage(), next.getShortMessage());
      next = rw.next();
      assertEquals(t1.getShortMessage(), next.getShortMessage());
      next = rw.next();
      assertEquals(sourceCommit, next);
      next = rw.next();
      assertEquals("Initial commit for source", next.getShortMessage());
      next = rw.next();
      assertNull(next);
    }
    rw.release();
  }

  @Override
  @Before
  public void setUp() throws Exception {
    super.setUp();
    dbTarget = createWorkRepository();
    source = new Git(db);
    target = new Git(dbTarget);

    // put some file in the source repo
    sourceFile = new File(db.getWorkTree(), "SomeFile.txt");
    writeToFile(sourceFile, "Hello world");
    // and commit it
    source.add().addFilepattern("SomeFile.txt").call();
    source.commit().setMessage("Initial commit for source").call();

    // configure the target repo to connect to the source via "origin"
    StoredConfig targetConfig = dbTarget.getConfig();
    targetConfig.setString("branch", "master", "remote", "origin");
    targetConfig
        .setString("branch", "master", "merge", "refs/heads/master");
    RemoteConfig config = new RemoteConfig(targetConfig, "origin");

    config
        .addURI(new URIish(source.getRepository().getWorkTree()
            .getAbsolutePath()));
    config.addFetchRefSpec(new RefSpec(
        "+refs/heads/*:refs/remotes/origin/*"));
    config.update(targetConfig);
    targetConfig.save();

    targetFile = new File(dbTarget.getWorkTree(), "SomeFile.txt");
    // make sure we have the same content
    target.pull().call();
    assertFileContentsEqual(targetFile, "Hello world");
  }

  private static void writeToFile(File actFile, String string)
      throws IOException {
    FileOutputStream fos = null;
    try {
      fos = new FileOutputStream(actFile);
      fos.write(string.getBytes("UTF-8"));
      fos.close();
    } finally {
      if (fos != null)
        fos.close();
    }
  }

  private static void assertFileContentsEqual(File actFile, String string)
      throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    FileInputStream fis = null;
    byte[] buffer = new byte[100];
    try {
      fis = new FileInputStream(actFile);
      int read = fis.read(buffer);
      while (read > 0) {
        bos.write(buffer, 0, read);
        read = fis.read(buffer);
      }
      String content = new String(bos.toByteArray(), "UTF-8");
      assertEquals(string, content);
    } finally {
      if (fis != null)
        fis.close();
    }
  }
}
TOP

Related Classes of org.eclipse.jgit.api.PullCommandTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.