Merge branch 'master' into stable-6.3
* master: Update Orbit to R20220830213456 for 2022-09 BaseSuperprojectWriter: report invalid paths as manifest errors ApplyCommand: fix ApplyResult#updatedFiles WorkTreeUpdater: rename metadata maps WorkTreeUpdater#Result: hide data members Add javadoc on RevCommit Option to pass start RevCommit to be blamed on to the BlameGenerator. WorkTreeUpdater: re-format and clean-up Adds FilteredRevCommit that can overwrites its parents in the DAG. Change-Id: Ie92037b78b54c4ab290db80034e3192b1f90a1fc
This commit is contained in:
commit
6e7a158869
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?pde?>
|
||||
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
|
||||
<target name="jgit-4.17" sequenceNumber="1658908301">
|
||||
<target name="jgit-4.17" sequenceNumber="1662389260">
|
||||
<locations>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="jakarta.servlet-api" version="4.0.0"/>
|
||||
|
@ -87,7 +87,7 @@
|
|||
<unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
|
||||
<unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
|
||||
<unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20220726152247/repository"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository"/>
|
||||
</location>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="org.eclipse.osgi" version="0.0.0"/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
target "jgit-4.17" with source configurePhase
|
||||
|
||||
include "projects/jetty-10.0.x.tpd"
|
||||
include "orbit/S20220726152247.tpd"
|
||||
include "orbit/R20220830213456-2022-09.tpd"
|
||||
|
||||
location "https://download.eclipse.org/releases/2020-09/" {
|
||||
org.eclipse.osgi lazy
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?pde?>
|
||||
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
|
||||
<target name="jgit-4.18" sequenceNumber="1658908301">
|
||||
<target name="jgit-4.18" sequenceNumber="1662389260">
|
||||
<locations>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="jakarta.servlet-api" version="4.0.0"/>
|
||||
|
@ -87,7 +87,7 @@
|
|||
<unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
|
||||
<unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
|
||||
<unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20220726152247/repository"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository"/>
|
||||
</location>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="org.eclipse.osgi" version="0.0.0"/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
target "jgit-4.18" with source configurePhase
|
||||
|
||||
include "projects/jetty-10.0.x.tpd"
|
||||
include "orbit/S20220726152247.tpd"
|
||||
include "orbit/R20220830213456-2022-09.tpd"
|
||||
|
||||
location "https://download.eclipse.org/releases/2020-12/" {
|
||||
org.eclipse.osgi lazy
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?pde?>
|
||||
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
|
||||
<target name="jgit-4.19-staging" sequenceNumber="1658908300">
|
||||
<target name="jgit-4.19-staging" sequenceNumber="1662389263">
|
||||
<locations>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="jakarta.servlet-api" version="4.0.0"/>
|
||||
|
@ -87,11 +87,11 @@
|
|||
<unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
|
||||
<unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
|
||||
<unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20220726152247/repository"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository"/>
|
||||
</location>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="org.eclipse.osgi" version="0.0.0"/>
|
||||
<repository location="https://download.eclipse.org/staging/2021-03/"/>
|
||||
<repository location="https://download.eclipse.org/releases/2021-03/"/>
|
||||
</location>
|
||||
</locations>
|
||||
</target>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
target "jgit-4.19-staging" with source configurePhase
|
||||
|
||||
include "projects/jetty-10.0.x.tpd"
|
||||
include "orbit/S20220726152247.tpd"
|
||||
include "orbit/R20220830213456-2022-09.tpd"
|
||||
|
||||
location "https://download.eclipse.org/staging/2021-03/" {
|
||||
location "https://download.eclipse.org/releases/2021-03/" {
|
||||
org.eclipse.osgi lazy
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?pde?>
|
||||
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
|
||||
<target name="jgit-4.20" sequenceNumber="1658908301">
|
||||
<target name="jgit-4.20" sequenceNumber="1662389260">
|
||||
<locations>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="jakarta.servlet-api" version="4.0.0"/>
|
||||
|
@ -87,7 +87,7 @@
|
|||
<unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
|
||||
<unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
|
||||
<unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20220726152247/repository"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository"/>
|
||||
</location>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="org.eclipse.osgi" version="0.0.0"/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
target "jgit-4.20" with source configurePhase
|
||||
|
||||
include "projects/jetty-10.0.x.tpd"
|
||||
include "orbit/S20220726152247.tpd"
|
||||
include "orbit/R20220830213456-2022-09.tpd"
|
||||
|
||||
location "https://download.eclipse.org/releases/2021-06/" {
|
||||
org.eclipse.osgi lazy
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?pde?>
|
||||
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
|
||||
<target name="jgit-4.21" sequenceNumber="1658908300">
|
||||
<target name="jgit-4.21" sequenceNumber="1662389260">
|
||||
<locations>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="jakarta.servlet-api" version="4.0.0"/>
|
||||
|
@ -87,7 +87,7 @@
|
|||
<unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
|
||||
<unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
|
||||
<unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20220726152247/repository"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository"/>
|
||||
</location>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="org.eclipse.osgi" version="0.0.0"/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
target "jgit-4.21" with source configurePhase
|
||||
|
||||
include "projects/jetty-10.0.x.tpd"
|
||||
include "orbit/S20220726152247.tpd"
|
||||
include "orbit/R20220830213456-2022-09.tpd"
|
||||
|
||||
location "https://download.eclipse.org/releases/2021-09/" {
|
||||
org.eclipse.osgi lazy
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?pde?>
|
||||
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
|
||||
<target name="jgit-4.22" sequenceNumber="1658908300">
|
||||
<target name="jgit-4.22" sequenceNumber="1662389260">
|
||||
<locations>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="jakarta.servlet-api" version="4.0.0"/>
|
||||
|
@ -87,7 +87,7 @@
|
|||
<unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
|
||||
<unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
|
||||
<unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20220726152247/repository"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository"/>
|
||||
</location>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="org.eclipse.osgi" version="0.0.0"/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
target "jgit-4.22" with source configurePhase
|
||||
|
||||
include "projects/jetty-10.0.x.tpd"
|
||||
include "orbit/S20220726152247.tpd"
|
||||
include "orbit/R20220830213456-2022-09.tpd"
|
||||
|
||||
location "https://download.eclipse.org/releases/2021-12/" {
|
||||
org.eclipse.osgi lazy
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?pde?>
|
||||
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
|
||||
<target name="jgit-4.23" sequenceNumber="1658908300">
|
||||
<target name="jgit-4.23" sequenceNumber="1662389260">
|
||||
<locations>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="jakarta.servlet-api" version="4.0.0"/>
|
||||
|
@ -87,7 +87,7 @@
|
|||
<unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
|
||||
<unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
|
||||
<unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20220726152247/repository"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository"/>
|
||||
</location>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="org.eclipse.osgi" version="0.0.0"/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
target "jgit-4.23" with source configurePhase
|
||||
|
||||
include "projects/jetty-10.0.x.tpd"
|
||||
include "orbit/S20220726152247.tpd"
|
||||
include "orbit/R20220830213456-2022-09.tpd"
|
||||
|
||||
location "https://download.eclipse.org/releases/2022-03/" {
|
||||
org.eclipse.osgi lazy
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?pde?>
|
||||
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
|
||||
<target name="jgit-4.24" sequenceNumber="1658908300">
|
||||
<target name="jgit-4.24" sequenceNumber="1662389260">
|
||||
<locations>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="jakarta.servlet-api" version="4.0.0"/>
|
||||
|
@ -87,7 +87,7 @@
|
|||
<unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
|
||||
<unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
|
||||
<unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20220726152247/repository"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository"/>
|
||||
</location>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="org.eclipse.osgi" version="0.0.0"/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
target "jgit-4.24" with source configurePhase
|
||||
|
||||
include "projects/jetty-10.0.x.tpd"
|
||||
include "orbit/S20220726152247.tpd"
|
||||
include "orbit/R20220830213456-2022-09.tpd"
|
||||
|
||||
location "https://download.eclipse.org/releases/2022-06/" {
|
||||
org.eclipse.osgi lazy
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?pde?>
|
||||
<!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
|
||||
<target name="jgit-4.25" sequenceNumber="1658908305">
|
||||
<target name="jgit-4.25" sequenceNumber="1662389260">
|
||||
<locations>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="jakarta.servlet-api" version="4.0.0"/>
|
||||
|
@ -87,7 +87,7 @@
|
|||
<unit id="org.slf4j.binding.simple.source" version="1.7.30.v20200204-2150"/>
|
||||
<unit id="org.tukaani.xz" version="1.9.0.v20210624-1259"/>
|
||||
<unit id="org.tukaani.xz.source" version="1.9.0.v20210624-1259"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/S20220726152247/repository"/>
|
||||
<repository location="https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository"/>
|
||||
</location>
|
||||
<location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
|
||||
<unit id="org.eclipse.osgi" version="0.0.0"/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
target "jgit-4.25" with source configurePhase
|
||||
|
||||
include "projects/jetty-10.0.x.tpd"
|
||||
include "orbit/S20220726152247.tpd"
|
||||
include "orbit/R20220830213456-2022-09.tpd"
|
||||
|
||||
location "https://download.eclipse.org/staging/2022-09/" {
|
||||
org.eclipse.osgi lazy
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
target "S20220726152247" with source configurePhase
|
||||
target "R20220830213456-2022-09" with source configurePhase
|
||||
// see https://download.eclipse.org/tools/orbit/downloads/
|
||||
|
||||
location "https://download.eclipse.org/tools/orbit/downloads/drops/S20220726152247/repository" {
|
||||
location "https://download.eclipse.org/tools/orbit/downloads/drops/R20220830213456/repository" {
|
||||
com.google.gson [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
|
||||
com.google.gson.source [2.8.9.v20220111-1409,2.8.9.v20220111-1409]
|
||||
com.jcraft.jsch [0.1.55.v20190404-1902,0.1.55.v20190404-1902]
|
|
@ -525,9 +525,9 @@ public void testNonASCIIDel() throws Exception {
|
|||
@Test
|
||||
public void testRenameNoHunks() throws Exception {
|
||||
ApplyResult result = init("RenameNoHunks", true, true);
|
||||
assertEquals(1, result.getUpdatedFiles().size());
|
||||
assertEquals(new File(db.getWorkTree(), "RenameNoHunks"), result.getUpdatedFiles()
|
||||
.get(0));
|
||||
assertEquals(2, result.getUpdatedFiles().size());
|
||||
assertTrue(result.getUpdatedFiles().contains(new File(db.getWorkTree(), "RenameNoHunks")));
|
||||
assertTrue(result.getUpdatedFiles().contains(new File(db.getWorkTree(), "nested/subdir/Renamed")));
|
||||
checkFile(new File(db.getWorkTree(), "nested/subdir/Renamed"),
|
||||
b.getString(0, b.size(), false));
|
||||
}
|
||||
|
@ -535,9 +535,9 @@ public void testRenameNoHunks() throws Exception {
|
|||
@Test
|
||||
public void testRenameWithHunks() throws Exception {
|
||||
ApplyResult result = init("RenameWithHunks", true, true);
|
||||
assertEquals(1, result.getUpdatedFiles().size());
|
||||
assertEquals(new File(db.getWorkTree(), "RenameWithHunks"), result.getUpdatedFiles()
|
||||
.get(0));
|
||||
assertEquals(2, result.getUpdatedFiles().size());
|
||||
assertTrue(result.getUpdatedFiles().contains(new File(db.getWorkTree(), "RenameWithHunks")));
|
||||
assertTrue(result.getUpdatedFiles().contains(new File(db.getWorkTree(), "nested/subdir/Renamed")));
|
||||
checkFile(new File(db.getWorkTree(), "nested/subdir/Renamed"),
|
||||
b.getString(0, b.size(), false));
|
||||
}
|
||||
|
@ -546,7 +546,7 @@ public void testRenameWithHunks() throws Exception {
|
|||
public void testCopyWithHunks() throws Exception {
|
||||
ApplyResult result = init("CopyWithHunks", true, true);
|
||||
assertEquals(1, result.getUpdatedFiles().size());
|
||||
assertEquals(new File(db.getWorkTree(), "CopyWithHunks"), result.getUpdatedFiles()
|
||||
assertEquals(new File(db.getWorkTree(), "CopyResult"), result.getUpdatedFiles()
|
||||
.get(0));
|
||||
checkFile(new File(db.getWorkTree(), "CopyResult"),
|
||||
b.getString(0, b.size(), false));
|
||||
|
|
|
@ -13,56 +13,330 @@
|
|||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.blame.BlameGenerator;
|
||||
import org.eclipse.jgit.blame.BlameResult;
|
||||
import org.eclipse.jgit.junit.RepositoryTestCase;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.merge.MergeStrategy;
|
||||
import org.eclipse.jgit.revwalk.FilteredRevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.junit.Test;
|
||||
|
||||
/** Unit tests of {@link BlameGenerator}. */
|
||||
public class BlameGeneratorTest extends RepositoryTestCase {
|
||||
|
||||
public static final String OTHER_FILE = "other_file.txt";
|
||||
|
||||
public static final String INTERESTING_FILE = "interesting_file.txt";
|
||||
|
||||
@Test
|
||||
public void testBoundLineDelete() throws Exception {
|
||||
try (Git git = new Git(db)) {
|
||||
String[] content1 = new String[] { "first", "second" };
|
||||
writeTrashFile("file.txt", join(content1));
|
||||
git.add().addFilepattern("file.txt").call();
|
||||
public void testSingleBlame() throws Exception {
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* (ts) OTHER_FILE INTERESTING_FILE
|
||||
* 1 a
|
||||
* 2 a, b
|
||||
* 3 1, 2 c1 <--
|
||||
* 4 a, b, c |
|
||||
* 5 1, 2, 3 c2---
|
||||
* </pre>
|
||||
*/
|
||||
try (Git git = new Git(db);
|
||||
RevWalk revWalk = new RevWalk(git.getRepository())) {
|
||||
writeTrashFile(OTHER_FILE, join("a"));
|
||||
git.add().addFilepattern(OTHER_FILE).call();
|
||||
git.commit().setMessage("create file").call();
|
||||
|
||||
writeTrashFile(OTHER_FILE, join("a", "b"));
|
||||
git.add().addFilepattern(OTHER_FILE).call();
|
||||
git.commit().setMessage("amend file").call();
|
||||
|
||||
writeTrashFile(INTERESTING_FILE, join("1", "2"));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
RevCommit c1 = git.commit().setMessage("create file").call();
|
||||
|
||||
String[] content2 = new String[] { "third", "first", "second" };
|
||||
writeTrashFile("file.txt", join(content2));
|
||||
git.add().addFilepattern("file.txt").call();
|
||||
RevCommit c2 = git.commit().setMessage("create file").call();
|
||||
writeTrashFile(OTHER_FILE, join("a", "b", "c"));
|
||||
git.add().addFilepattern(OTHER_FILE).call();
|
||||
git.commit().setMessage("amend file").call();
|
||||
|
||||
try (BlameGenerator generator = new BlameGenerator(db, "file.txt")) {
|
||||
generator.push(null, db.resolve(Constants.HEAD));
|
||||
writeTrashFile(INTERESTING_FILE, join("1", "2", "3"));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
RevCommit c2 = git.commit().setMessage("amend file").call();
|
||||
|
||||
RevCommit filteredC1 = new FilteredRevCommit(c1);
|
||||
RevCommit filteredC2 = new FilteredRevCommit(c2, filteredC1);
|
||||
|
||||
revWalk.parseHeaders(filteredC2);
|
||||
|
||||
try (BlameGenerator generator = new BlameGenerator(db,
|
||||
INTERESTING_FILE)) {
|
||||
generator.push(filteredC2);
|
||||
assertEquals(3, generator.getResultContents().size());
|
||||
|
||||
assertTrue(generator.next());
|
||||
assertEquals(c2, generator.getSourceCommit());
|
||||
assertEquals(1, generator.getRegionLength());
|
||||
assertEquals(0, generator.getResultStart());
|
||||
assertEquals(1, generator.getResultEnd());
|
||||
assertEquals(0, generator.getSourceStart());
|
||||
assertEquals(1, generator.getSourceEnd());
|
||||
assertEquals("file.txt", generator.getSourcePath());
|
||||
assertEquals(2, generator.getResultStart());
|
||||
assertEquals(3, generator.getResultEnd());
|
||||
assertEquals(2, generator.getSourceStart());
|
||||
assertEquals(3, generator.getSourceEnd());
|
||||
assertEquals(INTERESTING_FILE, generator.getSourcePath());
|
||||
|
||||
assertTrue(generator.next());
|
||||
assertEquals(c1, generator.getSourceCommit());
|
||||
assertEquals(2, generator.getRegionLength());
|
||||
assertEquals(1, generator.getResultStart());
|
||||
assertEquals(3, generator.getResultEnd());
|
||||
assertEquals(0, generator.getResultStart());
|
||||
assertEquals(2, generator.getResultEnd());
|
||||
assertEquals(0, generator.getSourceStart());
|
||||
assertEquals(2, generator.getSourceEnd());
|
||||
assertEquals("file.txt", generator.getSourcePath());
|
||||
assertEquals(INTERESTING_FILE, generator.getSourcePath());
|
||||
|
||||
assertFalse(generator.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeSingleBlame() throws Exception {
|
||||
try (Git git = new Git(db);
|
||||
RevWalk revWalk = new RevWalk(git.getRepository())) {
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* refs/heads/master
|
||||
* A
|
||||
* / \ refs/heads/side
|
||||
* / ----------------> side
|
||||
* / |
|
||||
* merge <-------------------
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
writeTrashFile(INTERESTING_FILE, join("1", "2"));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
RevCommit c1 = git.commit().setMessage("create file").call();
|
||||
|
||||
createBranch(c1, "refs/heads/side");
|
||||
checkoutBranch("refs/heads/side");
|
||||
writeTrashFile(INTERESTING_FILE, join("1", "2", "3", "4"));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
RevCommit sideCommit = git.commit()
|
||||
.setMessage("amend file in another branch").call();
|
||||
|
||||
checkoutBranch("refs/heads/master");
|
||||
git.merge().setMessage("merge").include(sideCommit)
|
||||
.setStrategy(MergeStrategy.RESOLVE).call();
|
||||
|
||||
Iterator<RevCommit> it = git.log().call().iterator();
|
||||
RevCommit mergeCommit = it.next();
|
||||
|
||||
RevCommit filteredC1 = new FilteredRevCommit(c1);
|
||||
RevCommit filteredSide = new FilteredRevCommit(sideCommit,
|
||||
filteredC1);
|
||||
RevCommit filteredMerge = new FilteredRevCommit(mergeCommit,
|
||||
filteredSide, filteredC1);
|
||||
|
||||
revWalk.parseHeaders(filteredMerge);
|
||||
|
||||
try (BlameGenerator generator = new BlameGenerator(db,
|
||||
INTERESTING_FILE)) {
|
||||
generator.push(filteredMerge);
|
||||
assertEquals(4, generator.getResultContents().size());
|
||||
|
||||
assertTrue(generator.next());
|
||||
assertEquals(mergeCommit, generator.getSourceCommit());
|
||||
assertEquals(2, generator.getRegionLength());
|
||||
assertEquals(2, generator.getResultStart());
|
||||
assertEquals(4, generator.getResultEnd());
|
||||
assertEquals(2, generator.getSourceStart());
|
||||
assertEquals(4, generator.getSourceEnd());
|
||||
assertEquals(INTERESTING_FILE, generator.getSourcePath());
|
||||
|
||||
assertTrue(generator.next());
|
||||
assertEquals(filteredC1, generator.getSourceCommit());
|
||||
assertEquals(2, generator.getRegionLength());
|
||||
assertEquals(0, generator.getResultStart());
|
||||
assertEquals(2, generator.getResultEnd());
|
||||
assertEquals(0, generator.getSourceStart());
|
||||
assertEquals(2, generator.getSourceEnd());
|
||||
assertEquals(INTERESTING_FILE, generator.getSourcePath());
|
||||
|
||||
assertFalse(generator.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMergeBlame() throws Exception {
|
||||
try (Git git = new Git(db);
|
||||
RevWalk revWalk = new RevWalk(git.getRepository())) {
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* refs/heads/master
|
||||
* A
|
||||
* / \ refs/heads/side
|
||||
* B ----------------> side
|
||||
* / |
|
||||
* merge <-------------------
|
||||
* </pre>
|
||||
*/
|
||||
writeTrashFile(INTERESTING_FILE, join("1", "2"));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
RevCommit c1 = git.commit().setMessage("create file").call();
|
||||
|
||||
createBranch(c1, "refs/heads/side");
|
||||
checkoutBranch("refs/heads/side");
|
||||
writeTrashFile(INTERESTING_FILE, join("1", "2", "3"));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
RevCommit sideCommit = git.commit().setMessage("amend file").call();
|
||||
|
||||
checkoutBranch("refs/heads/master");
|
||||
writeTrashFile(INTERESTING_FILE, join("1", "2", "4"));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
RevCommit c2 = git.commit().setMessage("delete and amend file")
|
||||
.call();
|
||||
|
||||
git.merge().setMessage("merge").include(sideCommit)
|
||||
.setStrategy(MergeStrategy.RESOLVE).call();
|
||||
writeTrashFile(INTERESTING_FILE, join("1", "2", "3", "4"));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
RevCommit mergeCommit = git.commit().setMessage("merge commit")
|
||||
.call();
|
||||
|
||||
RevCommit filteredC1 = new FilteredRevCommit(c1);
|
||||
RevCommit filteredSide = new FilteredRevCommit(sideCommit,
|
||||
filteredC1);
|
||||
RevCommit filteredC2 = new FilteredRevCommit(c2, filteredC1);
|
||||
|
||||
RevCommit filteredMerge = new FilteredRevCommit(mergeCommit,
|
||||
filteredSide, filteredC2);
|
||||
|
||||
revWalk.parseHeaders(filteredMerge);
|
||||
|
||||
try (BlameGenerator generator = new BlameGenerator(db,
|
||||
INTERESTING_FILE)) {
|
||||
generator.push(filteredMerge);
|
||||
assertEquals(4, generator.getResultContents().size());
|
||||
|
||||
assertTrue(generator.next());
|
||||
assertEquals(filteredC2, generator.getSourceCommit());
|
||||
assertEquals(1, generator.getRegionLength());
|
||||
assertEquals(3, generator.getResultStart());
|
||||
assertEquals(4, generator.getResultEnd());
|
||||
assertEquals(2, generator.getSourceStart());
|
||||
assertEquals(3, generator.getSourceEnd());
|
||||
assertEquals(INTERESTING_FILE, generator.getSourcePath());
|
||||
|
||||
assertTrue(generator.next());
|
||||
assertEquals(filteredSide, generator.getSourceCommit());
|
||||
assertEquals(1, generator.getRegionLength());
|
||||
assertEquals(2, generator.getResultStart());
|
||||
assertEquals(3, generator.getResultEnd());
|
||||
assertEquals(2, generator.getSourceStart());
|
||||
assertEquals(3, generator.getSourceEnd());
|
||||
assertEquals(INTERESTING_FILE, generator.getSourcePath());
|
||||
|
||||
assertTrue(generator.next());
|
||||
assertEquals(filteredC1, generator.getSourceCommit());
|
||||
assertEquals(2, generator.getRegionLength());
|
||||
assertEquals(0, generator.getResultStart());
|
||||
assertEquals(2, generator.getResultEnd());
|
||||
assertEquals(0, generator.getSourceStart());
|
||||
assertEquals(2, generator.getSourceEnd());
|
||||
assertEquals(INTERESTING_FILE, generator.getSourcePath());
|
||||
|
||||
assertFalse(generator.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleBlame_compareWithWalk() throws Exception {
|
||||
/**
|
||||
* <pre>
|
||||
* (ts) OTHER_FILE INTERESTING_FILE
|
||||
* 1 a
|
||||
* 2 a, b
|
||||
* 3 1, 2 c1 <--
|
||||
* 4 a, b, c |
|
||||
* 6 3, 1, 2 c2---
|
||||
* </pre>
|
||||
*/
|
||||
try (Git git = new Git(db);
|
||||
RevWalk revWalk = new RevWalk(git.getRepository())) {
|
||||
writeTrashFile(OTHER_FILE, join("a"));
|
||||
git.add().addFilepattern(OTHER_FILE).call();
|
||||
git.commit().setMessage("create file").call();
|
||||
|
||||
writeTrashFile(OTHER_FILE, join("a", "b"));
|
||||
git.add().addFilepattern(OTHER_FILE).call();
|
||||
git.commit().setMessage("amend file").call();
|
||||
|
||||
writeTrashFile(INTERESTING_FILE, join("1", "2"));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
RevCommit c1 = git.commit().setMessage("create file").call();
|
||||
|
||||
writeTrashFile(OTHER_FILE, join("a", "b", "c"));
|
||||
git.add().addFilepattern(OTHER_FILE).call();
|
||||
git.commit().setMessage("amend file").call();
|
||||
|
||||
writeTrashFile(INTERESTING_FILE, join("3", "1", "2"));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
RevCommit c2 = git.commit().setMessage("prepend").call();
|
||||
|
||||
RevCommit filteredC1 = new FilteredRevCommit(c1);
|
||||
RevCommit filteredC2 = new FilteredRevCommit(c2, filteredC1);
|
||||
|
||||
revWalk.parseHeaders(filteredC2);
|
||||
|
||||
try (BlameGenerator g1 = new BlameGenerator(db, INTERESTING_FILE);
|
||||
BlameGenerator g2 = new BlameGenerator(db,
|
||||
INTERESTING_FILE)) {
|
||||
g1.push(null, c2);
|
||||
g2.push(null, filteredC2);
|
||||
|
||||
assertEquals(g1.getResultContents().size(),
|
||||
g2.getResultContents().size()); // 3
|
||||
|
||||
assertTrue(g1.next());
|
||||
assertTrue(g2.next());
|
||||
|
||||
assertEquals(g1.getSourceCommit(), g2.getSourceCommit()); // c2
|
||||
assertEquals(INTERESTING_FILE, g1.getSourcePath());
|
||||
assertEquals(g1.getRegionLength(), g2.getRegionLength()); // 1
|
||||
assertEquals(g1.getResultStart(), g2.getResultStart()); // 0
|
||||
assertEquals(g1.getResultEnd(), g2.getResultEnd()); // 1
|
||||
assertEquals(g1.getSourceStart(), g2.getSourceStart()); // 0
|
||||
assertEquals(g1.getSourceEnd(), g2.getSourceEnd()); // 1
|
||||
assertEquals(g1.getSourcePath(), g2.getSourcePath()); // INTERESTING_FILE
|
||||
|
||||
assertTrue(g1.next());
|
||||
assertTrue(g2.next());
|
||||
|
||||
assertEquals(g1.getSourceCommit(), g2.getSourceCommit()); // c1
|
||||
assertEquals(g1.getRegionLength(), g2.getRegionLength()); // 2
|
||||
assertEquals(g1.getResultStart(), g2.getResultStart()); // 1
|
||||
assertEquals(g1.getResultEnd(), g2.getResultEnd()); // 3
|
||||
assertEquals(g1.getSourceStart(), g2.getSourceStart()); // 0
|
||||
assertEquals(g1.getSourceEnd(), g2.getSourceEnd()); // 2
|
||||
assertEquals(g1.getSourcePath(), g2.getSourcePath()); // INTERESTING_FILE
|
||||
|
||||
assertFalse(g1.next());
|
||||
assertFalse(g2.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRenamedBoundLineDelete() throws Exception {
|
||||
try (Git git = new Git(db)) {
|
||||
|
@ -87,7 +361,8 @@ public void testRenamedBoundLineDelete() throws Exception {
|
|||
git.add().addFilepattern(FILENAME_2).call();
|
||||
RevCommit c2 = git.commit().setMessage("change file2").call();
|
||||
|
||||
try (BlameGenerator generator = new BlameGenerator(db, FILENAME_2)) {
|
||||
try (BlameGenerator generator = new BlameGenerator(db,
|
||||
FILENAME_2)) {
|
||||
generator.push(null, db.resolve(Constants.HEAD));
|
||||
assertEquals(3, generator.getResultContents().size());
|
||||
|
||||
|
@ -113,7 +388,8 @@ public void testRenamedBoundLineDelete() throws Exception {
|
|||
}
|
||||
|
||||
// and test again with other BlameGenerator API:
|
||||
try (BlameGenerator generator = new BlameGenerator(db, FILENAME_2)) {
|
||||
try (BlameGenerator generator = new BlameGenerator(db,
|
||||
FILENAME_2)) {
|
||||
generator.push(null, db.resolve(Constants.HEAD));
|
||||
BlameResult result = generator.computeBlameResult();
|
||||
|
||||
|
@ -136,21 +412,22 @@ public void testLinesAllDeletedShortenedWalk() throws Exception {
|
|||
try (Git git = new Git(db)) {
|
||||
String[] content1 = new String[] { "first", "second", "third" };
|
||||
|
||||
writeTrashFile("file.txt", join(content1));
|
||||
git.add().addFilepattern("file.txt").call();
|
||||
writeTrashFile(INTERESTING_FILE, join(content1));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
git.commit().setMessage("create file").call();
|
||||
|
||||
String[] content2 = new String[] { "" };
|
||||
|
||||
writeTrashFile("file.txt", join(content2));
|
||||
git.add().addFilepattern("file.txt").call();
|
||||
writeTrashFile(INTERESTING_FILE, join(content2));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
git.commit().setMessage("create file").call();
|
||||
|
||||
writeTrashFile("file.txt", join(content1));
|
||||
git.add().addFilepattern("file.txt").call();
|
||||
writeTrashFile(INTERESTING_FILE, join(content1));
|
||||
git.add().addFilepattern(INTERESTING_FILE).call();
|
||||
RevCommit c3 = git.commit().setMessage("create file").call();
|
||||
|
||||
try (BlameGenerator generator = new BlameGenerator(db, "file.txt")) {
|
||||
try (BlameGenerator generator = new BlameGenerator(db,
|
||||
INTERESTING_FILE)) {
|
||||
generator.push(null, db.resolve(Constants.HEAD));
|
||||
assertEquals(3, generator.getResultContents().size());
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
@ -32,6 +33,7 @@
|
|||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.api.errors.InvalidRefNameException;
|
||||
import org.eclipse.jgit.api.errors.InvalidRemoteException;
|
||||
import org.eclipse.jgit.gitrepo.RepoCommand.ManifestErrorException;
|
||||
import org.eclipse.jgit.gitrepo.RepoCommand.RemoteFile;
|
||||
import org.eclipse.jgit.internal.JGitText;
|
||||
import org.eclipse.jgit.junit.JGitTestUtil;
|
||||
|
@ -1337,6 +1339,28 @@ public void testTwoPathUseTheSameName() throws Exception {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidPath() throws Exception {
|
||||
Repository remoteDb = createBareRepository();
|
||||
Repository tempDb = createWorkRepository();
|
||||
|
||||
StringBuilder xmlContent = new StringBuilder();
|
||||
xmlContent.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
|
||||
.append("<manifest>")
|
||||
.append("<remote name=\"remote1\" fetch=\".\" />")
|
||||
.append("<default revision=\"master\" remote=\"remote1\" />")
|
||||
.append("<project path=\".\" ").append("name=\"")
|
||||
.append(defaultUri).append("\" />").append("</manifest>");
|
||||
JGitTestUtil.writeTrashFile(tempDb, "manifest.xml",
|
||||
xmlContent.toString());
|
||||
|
||||
RepoCommand command = new RepoCommand(remoteDb);
|
||||
command.setPath(
|
||||
tempDb.getWorkTree().getAbsolutePath() + "/manifest.xml")
|
||||
.setURI(rootUri).setRecommendShallow(true);
|
||||
assertThrows(ManifestErrorException.class, () -> command.call());
|
||||
}
|
||||
|
||||
private void resolveRelativeUris() {
|
||||
// Find the longest common prefix ends with "/" as rootUri.
|
||||
defaultUri = defaultDb.getDirectory().toURI().toString();
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (C) 2022, Google LLC.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* @since 6.3
|
||||
*/
|
||||
package org.eclipse.jgit.revwalk;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
|
||||
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
||||
import org.eclipse.jgit.junit.TestRepository;
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectLoader;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class FilteredRevCommitTest {
|
||||
private TestRepository<InMemoryRepository> tr;
|
||||
|
||||
private RevWalk rw;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
tr = new TestRepository<>(
|
||||
new InMemoryRepository(new DfsRepositoryDescription("test")));
|
||||
rw = tr.getRevWalk();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseHeaders_noParent() throws Exception {
|
||||
RevCommit root = tr.commit().add("todelete", "to be deleted").create();
|
||||
RevCommit orig = tr.commit().parent(root).rm("todelete")
|
||||
.add("foo", "foo contents").add("bar", "bar contents")
|
||||
.add("dir/baz", "baz contents").create();
|
||||
FilteredRevCommit filteredRevCommit = new FilteredRevCommit(orig);
|
||||
filteredRevCommit.parseHeaders(rw);
|
||||
tr.branch("master").update(filteredRevCommit);
|
||||
assertEquals("foo contents", blobAsString(filteredRevCommit, "foo"));
|
||||
assertEquals("bar contents", blobAsString(filteredRevCommit, "bar"));
|
||||
assertEquals("baz contents",
|
||||
blobAsString(filteredRevCommit, "dir/baz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParents() throws Exception {
|
||||
RevCommit commit1 = tr.commit().add("foo", "foo contents\n").create();
|
||||
RevCommit commit2 = tr.commit().parent(commit1)
|
||||
.message("original message").add("bar", "bar contents")
|
||||
.create();
|
||||
RevCommit commit3 = tr.commit().parent(commit2).message("commit3")
|
||||
.add("foo", "foo contents\n new line\n").create();
|
||||
|
||||
FilteredRevCommit filteredCommitHead = new FilteredRevCommit(commit3,
|
||||
commit1);
|
||||
|
||||
assertEquals(commit1, Arrays.stream(filteredCommitHead.getParents())
|
||||
.findFirst().get());
|
||||
assertEquals("commit3", filteredCommitHead.getFullMessage());
|
||||
assertEquals("foo contents\n new line\n",
|
||||
blobAsString(filteredCommitHead, "foo"));
|
||||
assertEquals(filteredCommitHead.getTree(), commit3.getTree());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlag() throws Exception {
|
||||
RevCommit root = tr.commit().add("todelete", "to be deleted").create();
|
||||
RevCommit orig = tr.commit().parent(root).rm("todelete")
|
||||
.add("foo", "foo contents").add("bar", "bar contents")
|
||||
.add("dir/baz", "baz contents").create();
|
||||
|
||||
FilteredRevCommit filteredRevCommit = new FilteredRevCommit(orig, root);
|
||||
assertEquals(RevObject.PARSED, orig.flags);
|
||||
assertEquals(RevObject.PARSED, filteredRevCommit.flags);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommitState() throws Exception {
|
||||
RevCommit root = tr.commit().add("todelete", "to be deleted").create();
|
||||
RevCommit orig = tr.commit().parent(root).rm("todelete")
|
||||
.add("foo", "foo contents").add("bar", "bar contents")
|
||||
.add("dir/baz", "baz contents").create();
|
||||
|
||||
FilteredRevCommit filteredRevCommit = new FilteredRevCommit(orig, root);
|
||||
assertEquals(filteredRevCommit.getParentCount(), 1);
|
||||
assertSame(filteredRevCommit.getRawBuffer(), orig.getRawBuffer());
|
||||
assertSame(filteredRevCommit.getTree(), orig.getTree());
|
||||
assertEquals(filteredRevCommit.getFullMessage(), orig.getFullMessage());
|
||||
assertEquals(filteredRevCommit.commitTime, orig.commitTime);
|
||||
assertSame(filteredRevCommit.parents, RevCommit.NO_PARENTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseCommit_withParents_parsesRealParents()
|
||||
throws Exception {
|
||||
RevCommit commit1 = tr.commit().add("foo", "foo contents\n").create();
|
||||
RevCommit commit2 = tr.commit().parent(commit1)
|
||||
.message("original message").add("bar", "bar contents")
|
||||
.create();
|
||||
RevCommit commit3 = tr.commit().parent(commit2).message("commit3")
|
||||
.add("foo", "foo contents\n new line\n").create();
|
||||
|
||||
FilteredRevCommit filteredCommitHead = new FilteredRevCommit(commit3,
|
||||
commit1);
|
||||
|
||||
RevCommit parsedCommit = rw.parseCommit(filteredCommitHead.getId());
|
||||
assertEquals(filteredCommitHead.getId(), commit3.getId());
|
||||
// This is an intended behavior as revWalk#parseCommit doesn't parse
|
||||
// through the overridden parents rather uses the real parents.
|
||||
assertNotEquals(
|
||||
Arrays.stream(parsedCommit.getParents()).findFirst().get(),
|
||||
Arrays.stream(filteredCommitHead.getParents()).findFirst()
|
||||
.get());
|
||||
}
|
||||
|
||||
private String blobAsString(AnyObjectId treeish, String path)
|
||||
throws Exception {
|
||||
RevObject obj = tr.get(rw.parseTree(treeish), path);
|
||||
assertSame(RevBlob.class, obj.getClass());
|
||||
ObjectLoader loader = rw.getObjectReader().open(obj);
|
||||
return new String(loader.getCachedBytes(), UTF_8);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (C) 2022, Google LLC.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
package org.eclipse.jgit.revwalk;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import org.eclipse.jgit.internal.storage.file.FileRepository;
|
||||
import org.eclipse.jgit.junit.TestRepository;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class FilteredRevWalkTest extends RevWalkTestCase {
|
||||
private TestRepository<FileRepository> repository;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
repository = new TestRepository<>(db);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWalk() throws Exception {
|
||||
writeTrashFile("a.txt", "content");
|
||||
repository.git().add().addFilepattern("a.txt").call();
|
||||
RevCommit c1 = repository.git().commit().setMessage("first commit")
|
||||
.call();
|
||||
|
||||
writeTrashFile("b.txt", "new file added");
|
||||
repository.git().add().addFilepattern("b.txt").call();
|
||||
repository.git().commit().setMessage("second commit").call();
|
||||
|
||||
writeTrashFile("a.txt", "content added");
|
||||
repository.git().add().addFilepattern("a.txt").call();
|
||||
RevCommit c3 = repository.git().commit().setMessage("third commit")
|
||||
.call();
|
||||
|
||||
RevWalk revWalk = repository.getRevWalk();
|
||||
FilteredRevCommit filteredRevCommit = new FilteredRevCommit(c3, c1);
|
||||
|
||||
revWalk.markStart(filteredRevCommit);
|
||||
assertEquals(c3, revWalk.next());
|
||||
assertEquals(c1, revWalk.next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseBody() throws Exception {
|
||||
writeTrashFile("a.txt", "content");
|
||||
repository.git().add().addFilepattern("a.txt").call();
|
||||
RevCommit c1 = repository.git().commit().setMessage("first commit")
|
||||
.call();
|
||||
|
||||
writeTrashFile("b.txt", "new file added");
|
||||
repository.git().add().addFilepattern("b.txt").call();
|
||||
repository.git().commit().setMessage("second commit").call();
|
||||
|
||||
writeTrashFile("a.txt", "content added");
|
||||
repository.git().add().addFilepattern("a.txt").call();
|
||||
RevCommit c3 = repository.git().commit().setMessage("third commit")
|
||||
.call();
|
||||
|
||||
FilteredRevCommit filteredRevCommit = new FilteredRevCommit(c3, c1);
|
||||
filteredRevCommit.disposeBody();
|
||||
|
||||
RevWalk revWalk = repository.getRevWalk();
|
||||
|
||||
revWalk.parseBody(filteredRevCommit);
|
||||
assertEquals(filteredRevCommit.getFullMessage(), c3.getFullMessage());
|
||||
assertEquals(filteredRevCommit.getShortMessage(), c3.getShortMessage());
|
||||
assertEquals(filteredRevCommit.commitTime, c3.commitTime);
|
||||
assertSame(filteredRevCommit.getTree(), c3.getTree());
|
||||
assertSame(filteredRevCommit.parents, RevCommit.NO_PARENTS);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the uninteresting flag is carried over correctly. Every commit
|
||||
* should have the uninteresting flag resulting in a RevWalk returning no
|
||||
* commit.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testRevWalkCarryUninteresting() throws Exception {
|
||||
writeTrashFile("a.txt", "content");
|
||||
repository.git().add().addFilepattern("a.txt").call();
|
||||
RevCommit c1 = repository.git().commit().setMessage("first commit")
|
||||
.call();
|
||||
|
||||
writeTrashFile("b.txt", "new file added");
|
||||
repository.git().add().addFilepattern("b.txt").call();
|
||||
RevCommit c2 = repository.git().commit().setMessage("second commit")
|
||||
.call();
|
||||
|
||||
writeTrashFile("a.txt", "content added");
|
||||
repository.git().add().addFilepattern("a.txt").call();
|
||||
RevCommit c3 = repository.git().commit().setMessage("third commit")
|
||||
.call();
|
||||
|
||||
RevWalk revWalk = repository.getRevWalk();
|
||||
FilteredRevCommit filteredCommit1 = new FilteredRevCommit(c1);
|
||||
FilteredRevCommit filteredCommit2 = new FilteredRevCommit(c2,
|
||||
filteredCommit1);
|
||||
FilteredRevCommit filteredCommit3 = new FilteredRevCommit(c3,
|
||||
filteredCommit2);
|
||||
|
||||
revWalk.markStart(filteredCommit2);
|
||||
markUninteresting(filteredCommit3);
|
||||
assertNull("Found an unexpected commit", rw.next());
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.revwalk.filter.MessageRevFilter;
|
||||
|
@ -423,9 +424,41 @@ public void testWithTopoSortAndTreeFilter() throws Exception {
|
|||
rw.sort(RevSort.TOPO, true);
|
||||
rw.setTreeFilter(PathFilterGroup.createFromStrings("0"));
|
||||
markStart(d);
|
||||
assertCommit(d, rw.next());
|
||||
assertCommit(c, rw.next());
|
||||
assertCommit(b, rw.next());
|
||||
|
||||
assertEquals(d, rw.next());
|
||||
assertEquals(c, rw.next());
|
||||
assertEquals(b, rw.next());
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithTopoSortAndTreeFilter_shouldUseFilteredRevCommits()
|
||||
throws Exception {
|
||||
RevCommit a = commit();
|
||||
RevCommit b = commit(tree(file("0", blob("b"))), a);
|
||||
RevCommit c = commit(tree(file("0", blob("c"))), b, a);
|
||||
RevCommit d = commit(tree(file("0", blob("d"))), c);
|
||||
|
||||
rw.reset();
|
||||
rw.setFirstParent(true);
|
||||
rw.sort(RevSort.TOPO, true);
|
||||
rw.setTreeFilter(PathFilterGroup.createFromStrings("0"));
|
||||
markStart(d);
|
||||
|
||||
RevCommit x = rw.next();
|
||||
assertTrue(x instanceof FilteredRevCommit);
|
||||
assertEquals(1, x.getParentCount());
|
||||
assertEquals(c, x.getParent(0));
|
||||
|
||||
RevCommit y = rw.next();
|
||||
assertTrue(y instanceof FilteredRevCommit);
|
||||
assertEquals(1, y.getParentCount());
|
||||
assertEquals(b, y.getParent(0));
|
||||
|
||||
RevCommit z = rw.next();
|
||||
assertTrue(z instanceof FilteredRevCommit);
|
||||
assertEquals(0, z.getParentCount());
|
||||
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
||||
|
@ -441,8 +474,8 @@ public void testWithTopoSortAndTreeFilter2() throws Exception {
|
|||
rw.sort(RevSort.TOPO, true);
|
||||
rw.setTreeFilter(PathFilterGroup.createFromStrings("0"));
|
||||
markStart(d);
|
||||
assertCommit(d, rw.next());
|
||||
assertCommit(c, rw.next());
|
||||
assertEquals(d, rw.next());
|
||||
assertEquals(c, rw.next());
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
||||
|
@ -458,9 +491,9 @@ public void testWithTopoNonIntermixSortAndTreeFilter() throws Exception {
|
|||
rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER, true);
|
||||
rw.setTreeFilter(PathFilterGroup.createFromStrings("0"));
|
||||
markStart(d);
|
||||
assertCommit(d, rw.next());
|
||||
assertCommit(c, rw.next());
|
||||
assertCommit(b, rw.next());
|
||||
assertEquals(d, rw.next());
|
||||
assertEquals(c, rw.next());
|
||||
assertEquals(b, rw.next());
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
||||
|
@ -476,8 +509,8 @@ public void testWithTopoNonIntermixSortAndTreeFilter2() throws Exception {
|
|||
rw.sort(RevSort.TOPO_KEEP_BRANCH_TOGETHER, true);
|
||||
rw.setTreeFilter(PathFilterGroup.createFromStrings("0"));
|
||||
markStart(d);
|
||||
assertCommit(d, rw.next());
|
||||
assertCommit(c, rw.next());
|
||||
assertEquals(d, rw.next());
|
||||
assertEquals(c, rw.next());
|
||||
assertNull(rw.next());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
package org.eclipse.jgit.revwalk;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -55,7 +56,7 @@ public void testNoRename() throws Exception {
|
|||
final RevCommit a = commit(tree(file("0", blob("0"))));
|
||||
follow("0");
|
||||
markStart(a);
|
||||
assertCommit(a, rw.next());
|
||||
assertEquals(a, rw.next());
|
||||
assertNull(rw.next());
|
||||
|
||||
assertNoRenames();
|
||||
|
@ -72,8 +73,8 @@ public void testSingleRename() throws Exception {
|
|||
|
||||
follow("b");
|
||||
markStart(renameCommit);
|
||||
assertCommit(renameCommit, rw.next());
|
||||
assertCommit(a, rw.next());
|
||||
assertEquals(renameCommit, rw.next());
|
||||
assertEquals(a, rw.next());
|
||||
assertNull(rw.next());
|
||||
|
||||
assertRenames("a->b");
|
||||
|
@ -101,10 +102,10 @@ public void testMultiRename() throws Exception {
|
|||
|
||||
follow("a");
|
||||
markStart(renameCommit3);
|
||||
assertCommit(renameCommit3, rw.next());
|
||||
assertCommit(renameCommit2, rw.next());
|
||||
assertCommit(renameCommit1, rw.next());
|
||||
assertCommit(a, rw.next());
|
||||
assertEquals(renameCommit3, rw.next());
|
||||
assertEquals(renameCommit2, rw.next());
|
||||
assertEquals(renameCommit1, rw.next());
|
||||
assertEquals(a, rw.next());
|
||||
assertNull(rw.next());
|
||||
|
||||
assertRenames("c->a", "b->c", "a->b");
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
package org.eclipse.jgit.revwalk;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.util.Collections;
|
||||
|
@ -23,8 +24,8 @@
|
|||
|
||||
public class RevWalkPathFilter1Test extends RevWalkTestCase {
|
||||
protected void filter(String path) {
|
||||
rw.setTreeFilter(AndTreeFilter.create(PathFilterGroup
|
||||
.createFromStrings(Collections.singleton(path)),
|
||||
rw.setTreeFilter(AndTreeFilter.create(
|
||||
PathFilterGroup.createFromStrings(Collections.singleton(path)),
|
||||
TreeFilter.ANY_DIFF));
|
||||
}
|
||||
|
||||
|
@ -49,7 +50,7 @@ public void testSimple1() throws Exception {
|
|||
final RevCommit a = commit(tree(file("0", blob("0"))));
|
||||
filter("0");
|
||||
markStart(a);
|
||||
assertCommit(a, rw.next());
|
||||
assertEquals(a, rw.next());
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
||||
|
@ -72,10 +73,10 @@ public void testEdits_MatchAll() throws Exception {
|
|||
final RevCommit d = commit(tree(file("0", blob("d"))), c);
|
||||
filter("0");
|
||||
markStart(d);
|
||||
assertCommit(d, rw.next());
|
||||
assertCommit(c, rw.next());
|
||||
assertCommit(b, rw.next());
|
||||
assertCommit(a, rw.next());
|
||||
assertEquals(d, rw.next());
|
||||
assertEquals(c, rw.next());
|
||||
assertEquals(b, rw.next());
|
||||
assertEquals(a, rw.next());
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
||||
|
@ -87,11 +88,11 @@ public void testStringOfPearls_FilePath1() throws Exception {
|
|||
filter("d/f");
|
||||
markStart(c);
|
||||
|
||||
assertCommit(c, rw.next());
|
||||
assertEquals(c, rw.next());
|
||||
assertEquals(1, c.getParentCount());
|
||||
assertCommit(a, c.getParent(0)); // b was skipped
|
||||
assertEquals(a, c.getParent(0)); // b was skipped
|
||||
|
||||
assertCommit(a, rw.next());
|
||||
assertEquals(a, rw.next());
|
||||
assertEquals(0, a.getParentCount());
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
@ -106,11 +107,11 @@ public void testStringOfPearls_FilePath1_NoParentRewriting()
|
|||
markStart(c);
|
||||
rw.setRewriteParents(false);
|
||||
|
||||
assertCommit(c, rw.next());
|
||||
assertEquals(c, rw.next());
|
||||
assertEquals(1, c.getParentCount());
|
||||
assertCommit(b, c.getParent(0));
|
||||
assertEquals(b, c.getParent(0));
|
||||
|
||||
assertCommit(a, rw.next()); // b was skipped
|
||||
assertEquals(a, rw.next()); // b was skipped
|
||||
assertEquals(0, a.getParentCount());
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
@ -125,18 +126,18 @@ public void testStringOfPearls_FilePath2() throws Exception {
|
|||
markStart(d);
|
||||
|
||||
// d was skipped
|
||||
assertCommit(c, rw.next());
|
||||
assertEquals(c, rw.next());
|
||||
assertEquals(1, c.getParentCount());
|
||||
assertCommit(a, c.getParent(0)); // b was skipped
|
||||
assertEquals(a, c.getParent(0)); // b was skipped
|
||||
|
||||
assertCommit(a, rw.next());
|
||||
assertEquals(a, rw.next());
|
||||
assertEquals(0, a.getParentCount());
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringOfPearls_FilePath2_NoParentRewriting()
|
||||
throws Exception {
|
||||
throws Exception {
|
||||
final RevCommit a = commit(tree(file("d/f", blob("a"))));
|
||||
final RevCommit b = commit(tree(file("d/f", blob("a"))), a);
|
||||
final RevCommit c = commit(tree(file("d/f", blob("b"))), b);
|
||||
|
@ -146,12 +147,12 @@ public void testStringOfPearls_FilePath2_NoParentRewriting()
|
|||
rw.setRewriteParents(false);
|
||||
|
||||
// d was skipped
|
||||
assertCommit(c, rw.next());
|
||||
assertEquals(c, rw.next());
|
||||
assertEquals(1, c.getParentCount());
|
||||
assertCommit(b, c.getParent(0));
|
||||
assertEquals(b, c.getParent(0));
|
||||
|
||||
// b was skipped
|
||||
assertCommit(a, rw.next());
|
||||
assertEquals(a, rw.next());
|
||||
assertEquals(0, a.getParentCount());
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
@ -166,11 +167,11 @@ public void testStringOfPearls_DirPath2() throws Exception {
|
|||
markStart(d);
|
||||
|
||||
// d was skipped
|
||||
assertCommit(c, rw.next());
|
||||
assertEquals(c, rw.next());
|
||||
assertEquals(1, c.getParentCount());
|
||||
assertCommit(a, c.getParent(0)); // b was skipped
|
||||
assertEquals(a, c.getParent(0)); // b was skipped
|
||||
|
||||
assertCommit(a, rw.next());
|
||||
assertEquals(a, rw.next());
|
||||
assertEquals(0, a.getParentCount());
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
@ -211,15 +212,15 @@ public void testStringOfPearls_FilePath3() throws Exception {
|
|||
filter("d/f");
|
||||
markStart(i);
|
||||
|
||||
assertCommit(i, rw.next());
|
||||
assertEquals(i, rw.next());
|
||||
assertEquals(1, i.getParentCount());
|
||||
assertCommit(c, i.getParent(0)); // h..d was skipped
|
||||
assertEquals(c, i.getParent(0)); // h..d was skipped
|
||||
|
||||
assertCommit(c, rw.next());
|
||||
assertEquals(c, rw.next());
|
||||
assertEquals(1, c.getParentCount());
|
||||
assertCommit(a, c.getParent(0)); // b was skipped
|
||||
assertEquals(a, c.getParent(0)); // b was skipped
|
||||
|
||||
assertCommit(a, rw.next());
|
||||
assertEquals(a, rw.next());
|
||||
assertEquals(0, a.getParentCount());
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
@ -273,4 +274,49 @@ public void testStopWhenPathDisappears() throws Exception {
|
|||
assertCommit(b, rw.next());
|
||||
assertCommit(a, rw.next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommitHeaders_rewrittenParents() throws Exception {
|
||||
final RevCommit a = commit(tree(file("d/f", blob("a"))));
|
||||
final RevCommit b = commit(tree(file("d/f", blob("a"))), a);
|
||||
final RevCommit c = commit(tree(file("d/f", blob("b"))), b);
|
||||
filter("d/f");
|
||||
markStart(c);
|
||||
|
||||
RevCommit cBar = rw.next();
|
||||
assertNotNull(cBar.getShortMessage());
|
||||
assertEquals(cBar.getCommitTime(), c.getCommitTime());
|
||||
|
||||
RevCommit aBar = rw.next();
|
||||
assertNotNull(aBar.getShortMessage());
|
||||
assertEquals(aBar.getCommitTime(), a.getCommitTime());
|
||||
|
||||
assertNull(rw.next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlags_rewrittenParents() throws Exception {
|
||||
final RevCommit a = commit(tree(file("d/f", blob("a"))));
|
||||
final RevCommit b = commit(tree(file("d/f", blob("a"))), a);
|
||||
final RevCommit c = commit(tree(file("d/f", blob("b"))), b);
|
||||
|
||||
final RevFlag flag1 = rw.newFlag("flag1");
|
||||
final RevFlag flag2 = rw.newFlag("flag2");
|
||||
|
||||
a.add(flag1);
|
||||
c.add(flag2);
|
||||
|
||||
filter("d/f");
|
||||
markStart(c);
|
||||
|
||||
RevCommit cBar = rw.next();
|
||||
assertEquals(cBar.flags & RevObject.PARSED, 1);
|
||||
assertEquals(cBar.flags & flag2.mask, flag2.mask);
|
||||
|
||||
RevCommit aBar = rw.next();
|
||||
assertEquals(aBar.flags & RevObject.PARSED, 1);
|
||||
assertEquals(aBar.flags & flag1.mask, flag1.mask);
|
||||
|
||||
assertNull(rw.next());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,13 +157,14 @@ public ApplyResult call() throws GitAPIException, PatchFormatException,
|
|||
JGitText.get().renameFileFailed, f, dest), e);
|
||||
}
|
||||
apply(repository, fh.getOldPath(), cache, dest, fh);
|
||||
r.addUpdatedFile(dest);
|
||||
break;
|
||||
case COPY:
|
||||
f = getFile(fh.getOldPath(), false);
|
||||
File target = getFile(fh.getNewPath(), false);
|
||||
FileUtils.mkdirs(target.getParentFile(), true);
|
||||
Files.copy(f.toPath(), target.toPath());
|
||||
apply(repository, fh.getOldPath(), cache, target, fh);
|
||||
File src = getFile(fh.getOldPath(), false);
|
||||
f = getFile(fh.getNewPath(), false);
|
||||
FileUtils.mkdirs(f.getParentFile(), true);
|
||||
Files.copy(src.toPath(), f.toPath());
|
||||
apply(repository, fh.getOldPath(), cache, f, fh);
|
||||
}
|
||||
r.addUpdatedFile(f);
|
||||
}
|
||||
|
|
|
@ -129,6 +129,7 @@ public class BlameGenerator implements AutoCloseable {
|
|||
|
||||
/** Blame is currently assigned to this source. */
|
||||
private Candidate outCandidate;
|
||||
|
||||
private Region outRegion;
|
||||
|
||||
/**
|
||||
|
@ -395,6 +396,35 @@ private static byte[] getBytes(String path, InputStream in, long maxLength)
|
|||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a candidate object onto the generator's traversal stack.
|
||||
* <p>
|
||||
* Candidates should be pushed in history order from oldest-to-newest.
|
||||
* Applications should push the starting commit first, then the index
|
||||
* revision (if the index is interesting), and finally the working tree copy
|
||||
* (if the working tree is interesting).
|
||||
*
|
||||
* @param blameCommit
|
||||
* ordered commits to use instead of RevWalk.
|
||||
* @return {@code this}
|
||||
* @throws java.io.IOException
|
||||
* the repository cannot be read.
|
||||
* @since 6.3
|
||||
*/
|
||||
public BlameGenerator push(RevCommit blameCommit) throws IOException {
|
||||
if (!find(blameCommit, resultPath)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
Candidate c = new Candidate(getRepository(), blameCommit, resultPath);
|
||||
c.sourceBlob = idBuf.toObjectId();
|
||||
c.loadText(reader);
|
||||
c.regionList = new Region(0, 0, c.sourceText.size());
|
||||
remaining = c.sourceText.size();
|
||||
push(c);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a candidate object onto the generator's traversal stack.
|
||||
* <p>
|
||||
|
@ -428,16 +458,7 @@ public BlameGenerator push(String description, AnyObjectId id)
|
|||
}
|
||||
|
||||
RevCommit commit = revPool.parseCommit(id);
|
||||
if (!find(commit, resultPath))
|
||||
return this;
|
||||
|
||||
Candidate c = new Candidate(getRepository(), commit, resultPath);
|
||||
c.sourceBlob = idBuf.toObjectId();
|
||||
c.loadText(reader);
|
||||
c.regionList = new Region(0, 0, c.sourceText.size());
|
||||
remaining = c.sourceText.size();
|
||||
push(c);
|
||||
return this;
|
||||
return push(commit);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -605,7 +626,7 @@ public boolean next() throws IOException {
|
|||
// Do not generate a tip of a reverse. The region
|
||||
// survives and should not appear to be deleted.
|
||||
|
||||
} else /* if (pCnt == 0) */{
|
||||
} else /* if (pCnt == 0) */ {
|
||||
// Root commit, with at least one surviving region.
|
||||
// Assign the remaining blame here.
|
||||
return result(n);
|
||||
|
@ -846,8 +867,8 @@ private boolean processMerge(Candidate n) throws IOException {
|
|||
editList = new EditList(0);
|
||||
} else {
|
||||
p.loadText(reader);
|
||||
editList = diffAlgorithm.diff(textComparator,
|
||||
p.sourceText, n.sourceText);
|
||||
editList = diffAlgorithm.diff(textComparator, p.sourceText,
|
||||
n.sourceText);
|
||||
}
|
||||
|
||||
if (editList.isEmpty()) {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
import org.eclipse.jgit.dircache.DirCache;
|
||||
import org.eclipse.jgit.dircache.DirCacheBuilder;
|
||||
import org.eclipse.jgit.dircache.DirCacheEntry;
|
||||
import org.eclipse.jgit.dircache.InvalidPathException;
|
||||
import org.eclipse.jgit.gitrepo.RepoCommand.ManifestErrorException;
|
||||
import org.eclipse.jgit.gitrepo.RepoCommand.RemoteFile;
|
||||
import org.eclipse.jgit.gitrepo.RepoCommand.RemoteReader;
|
||||
|
@ -138,7 +139,7 @@ RevCommit write(List<RepoProject> repoProjects)
|
|||
}
|
||||
// In the last try, just propagate the exceptions
|
||||
return commitTreeOnCurrentTip(inserter, rw, treeId);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
} catch (IOException | InterruptedException | InvalidPathException e) {
|
||||
throw new ManifestErrorException(e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1181,12 +1181,12 @@ protected boolean mergeTrees(AbstractTreeIterator baseTree,
|
|||
workTreeUpdater.writeWorkTreeChanges(true);
|
||||
if (getUnmergedPaths().isEmpty() && !failed()) {
|
||||
WorkTreeUpdater.Result result = workTreeUpdater.writeIndexChanges();
|
||||
resultTree = result.treeId;
|
||||
modifiedFiles = result.modifiedFiles;
|
||||
for (String f : result.failedToDelete) {
|
||||
resultTree = result.getTreeId();
|
||||
modifiedFiles = result.getModifiedFiles();
|
||||
for (String f : result.getFailedToDelete()) {
|
||||
failingPaths.put(f, MergeFailureReason.COULD_NOT_DELETE);
|
||||
}
|
||||
return result.failedToDelete.isEmpty();
|
||||
return result.getFailedToDelete().isEmpty();
|
||||
}
|
||||
resultTree = null;
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (C) 2022, Google LLC.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Distribution License v. 1.0 which is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
package org.eclipse.jgit.revwalk;
|
||||
|
||||
/**
|
||||
* A filtered commit reference that overrides its parent in the DAG.
|
||||
*
|
||||
* @since 6.3
|
||||
*/
|
||||
public class FilteredRevCommit extends RevCommit {
|
||||
private RevCommit[] overriddenParents;
|
||||
|
||||
/**
|
||||
* Create a new commit reference wrapping an underlying commit reference.
|
||||
*
|
||||
* @param commit
|
||||
* commit that is being wrapped
|
||||
*/
|
||||
public FilteredRevCommit(RevCommit commit) {
|
||||
this(commit, NO_PARENTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new commit reference wrapping an underlying commit reference.
|
||||
*
|
||||
* @param commit
|
||||
* commit that is being wrapped
|
||||
* @param parents
|
||||
* overridden parents for the commit
|
||||
*/
|
||||
public FilteredRevCommit(RevCommit commit, RevCommit... parents) {
|
||||
super(commit);
|
||||
this.overriddenParents = parents;
|
||||
this.parents = NO_PARENTS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update parents on the commit
|
||||
*
|
||||
* @param overriddenParents
|
||||
* parents to be overwritten
|
||||
*/
|
||||
public void setParents(RevCommit... overriddenParents) {
|
||||
this.overriddenParents = overriddenParents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of parent commits listed in this commit.
|
||||
*
|
||||
* @return number of parents; always a positive value but can be 0 if it has
|
||||
* no parents.
|
||||
*/
|
||||
@Override
|
||||
public int getParentCount() {
|
||||
return overriddenParents.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the nth parent from this commit's parent list.
|
||||
*
|
||||
* @param nth
|
||||
* parent index to obtain. Must be in the range 0 through
|
||||
* {@link #getParentCount()}-1.
|
||||
* @return the specified parent.
|
||||
* @throws java.lang.ArrayIndexOutOfBoundsException
|
||||
* an invalid parent index was specified.
|
||||
*/
|
||||
@Override
|
||||
public RevCommit getParent(int nth) {
|
||||
return overriddenParents[nth];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain an array of all parents (<b>NOTE - THIS IS NOT A COPY</b>).
|
||||
*
|
||||
* <p>
|
||||
* This method is exposed only to provide very fast, efficient access to
|
||||
* this commit's parent list. Applications relying on this list should be
|
||||
* very careful to ensure they do not modify its contents during their use
|
||||
* of it.
|
||||
*
|
||||
* @return the array of parents.
|
||||
*/
|
||||
@Override
|
||||
public RevCommit[] getParents() {
|
||||
return overriddenParents;
|
||||
}
|
||||
}
|
|
@ -36,6 +36,11 @@
|
|||
|
||||
/**
|
||||
* A commit reference to a commit in the DAG.
|
||||
*
|
||||
* The state of the RevCommit isn't populated until the commit is parsed. The
|
||||
* newly created RevCommit is unparsed and only has an objectId reference. Other
|
||||
* states like parents, trees, commit ident, commit message, etc. are
|
||||
* populated/available when the commit is parsed.
|
||||
*/
|
||||
public class RevCommit extends RevObject {
|
||||
private static final int STACK_DEPTH = 500;
|
||||
|
@ -109,7 +114,7 @@ public static RevCommit parse(RevWalk rw, byte[] raw) throws IOException {
|
|||
*
|
||||
* @since 6.3
|
||||
*/
|
||||
protected RevCommit[] parents;
|
||||
RevCommit[] parents;
|
||||
|
||||
int commitTime; // An int here for performance, overflows in 2038
|
||||
|
||||
|
@ -127,6 +132,22 @@ protected RevCommit(AnyObjectId id) {
|
|||
super(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new commit reference.
|
||||
*
|
||||
* @param orig
|
||||
* commit to be copied from.
|
||||
*/
|
||||
RevCommit(RevCommit orig) {
|
||||
super(orig.getId());
|
||||
this.buffer = orig.buffer;
|
||||
this.commitTime = orig.commitTime;
|
||||
this.flags = orig.flags;
|
||||
this.parents = orig.parents;
|
||||
this.tree = orig.tree;
|
||||
this.inDegree = orig.inDegree;
|
||||
}
|
||||
|
||||
@Override
|
||||
void parseHeaders(RevWalk walk) throws MissingObjectException,
|
||||
IncorrectObjectTypeException, IOException {
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
package org.eclipse.jgit.revwalk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||
import org.eclipse.jgit.errors.MissingObjectException;
|
||||
|
@ -38,10 +40,13 @@ class RewriteGenerator extends Generator {
|
|||
|
||||
private final FIFORevQueue pending;
|
||||
|
||||
private final Map<RevCommit, FilteredRevCommit> transformedCommits;
|
||||
|
||||
RewriteGenerator(Generator s) {
|
||||
super(s.firstParent);
|
||||
source = s;
|
||||
pending = new FIFORevQueue(s.firstParent);
|
||||
transformedCommits = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -58,10 +63,10 @@ int outputType() {
|
|||
@Override
|
||||
RevCommit next() throws MissingObjectException,
|
||||
IncorrectObjectTypeException, IOException {
|
||||
RevCommit c = pending.next();
|
||||
FilteredRevCommit c = (FilteredRevCommit) pending.next();
|
||||
|
||||
if (c == null) {
|
||||
c = source.next();
|
||||
c = transform(source.next());
|
||||
if (c == null) {
|
||||
// We are done: Both the source generator and our internal list
|
||||
// are completely exhausted.
|
||||
|
@ -79,9 +84,9 @@ RevCommit next() throws MissingObjectException,
|
|||
final RevCommit newp = rewrite(oldp);
|
||||
if (firstParent) {
|
||||
if (newp == null) {
|
||||
c.parents = RevCommit.NO_PARENTS;
|
||||
c.setParents(RevCommit.NO_PARENTS);
|
||||
} else {
|
||||
c.parents = new RevCommit[] { newp };
|
||||
c.setParents(newp);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
@ -91,7 +96,7 @@ RevCommit next() throws MissingObjectException,
|
|||
}
|
||||
}
|
||||
if (rewrote) {
|
||||
c.parents = cleanup(pList);
|
||||
c.setParents(cleanup(pList));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
@ -111,7 +116,7 @@ private void applyFilterToParents(RevCommit c)
|
|||
for (RevCommit parent : c.getParents()) {
|
||||
while ((parent.flags & RevWalk.TREE_REV_FILTER_APPLIED) == 0) {
|
||||
|
||||
RevCommit n = source.next();
|
||||
FilteredRevCommit n = transform(source.next());
|
||||
|
||||
if (n != null) {
|
||||
pending.add(n);
|
||||
|
@ -130,6 +135,8 @@ private RevCommit rewrite(RevCommit p) throws MissingObjectException,
|
|||
IncorrectObjectTypeException, IOException {
|
||||
for (;;) {
|
||||
|
||||
p = transform(p);
|
||||
|
||||
if (p.getParentCount() > 1) {
|
||||
// This parent is a merge, so keep it.
|
||||
//
|
||||
|
@ -158,11 +165,27 @@ private RevCommit rewrite(RevCommit p) throws MissingObjectException,
|
|||
}
|
||||
|
||||
applyFilterToParents(p.getParent(0));
|
||||
p = p.getParent(0);
|
||||
p = transform(p.getParent(0));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private FilteredRevCommit transform(RevCommit c) {
|
||||
if (c == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (c instanceof FilteredRevCommit) {
|
||||
return (FilteredRevCommit) c;
|
||||
}
|
||||
|
||||
if (!transformedCommits.containsKey(c)) {
|
||||
transformedCommits.put(c, new FilteredRevCommit(c, c.getParents()));
|
||||
}
|
||||
|
||||
return transformedCommits.get(c);
|
||||
}
|
||||
|
||||
private RevCommit[] cleanup(RevCommit[] oldList) {
|
||||
// Remove any duplicate parents caused due to rewrites (e.g. a merge
|
||||
// with two sides that both simplified back into the merge base).
|
||||
|
|
|
@ -25,7 +25,10 @@
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.eclipse.jgit.annotations.NonNull;
|
||||
import org.eclipse.jgit.annotations.Nullable;
|
||||
import org.eclipse.jgit.attributes.Attribute;
|
||||
import org.eclipse.jgit.attributes.Attributes;
|
||||
|
@ -71,20 +74,33 @@ public class WorkTreeUpdater implements Closeable {
|
|||
*/
|
||||
public static class Result {
|
||||
|
||||
/**
|
||||
* Files modified during this operation.
|
||||
*/
|
||||
public List<String> modifiedFiles = new LinkedList<>();
|
||||
private final List<String> modifiedFiles = new LinkedList<>();
|
||||
|
||||
|
||||
private final List<String> failedToDelete = new LinkedList<>();
|
||||
|
||||
private ObjectId treeId = null;
|
||||
|
||||
/**
|
||||
* Files in this list were failed to be deleted.
|
||||
* @return Modified tree ID if any, or null otherwise.
|
||||
*/
|
||||
public List<String> failedToDelete = new LinkedList<>();
|
||||
public ObjectId getTreeId() {
|
||||
return treeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modified tree ID if any, or null otherwise.
|
||||
* @return Files that couldn't be deleted.
|
||||
*/
|
||||
public ObjectId treeId = null;
|
||||
public List<String> getFailedToDelete() {
|
||||
return failedToDelete;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Files modified during this operation.
|
||||
*/
|
||||
public List<String> getModifiedFiles() {
|
||||
return modifiedFiles;
|
||||
}
|
||||
}
|
||||
|
||||
Result result = new Result();
|
||||
|
@ -96,97 +112,112 @@ public static class Result {
|
|||
private final Repository repo;
|
||||
|
||||
/**
|
||||
* Set to true if this operation should work in-memory. The repo's dircache and
|
||||
* workingtree are not touched by this method. Eventually needed files are
|
||||
* created as temporary files and a new empty, in-memory dircache will be
|
||||
* used instead the repo's one. Often used for bare repos where the repo
|
||||
* Set to true if this operation should work in-memory. The repo's dircache
|
||||
* and workingtree are not touched by this method. Eventually needed files
|
||||
* are created as temporary files and a new empty, in-memory dircache will
|
||||
* be used instead the repo's one. Often used for bare repos where the repo
|
||||
* doesn't even have a workingtree and dircache.
|
||||
*/
|
||||
private final boolean inCore;
|
||||
|
||||
private final ObjectInserter inserter;
|
||||
|
||||
private final ObjectReader reader;
|
||||
|
||||
private DirCache dirCache;
|
||||
|
||||
private boolean implicitDirCache = false;
|
||||
|
||||
/**
|
||||
* Builder to update the dir cache during this operation.
|
||||
*/
|
||||
private DirCacheBuilder builder = null;
|
||||
private DirCacheBuilder builder;
|
||||
|
||||
/**
|
||||
* The {@link WorkingTreeOptions} are needed to determine line endings for affected files.
|
||||
* The {@link WorkingTreeOptions} are needed to determine line endings for
|
||||
* affected files.
|
||||
*/
|
||||
private WorkingTreeOptions workingTreeOptions;
|
||||
|
||||
/**
|
||||
* The size limit (bytes) which controls a file to be stored in {@code Heap} or {@code LocalFile}
|
||||
* during the operation.
|
||||
* The size limit (bytes) which controls a file to be stored in {@code Heap}
|
||||
* or {@code LocalFile} during the operation.
|
||||
*/
|
||||
private int inCoreFileSizeLimit;
|
||||
|
||||
/**
|
||||
* If the operation has nothing to do for a file but check it out at the end of the operation, it
|
||||
* can be added here.
|
||||
* If the operation has nothing to do for a file but check it out at the end
|
||||
* of the operation, it can be added here.
|
||||
*/
|
||||
private final Map<String, DirCacheEntry> toBeCheckedOut = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Files in this list will be deleted from the local copy at the end of the operation.
|
||||
* Files in this list will be deleted from the local copy at the end of the
|
||||
* operation.
|
||||
*/
|
||||
private final TreeMap<String, File> toBeDeleted = new TreeMap<>();
|
||||
|
||||
/**
|
||||
* Keeps {@link CheckoutMetadata} for {@link #checkout()}.
|
||||
*/
|
||||
private Map<String, CheckoutMetadata> checkoutMetadata;
|
||||
private Map<String, CheckoutMetadata> checkoutMetadataByPath;
|
||||
|
||||
/**
|
||||
* Keeps {@link CheckoutMetadata} for {@link #revertModifiedFiles()}.
|
||||
*/
|
||||
private Map<String, CheckoutMetadata> cleanupMetadata;
|
||||
private Map<String, CheckoutMetadata> cleanupMetadataByPath;
|
||||
|
||||
/**
|
||||
* Whether the changes were successfully written
|
||||
* Whether the changes were successfully written.
|
||||
*/
|
||||
private boolean indexChangesWritten = false;
|
||||
private boolean indexChangesWritten;
|
||||
|
||||
/**
|
||||
* @param repo the {@link org.eclipse.jgit.lib.Repository}.
|
||||
* @param dirCache if set, use the provided dir cache. Otherwise, use the default repository one
|
||||
* @param repo
|
||||
* the {@link Repository}.
|
||||
* @param dirCache
|
||||
* if set, use the provided dir cache. Otherwise, use the default
|
||||
* repository one
|
||||
*/
|
||||
private WorkTreeUpdater(
|
||||
Repository repo,
|
||||
DirCache dirCache) {
|
||||
private WorkTreeUpdater(Repository repo, DirCache dirCache) {
|
||||
this.repo = repo;
|
||||
this.dirCache = dirCache;
|
||||
|
||||
this.inCore = false;
|
||||
this.inserter = repo.newObjectInserter();
|
||||
this.reader = inserter.newReader();
|
||||
this.workingTreeOptions = repo.getConfig().get(WorkingTreeOptions.KEY);
|
||||
this.checkoutMetadata = new HashMap<>();
|
||||
this.cleanupMetadata = new HashMap<>();
|
||||
this.inCoreFileSizeLimit = setInCoreFileSizeLimit(repo.getConfig());
|
||||
Config config = repo.getConfig();
|
||||
this.workingTreeOptions = config.get(WorkingTreeOptions.KEY);
|
||||
this.inCoreFileSizeLimit = getInCoreFileSizeLimit(config);
|
||||
this.checkoutMetadataByPath = new HashMap<>();
|
||||
this.cleanupMetadataByPath = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param repo the {@link org.eclipse.jgit.lib.Repository}.
|
||||
* @param dirCache if set, use the provided dir cache. Otherwise, use the default repository one
|
||||
* @return an IO handler.
|
||||
* Creates a new {@link WorkTreeUpdater} for the given repository.
|
||||
*
|
||||
* @param repo
|
||||
* the {@link Repository}.
|
||||
* @param dirCache
|
||||
* if set, use the provided dir cache. Otherwise, use the default
|
||||
* repository one
|
||||
* @return the {@link WorkTreeUpdater}.
|
||||
*/
|
||||
public static WorkTreeUpdater createWorkTreeUpdater(Repository repo, DirCache dirCache) {
|
||||
public static WorkTreeUpdater createWorkTreeUpdater(Repository repo,
|
||||
DirCache dirCache) {
|
||||
return new WorkTreeUpdater(repo, dirCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param repo the {@link org.eclipse.jgit.lib.Repository}.
|
||||
* @param dirCache if set, use the provided dir cache. Otherwise, creates a new one
|
||||
* @param oi to use for writing the modified objects with.
|
||||
* @param repo
|
||||
* the {@link Repository}.
|
||||
* @param dirCache
|
||||
* if set, use the provided dir cache. Otherwise, creates a new
|
||||
* one
|
||||
* @param oi
|
||||
* to use for writing the modified objects with.
|
||||
*/
|
||||
private WorkTreeUpdater(
|
||||
Repository repo,
|
||||
DirCache dirCache,
|
||||
private WorkTreeUpdater(Repository repo, DirCache dirCache,
|
||||
ObjectInserter oi) {
|
||||
this.repo = repo;
|
||||
this.dirCache = dirCache;
|
||||
|
@ -195,18 +226,24 @@ private WorkTreeUpdater(
|
|||
this.inCore = true;
|
||||
this.reader = oi.newReader();
|
||||
if (repo != null) {
|
||||
this.inCoreFileSizeLimit = setInCoreFileSizeLimit(repo.getConfig());
|
||||
this.inCoreFileSizeLimit = getInCoreFileSizeLimit(repo.getConfig());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param repo the {@link org.eclipse.jgit.lib.Repository}.
|
||||
* @param dirCache if set, use the provided dir cache. Otherwise, creates a new one
|
||||
* @param oi to use for writing the modified objects with.
|
||||
* @return an IO handler.
|
||||
* Creates a new {@link WorkTreeUpdater} that works in memory only.
|
||||
*
|
||||
* @param repo
|
||||
* the {@link Repository}.
|
||||
* @param dirCache
|
||||
* if set, use the provided dir cache. Otherwise, creates a new
|
||||
* one
|
||||
* @param oi
|
||||
* to use for writing the modified objects with.
|
||||
* @return the {@link WorkTreeUpdater}
|
||||
*/
|
||||
public static WorkTreeUpdater createInCoreWorkTreeUpdater(Repository repo, DirCache dirCache,
|
||||
ObjectInserter oi) {
|
||||
public static WorkTreeUpdater createInCoreWorkTreeUpdater(Repository repo,
|
||||
DirCache dirCache, ObjectInserter oi) {
|
||||
return new WorkTreeUpdater(repo, dirCache, oi);
|
||||
}
|
||||
|
||||
|
@ -219,17 +256,15 @@ public interface StreamSupplier {
|
|||
* Loads the input stream.
|
||||
*
|
||||
* @return the loaded stream
|
||||
* @throws IOException if any reading error occurs
|
||||
* @throws IOException
|
||||
* if any reading error occurs
|
||||
*/
|
||||
InputStream load() throws IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* We write the patch result to a {@link org.eclipse.jgit.util.TemporaryBuffer} and then use
|
||||
* {@link DirCacheCheckout}.getContent() to run the result through the CR-LF and smudge filters.
|
||||
* DirCacheCheckout needs an ObjectLoader, not a TemporaryBuffer, so this class bridges between
|
||||
* the two, making any Stream provided by a {@link StreamSupplier} look like an ordinary git blob
|
||||
* to DirCacheCheckout.
|
||||
* We want to use DirCacheCheckout for its CR-LF and smudge filters, but DirCacheCheckout needs an
|
||||
* ObjectLoader rather than InputStream. This class provides a bridge between the two.
|
||||
*/
|
||||
public static class StreamLoader extends ObjectLoader {
|
||||
|
||||
|
@ -264,24 +299,28 @@ public byte[] getCachedBytes() throws LargeObjectException {
|
|||
|
||||
@Override
|
||||
public ObjectStream openStream() throws IOException {
|
||||
return new ObjectStream.Filter(getType(), getSize(), new BufferedInputStream(data.load()));
|
||||
return new ObjectStream.Filter(getType(), getSize(),
|
||||
new BufferedInputStream(data.load()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates stream loader for the given supplier.
|
||||
*
|
||||
* @param supplier to wrap
|
||||
* @param length of the supplied content
|
||||
* @param supplier
|
||||
* to wrap
|
||||
* @param length
|
||||
* of the supplied content
|
||||
* @return the result stream loader
|
||||
*/
|
||||
public static StreamLoader createStreamLoader(StreamSupplier supplier, long length) {
|
||||
public static StreamLoader createStreamLoader(StreamSupplier supplier,
|
||||
long length) {
|
||||
return new StreamLoader(supplier, length);
|
||||
}
|
||||
|
||||
private static int setInCoreFileSizeLimit(Config config) {
|
||||
return config.getInt(
|
||||
ConfigConstants.CONFIG_MERGE_SECTION, ConfigConstants.CONFIG_KEY_IN_CORE_LIMIT, 10 << 20);
|
||||
private static int getInCoreFileSizeLimit(Config config) {
|
||||
return config.getInt(ConfigConstants.CONFIG_MERGE_SECTION,
|
||||
ConfigConstants.CONFIG_KEY_IN_CORE_LIMIT, 10 << 20);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -297,7 +336,8 @@ public int getInCoreFileSizeLimit() {
|
|||
* Gets dir cache for the repo. Locked if not inCore.
|
||||
*
|
||||
* @return the result dir cache
|
||||
* @throws IOException is case the dir cache cannot be read
|
||||
* @throws IOException
|
||||
* is case the dir cache cannot be read
|
||||
*/
|
||||
public DirCache getLockedDirCache() throws IOException {
|
||||
if (dirCache == null) {
|
||||
|
@ -305,7 +345,7 @@ public DirCache getLockedDirCache() throws IOException {
|
|||
if (inCore) {
|
||||
dirCache = DirCache.newInCore();
|
||||
} else {
|
||||
dirCache = nonNullNonBareRepo().lockDirCache();
|
||||
dirCache = nonNullRepo().lockDirCache();
|
||||
}
|
||||
}
|
||||
if (builder == null) {
|
||||
|
@ -315,21 +355,25 @@ public DirCache getLockedDirCache() throws IOException {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates build iterator for the handler's builder.
|
||||
* Creates a {@link DirCacheBuildIterator} for the builder of this
|
||||
* {@link WorkTreeUpdater}.
|
||||
*
|
||||
* @return the iterator
|
||||
* @return the {@link DirCacheBuildIterator}
|
||||
*/
|
||||
public DirCacheBuildIterator createDirCacheBuildIterator() {
|
||||
return new DirCacheBuildIterator(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the changes to the WorkTree (but not the index).
|
||||
* Writes the changes to the working tree (but not to the index).
|
||||
*
|
||||
* @param shouldCheckoutTheirs before committing the changes
|
||||
* @throws IOException if any of the writes fail
|
||||
* @param shouldCheckoutTheirs
|
||||
* before committing the changes
|
||||
* @throws IOException
|
||||
* if any of the writes fail
|
||||
*/
|
||||
public void writeWorkTreeChanges(boolean shouldCheckoutTheirs) throws IOException {
|
||||
public void writeWorkTreeChanges(boolean shouldCheckoutTheirs)
|
||||
throws IOException {
|
||||
handleDeletedFiles();
|
||||
|
||||
if (inCore) {
|
||||
|
@ -356,8 +400,9 @@ public void writeWorkTreeChanges(boolean shouldCheckoutTheirs) throws IOExceptio
|
|||
/**
|
||||
* Writes the changes to the index.
|
||||
*
|
||||
* @return the Result of the operation.
|
||||
* @throws IOException if any of the writes fail
|
||||
* @return the {@link Result} of the operation.
|
||||
* @throws IOException
|
||||
* if any of the writes fail
|
||||
*/
|
||||
public Result writeIndexChanges() throws IOException {
|
||||
result.treeId = getLockedDirCache().writeTree(inserter);
|
||||
|
@ -366,32 +411,42 @@ public Result writeIndexChanges() throws IOException {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link DirCacheEntry} for direct checkout and remembers its {@link CheckoutMetadata}.
|
||||
* Adds a {@link DirCacheEntry} for direct checkout and remembers its
|
||||
* {@link CheckoutMetadata}.
|
||||
*
|
||||
* @param path of the entry
|
||||
* @param entry to add
|
||||
* @param cleanupStreamType to use for the cleanup metadata
|
||||
* @param cleanupSmudgeCommand to use for the cleanup metadata
|
||||
* @param checkoutStreamType to use for the checkout metadata
|
||||
* @param checkoutSmudgeCommand to use for the checkout metadata
|
||||
* @since 6.1
|
||||
* @param path
|
||||
* of the entry
|
||||
* @param entry
|
||||
* to add
|
||||
* @param cleanupStreamType
|
||||
* to use for the cleanup metadata
|
||||
* @param cleanupSmudgeCommand
|
||||
* to use for the cleanup metadata
|
||||
* @param checkoutStreamType
|
||||
* to use for the checkout metadata
|
||||
* @param checkoutSmudgeCommand
|
||||
* to use for the checkout metadata
|
||||
*/
|
||||
public void addToCheckout(
|
||||
String path, DirCacheEntry entry, EolStreamType cleanupStreamType,
|
||||
String cleanupSmudgeCommand, EolStreamType checkoutStreamType, String checkoutSmudgeCommand) {
|
||||
public void addToCheckout(String path, DirCacheEntry entry,
|
||||
EolStreamType cleanupStreamType, String cleanupSmudgeCommand,
|
||||
EolStreamType checkoutStreamType, String checkoutSmudgeCommand) {
|
||||
if (entry != null) {
|
||||
// In some cases, we just want to add the metadata.
|
||||
toBeCheckedOut.put(path, entry);
|
||||
}
|
||||
addCheckoutMetadata(cleanupMetadata, path, cleanupStreamType, cleanupSmudgeCommand);
|
||||
addCheckoutMetadata(checkoutMetadata, path, checkoutStreamType, checkoutSmudgeCommand);
|
||||
addCheckoutMetadata(cleanupMetadataByPath, path, cleanupStreamType,
|
||||
cleanupSmudgeCommand);
|
||||
addCheckoutMetadata(checkoutMetadataByPath, path, checkoutStreamType,
|
||||
checkoutSmudgeCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map which maps the paths of files which have to be checked out because the operation
|
||||
* created new fully-merged content for this file into the index.
|
||||
*
|
||||
* <p>This means: the operation wrote a new stage 0 entry for this path.</p>
|
||||
* Gets a map which maps the paths of files which have to be checked out
|
||||
* because the operation created new fully-merged content for this file into
|
||||
* the index.
|
||||
* <p>
|
||||
* This means: the operation wrote a new stage 0 entry for this path.
|
||||
* </p>
|
||||
*
|
||||
* @return the map
|
||||
*/
|
||||
|
@ -400,37 +455,43 @@ public Map<String, DirCacheEntry> getToBeCheckedOut() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Deletes the given file
|
||||
* Remembers the given file to be deleted.
|
||||
* <p>
|
||||
* Note the actual deletion is only done in {@link #writeWorkTreeChanges}
|
||||
* Note the actual deletion is only done in {@link #writeWorkTreeChanges}.
|
||||
*
|
||||
* @param path of the file to be deleted
|
||||
* @param file to be deleted
|
||||
* @param streamType to use for cleanup metadata
|
||||
* @param smudgeCommand to use for cleanup metadata
|
||||
* @throws IOException if the file cannot be deleted
|
||||
* @param path
|
||||
* of the file to be deleted
|
||||
* @param file
|
||||
* to be deleted
|
||||
* @param streamType
|
||||
* to use for cleanup metadata
|
||||
* @param smudgeCommand
|
||||
* to use for cleanup metadata
|
||||
*/
|
||||
public void deleteFile(String path, File file, EolStreamType streamType, String smudgeCommand)
|
||||
throws IOException {
|
||||
public void deleteFile(String path, File file, EolStreamType streamType,
|
||||
String smudgeCommand) {
|
||||
toBeDeleted.put(path, file);
|
||||
if (file != null && file.isFile()) {
|
||||
addCheckoutMetadata(cleanupMetadata, path, streamType, smudgeCommand);
|
||||
addCheckoutMetadata(cleanupMetadataByPath, path, streamType,
|
||||
smudgeCommand);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remembers the {@link CheckoutMetadata} for the given path; it may be needed in {@link
|
||||
* #checkout()} or in {@link #revertModifiedFiles()}.
|
||||
* Remembers the {@link CheckoutMetadata} for the given path; it may be
|
||||
* needed in {@link #checkout()} or in {@link #revertModifiedFiles()}.
|
||||
*
|
||||
* @param map to add the metadata to
|
||||
* @param path of the current node
|
||||
* @param streamType to use for the metadata
|
||||
* @param smudgeCommand to use for the metadata
|
||||
* @since 6.1
|
||||
* @param map
|
||||
* to add the metadata to
|
||||
* @param path
|
||||
* of the current node
|
||||
* @param streamType
|
||||
* to use for the metadata
|
||||
* @param smudgeCommand
|
||||
* to use for the metadata
|
||||
*/
|
||||
private void addCheckoutMetadata(
|
||||
Map<String, CheckoutMetadata> map, String path, EolStreamType streamType,
|
||||
String smudgeCommand) {
|
||||
private void addCheckoutMetadata(Map<String, CheckoutMetadata> map,
|
||||
String path, EolStreamType streamType, String smudgeCommand) {
|
||||
if (inCore || map == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -439,18 +500,20 @@ private void addCheckoutMetadata(
|
|||
|
||||
/**
|
||||
* Detects if CRLF conversion has been configured.
|
||||
* <p></p>
|
||||
* <p>
|
||||
* </p>
|
||||
* See {@link EolStreamTypeUtil#detectStreamType} for more info.
|
||||
*
|
||||
* @param attributes of the file for which the type is to be detected
|
||||
* @param attributes
|
||||
* of the file for which the type is to be detected
|
||||
* @return the detected type
|
||||
*/
|
||||
public EolStreamType detectCheckoutStreamType(Attributes attributes) {
|
||||
if (inCore) {
|
||||
return null;
|
||||
}
|
||||
return EolStreamTypeUtil.detectStreamType(
|
||||
OperationType.CHECKOUT_OP, workingTreeOptions, attributes);
|
||||
return EolStreamTypeUtil.detectStreamType(OperationType.CHECKOUT_OP,
|
||||
workingTreeOptions, attributes);
|
||||
}
|
||||
|
||||
private void handleDeletedFiles() {
|
||||
|
@ -470,7 +533,8 @@ private void handleDeletedFiles() {
|
|||
/**
|
||||
* Marks the given path as modified in the operation.
|
||||
*
|
||||
* @param path to mark as modified
|
||||
* @param path
|
||||
* to mark as modified
|
||||
*/
|
||||
public void markAsModified(String path) {
|
||||
result.modifiedFiles.add(path);
|
||||
|
@ -486,17 +550,15 @@ public List<String> getModifiedFiles() {
|
|||
}
|
||||
|
||||
private void checkout() throws NoWorkTreeException, IOException {
|
||||
// Iterate in reverse so that "folder/file" is deleted before
|
||||
// "folder". Otherwise, this could result in a failing path because
|
||||
// of a non-empty directory, for which delete() would fail.
|
||||
for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut.entrySet()) {
|
||||
for (Map.Entry<String, DirCacheEntry> entry : toBeCheckedOut
|
||||
.entrySet()) {
|
||||
DirCacheEntry dirCacheEntry = entry.getValue();
|
||||
if (dirCacheEntry.getFileMode() == FileMode.GITLINK) {
|
||||
new File(nonNullNonBareRepo().getWorkTree(), entry.getKey()).mkdirs();
|
||||
new File(nonNullRepo().getWorkTree(), entry.getKey())
|
||||
.mkdirs();
|
||||
} else {
|
||||
DirCacheCheckout.checkoutEntry(
|
||||
repo, dirCacheEntry, reader, false,
|
||||
checkoutMetadata.get(entry.getKey()),
|
||||
DirCacheCheckout.checkoutEntry(repo, dirCacheEntry, reader,
|
||||
false, checkoutMetadataByPath.get(entry.getKey()),
|
||||
workingTreeOptions);
|
||||
result.modifiedFiles.add(entry.getKey());
|
||||
}
|
||||
|
@ -504,11 +566,13 @@ private void checkout() throws NoWorkTreeException, IOException {
|
|||
}
|
||||
|
||||
/**
|
||||
* Reverts any uncommitted changes in the worktree. We know that for all modified files the
|
||||
* old content was in the old index and the index contained only stage 0. In case if inCore
|
||||
* operation just clear the history of modified files.
|
||||
* Reverts any uncommitted changes in the worktree. We know that for all
|
||||
* modified files the old content was in the old index and the index
|
||||
* contained only stage 0. In case of inCore operation just clear the
|
||||
* history of modified files.
|
||||
*
|
||||
* @throws java.io.IOException in case the cleaning up failed
|
||||
* @throws IOException
|
||||
* in case the cleaning up failed
|
||||
*/
|
||||
public void revertModifiedFiles() throws IOException {
|
||||
if (inCore) {
|
||||
|
@ -521,9 +585,8 @@ public void revertModifiedFiles() throws IOException {
|
|||
for (String path : result.modifiedFiles) {
|
||||
DirCacheEntry entry = dirCache.getEntry(path);
|
||||
if (entry != null) {
|
||||
DirCacheCheckout.checkoutEntry(
|
||||
repo, entry, reader, false, cleanupMetadata.get(path),
|
||||
workingTreeOptions);
|
||||
DirCacheCheckout.checkoutEntry(repo, entry, reader, false,
|
||||
cleanupMetadataByPath.get(path), workingTreeOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -538,22 +601,24 @@ public void close() throws IOException {
|
|||
/**
|
||||
* Updates the file in the checkout with the given content.
|
||||
*
|
||||
* @param resultStreamLoader with the content to be updated
|
||||
* @param streamType for parsing the content
|
||||
* @param smudgeCommand for formatting the content
|
||||
* @param path of the file to be updated
|
||||
* @param file to be updated
|
||||
* @param safeWrite whether the content should be written to a buffer first
|
||||
* @throws IOException if the {@link CheckoutMetadata} cannot be determined
|
||||
* @param resultStreamLoader
|
||||
* with the content to be updated
|
||||
* @param streamType
|
||||
* for parsing the content
|
||||
* @param smudgeCommand
|
||||
* for formatting the content
|
||||
* @param path
|
||||
* of the file to be updated
|
||||
* @param file
|
||||
* to be updated
|
||||
* @param safeWrite
|
||||
* whether the content should be written to a buffer first
|
||||
* @throws IOException
|
||||
* if the file cannot be updated
|
||||
*/
|
||||
public void updateFileWithContent(
|
||||
StreamLoader resultStreamLoader,
|
||||
EolStreamType streamType,
|
||||
String smudgeCommand,
|
||||
String path,
|
||||
File file,
|
||||
boolean safeWrite)
|
||||
throws IOException {
|
||||
public void updateFileWithContent(StreamLoader resultStreamLoader,
|
||||
EolStreamType streamType, String smudgeCommand, String path,
|
||||
File file, boolean safeWrite) throws IOException {
|
||||
if (inCore) {
|
||||
return;
|
||||
}
|
||||
|
@ -584,74 +649,85 @@ public void updateFileWithContent(
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a path with the given content, and adds it to the specified stage to the index builder
|
||||
* Creates a path with the given content, and adds it to the specified stage
|
||||
* to the index builder.
|
||||
*
|
||||
* @param inputStream with the content to be updated
|
||||
* @param path of the file to be updated
|
||||
* @param fileMode of the modified file
|
||||
* @param entryStage of the new entry
|
||||
* @param lastModified instant of the modified file
|
||||
* @param len of the content
|
||||
* @param lfsAttribute for checking for LFS enablement
|
||||
* @param inputStream
|
||||
* with the content to be updated
|
||||
* @param path
|
||||
* of the file to be updated
|
||||
* @param fileMode
|
||||
* of the modified file
|
||||
* @param entryStage
|
||||
* of the new entry
|
||||
* @param lastModified
|
||||
* instant of the modified file
|
||||
* @param len
|
||||
* of the content
|
||||
* @param lfsAttribute
|
||||
* for checking for LFS enablement
|
||||
* @return the entry which was added to the index
|
||||
* @throws IOException if inserting the content fails
|
||||
* @throws IOException
|
||||
* if inserting the content fails
|
||||
*/
|
||||
public DirCacheEntry insertToIndex(
|
||||
InputStream inputStream,
|
||||
byte[] path,
|
||||
FileMode fileMode,
|
||||
int entryStage,
|
||||
Instant lastModified,
|
||||
int len,
|
||||
public DirCacheEntry insertToIndex(InputStream inputStream, byte[] path,
|
||||
FileMode fileMode, int entryStage, Instant lastModified, int len,
|
||||
Attribute lfsAttribute) throws IOException {
|
||||
StreamLoader contentLoader = createStreamLoader(() -> inputStream, len);
|
||||
return insertToIndex(contentLoader, path, fileMode, entryStage, lastModified, len,
|
||||
lfsAttribute);
|
||||
return insertToIndex(contentLoader, path, fileMode, entryStage,
|
||||
lastModified, len, lfsAttribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a path with the given content, and adds it to the specified stage to the index builder
|
||||
* Creates a path with the given content, and adds it to the specified stage
|
||||
* to the index builder.
|
||||
*
|
||||
* @param resultStreamLoader with the content to be updated
|
||||
* @param path of the file to be updated
|
||||
* @param fileMode of the modified file
|
||||
* @param entryStage of the new entry
|
||||
* @param lastModified instant of the modified file
|
||||
* @param len of the content
|
||||
* @param lfsAttribute for checking for LFS enablement
|
||||
* @param resultStreamLoader
|
||||
* with the content to be updated
|
||||
* @param path
|
||||
* of the file to be updated
|
||||
* @param fileMode
|
||||
* of the modified file
|
||||
* @param entryStage
|
||||
* of the new entry
|
||||
* @param lastModified
|
||||
* instant of the modified file
|
||||
* @param len
|
||||
* of the content
|
||||
* @param lfsAttribute
|
||||
* for checking for LFS enablement
|
||||
* @return the entry which was added to the index
|
||||
* @throws IOException if inserting the content fails
|
||||
* @throws IOException
|
||||
* if inserting the content fails
|
||||
*/
|
||||
public DirCacheEntry insertToIndex(
|
||||
StreamLoader resultStreamLoader,
|
||||
byte[] path,
|
||||
FileMode fileMode,
|
||||
int entryStage,
|
||||
Instant lastModified,
|
||||
int len,
|
||||
Attribute lfsAttribute) throws IOException {
|
||||
return addExistingToIndex(insertResult(resultStreamLoader, lfsAttribute),
|
||||
path, fileMode, entryStage, lastModified, len);
|
||||
public DirCacheEntry insertToIndex(StreamLoader resultStreamLoader,
|
||||
byte[] path, FileMode fileMode, int entryStage,
|
||||
Instant lastModified, int len, Attribute lfsAttribute)
|
||||
throws IOException {
|
||||
return addExistingToIndex(
|
||||
insertResult(resultStreamLoader, lfsAttribute), path, fileMode,
|
||||
entryStage, lastModified, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a path with the specified stage to the index builder
|
||||
* Adds a path with the specified stage to the index builder.
|
||||
*
|
||||
* @param objectId of the existing object to add
|
||||
* @param path of the modified file
|
||||
* @param fileMode of the modified file
|
||||
* @param entryStage of the new entry
|
||||
* @param lastModified instant of the modified file
|
||||
* @param len of the modified file content
|
||||
* @param objectId
|
||||
* of the existing object to add
|
||||
* @param path
|
||||
* of the modified file
|
||||
* @param fileMode
|
||||
* of the modified file
|
||||
* @param entryStage
|
||||
* of the new entry
|
||||
* @param lastModified
|
||||
* instant of the modified file
|
||||
* @param len
|
||||
* of the modified file content
|
||||
* @return the entry which was added to the index
|
||||
*/
|
||||
public DirCacheEntry addExistingToIndex(
|
||||
ObjectId objectId,
|
||||
byte[] path,
|
||||
FileMode fileMode,
|
||||
int entryStage,
|
||||
Instant lastModified,
|
||||
int len) {
|
||||
public DirCacheEntry addExistingToIndex(ObjectId objectId, byte[] path,
|
||||
FileMode fileMode, int entryStage, Instant lastModified, int len) {
|
||||
DirCacheEntry dce = new DirCacheEntry(path, entryStage);
|
||||
dce.setFileMode(fileMode);
|
||||
if (lastModified != null) {
|
||||
|
@ -664,44 +740,25 @@ public DirCacheEntry addExistingToIndex(
|
|||
return dce;
|
||||
}
|
||||
|
||||
private ObjectId insertResult(StreamLoader resultStreamLoader, Attribute lfsAttribute)
|
||||
throws IOException {
|
||||
try (LfsInputStream is =
|
||||
org.eclipse.jgit.util.LfsFactory.getInstance()
|
||||
.applyCleanFilter(
|
||||
repo,
|
||||
resultStreamLoader.data.load(),
|
||||
resultStreamLoader.size,
|
||||
lfsAttribute)) {
|
||||
private ObjectId insertResult(StreamLoader resultStreamLoader,
|
||||
Attribute lfsAttribute) throws IOException {
|
||||
try (LfsInputStream is = LfsFactory.getInstance().applyCleanFilter(repo,
|
||||
resultStreamLoader.data.load(), resultStreamLoader.size,
|
||||
lfsAttribute)) {
|
||||
return inserter.insert(OBJ_BLOB, is.getLength(), is);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets non-null repository instance
|
||||
* Gets the non-null repository instance of this {@link WorkTreeUpdater}.
|
||||
*
|
||||
* @return non-null repository instance
|
||||
* @throws java.lang.NullPointerException if the handler was constructed without a repository.
|
||||
* @throws NullPointerException
|
||||
* if the handler was constructed without a repository.
|
||||
*/
|
||||
@NonNull
|
||||
private Repository nonNullRepo() throws NullPointerException {
|
||||
if (repo == null) {
|
||||
throw new NullPointerException(JGitText.get().repositoryIsRequired);
|
||||
}
|
||||
return repo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets non-null and non-bare repository instance
|
||||
*
|
||||
* @return non-null and non-bare repository instance
|
||||
* @throws java.lang.NullPointerException if the handler was constructed without a repository.
|
||||
* @throws NoWorkTreeException if the handler was constructed with a bare repository
|
||||
*/
|
||||
private Repository nonNullNonBareRepo() throws NullPointerException, NoWorkTreeException {
|
||||
if (nonNullRepo().isBare()) {
|
||||
throw new NoWorkTreeException();
|
||||
}
|
||||
return repo;
|
||||
return Objects.requireNonNull(repo,
|
||||
() -> JGitText.get().repositoryIsRequired);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue