diff options
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit')
4 files changed, 179 insertions, 58 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBundleWriter.java new file mode 100644 index 0000000000..736f381d78 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsBundleWriter.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020, Google LLC and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +package org.eclipse.jgit.internal.storage.dfs; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jgit.internal.storage.pack.CachedPack; +import org.eclipse.jgit.lib.ProgressMonitor; +import org.eclipse.jgit.transport.BundleWriter; + +/** Writes {@link DfsRepository} to a Git bundle. */ +public class DfsBundleWriter { + /** + * Writes the entire {@link DfsRepository} to a Git bundle. + * <p> + * This method try to avoid traversing the pack files as much as possible + * and dumps all objects as-is to a Git bundle. + * + * @param pm + * progress monitor + * @param os + * Git bundle output + * @param db + * repository + * @throws IOException + * thrown if the output stream throws one. + */ + public static void writeEntireRepositoryAsBundle(ProgressMonitor pm, + OutputStream os, DfsRepository db) throws IOException { + BundleWriter bw = new BundleWriter(db); + db.getRefDatabase().getRefs().forEach(bw::include); + List<CachedPack> packs = new ArrayList<>(); + for (DfsPackFile p : db.getObjectDatabase().getPacks()) { + packs.add(new DfsCachedPack(p)); + } + bw.addObjectsAsIs(packs); + bw.writeBundle(pm, os); + } + + private DfsBundleWriter() { + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java index 9e409490fa..3e4b5df6aa 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java @@ -756,6 +756,19 @@ public class PackWriter implements AutoCloseable { /** * Prepare the list of objects to be written to the pack stream. + * + * <p> + * PackWriter will concat and write out the specified packs as-is. + * + * @param c + * cached packs to be written. + */ + public void preparePack(Collection<? extends CachedPack> c) { + cachedPacks.addAll(c); + } + + /** + * Prepare the list of objects to be written to the pack stream. * <p> * Basing on these 2 sets, another set of objects to put in a pack file is * created: this set consists of all objects reachable (ancestors) from diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java index 506d333120..6c217fdf25 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java @@ -588,7 +588,8 @@ public class ResolveMerger extends ThreeWayMerger { final int modeO = tw.getRawMode(T_OURS); final int modeT = tw.getRawMode(T_THEIRS); final int modeB = tw.getRawMode(T_BASE); - + boolean gitLinkMerging = isGitLink(modeO) || isGitLink(modeT) + || isGitLink(modeB); if (modeO == 0 && modeT == 0 && modeB == 0) // File is either untracked or new, staged but uncommitted return true; @@ -737,31 +738,28 @@ public class ResolveMerger extends ThreeWayMerger { return false; } - boolean gitlinkConflict = isGitLink(modeO) || isGitLink(modeT); - // Don't attempt to resolve submodule link conflicts - if (gitlinkConflict || !attributes.canBeContentMerged()) { + if (gitLinkMerging && ignoreConflicts) { + // Always select 'ours' in case of GITLINK merge failures so + // a caller can use virtual commit. + add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0, EPOCH, 0); + return true; + } else if (gitLinkMerging) { + add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0); + add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0); + add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0); + MergeResult<SubmoduleConflict> result = createGitLinksMergeResult( + base, ours, theirs); + result.setContainsConflicts(true); + mergeResults.put(tw.getPathString(), result); + unmergedPaths.add(tw.getPathString()); + return true; + } else if (!attributes.canBeContentMerged()) { add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0); add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0); add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0); - if (gitlinkConflict) { - MergeResult<SubmoduleConflict> result = new MergeResult<>( - Arrays.asList( - new SubmoduleConflict(base == null ? null - : base.getEntryObjectId()), - new SubmoduleConflict(ours == null ? null - : ours.getEntryObjectId()), - new SubmoduleConflict(theirs == null ? null - : theirs.getEntryObjectId()))); - result.setContainsConflicts(true); - mergeResults.put(tw.getPathString(), result); - if (!ignoreConflicts) { - unmergedPaths.add(tw.getPathString()); - } - } else { - // attribute merge issues are conflicts but not failures - unmergedPaths.add(tw.getPathString()); - } + // attribute merge issues are conflicts but not failures + unmergedPaths.add(tw.getPathString()); return true; } @@ -786,45 +784,73 @@ public class ResolveMerger extends ThreeWayMerger { // OURS or THEIRS has been deleted if (((modeO != 0 && !tw.idEqual(T_BASE, T_OURS)) || (modeT != 0 && !tw .idEqual(T_BASE, T_THEIRS)))) { - MergeResult<RawText> result = contentMerge(base, ours, theirs, - attributes); - - if (ignoreConflicts) { - // In case a conflict is detected the working tree file is - // again filled with new content (containing conflict - // markers). But also stage 0 of the index is filled with - // that content. - result.setContainsConflicts(false); - updateIndex(base, ours, theirs, result, attributes); - } else { + if (gitLinkMerging && ignoreConflicts) { + add(tw.getRawPath(), ours, DirCacheEntry.STAGE_0, EPOCH, 0); + } else if (gitLinkMerging) { add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0); add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0); - DirCacheEntry e = add(tw.getRawPath(), theirs, - DirCacheEntry.STAGE_3, EPOCH, 0); - - // OURS was deleted checkout THEIRS - if (modeO == 0) { - // Check worktree before checking out THEIRS - if (isWorktreeDirty(work, ourDce)) { - return false; - } - if (nonTree(modeT)) { - if (e != null) { - addToCheckout(tw.getPathString(), e, attributes); + add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0); + MergeResult<SubmoduleConflict> result = createGitLinksMergeResult( + base, ours, theirs); + result.setContainsConflicts(true); + mergeResults.put(tw.getPathString(), result); + unmergedPaths.add(tw.getPathString()); + } else { + MergeResult<RawText> result = contentMerge(base, ours, + theirs, attributes); + + if (ignoreConflicts) { + // In case a conflict is detected the working tree file + // is again filled with new content (containing conflict + // markers). But also stage 0 of the index is filled + // with that content. + result.setContainsConflicts(false); + updateIndex(base, ours, theirs, result, attributes); + } else { + add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, + 0); + add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, + 0); + DirCacheEntry e = add(tw.getRawPath(), theirs, + DirCacheEntry.STAGE_3, EPOCH, 0); + + // OURS was deleted checkout THEIRS + if (modeO == 0) { + // Check worktree before checking out THEIRS + if (isWorktreeDirty(work, ourDce)) { + return false; + } + if (nonTree(modeT)) { + if (e != null) { + addToCheckout(tw.getPathString(), e, + attributes); + } } } - } - unmergedPaths.add(tw.getPathString()); + unmergedPaths.add(tw.getPathString()); - // generate a MergeResult for the deleted file - mergeResults.put(tw.getPathString(), result); + // generate a MergeResult for the deleted file + mergeResults.put(tw.getPathString(), result); + } } } } return true; } + private static MergeResult<SubmoduleConflict> createGitLinksMergeResult( + CanonicalTreeParser base, CanonicalTreeParser ours, + CanonicalTreeParser theirs) { + return new MergeResult<>(Arrays.asList( + new SubmoduleConflict( + base == null ? null : base.getEntryObjectId()), + new SubmoduleConflict( + ours == null ? null : ours.getEntryObjectId()), + new SubmoduleConflict( + theirs == null ? null : theirs.getEntryObjectId()))); + } + /** * Does the content merge. The three texts base, ours and theirs are * specified with {@link CanonicalTreeParser}. If any of the parsers is diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java index 57eed3ad2a..e1aa9d72fb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BundleWriter.java @@ -17,12 +17,16 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.internal.storage.pack.CachedPack; import org.eclipse.jgit.internal.storage.pack.PackWriter; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; @@ -62,6 +66,8 @@ public class BundleWriter { private final Set<ObjectId> tagTargets; + private final List<CachedPack> cachedPacks = new ArrayList<>(); + private PackConfig packConfig; private ObjectCountCallback callback; @@ -150,6 +156,25 @@ public class BundleWriter { } /** + * Add objects to the bundle file. + * + * <p> + * When this method is used, object traversal is disabled and specified pack + * files are directly saved to the Git bundle file. + * + * <p> + * Unlike {@link #include}, this doesn't affect the refs. Even if the + * objects are not reachable from any ref, they will be included in the + * bundle file. + * + * @param c + * pack to include + */ + public void addObjectsAsIs(Collection<? extends CachedPack> c) { + cachedPacks.addAll(c); + } + + /** * Assume a commit is available on the recipient's side. * <p> * In order to fetch from a bundle the recipient must have any assumed @@ -187,19 +212,24 @@ public class BundleWriter { try (PackWriter packWriter = newPackWriter()) { packWriter.setObjectCountCallback(callback); - final HashSet<ObjectId> inc = new HashSet<>(); - final HashSet<ObjectId> exc = new HashSet<>(); - inc.addAll(include.values()); - for (RevCommit r : assume) - exc.add(r.getId()); packWriter.setIndexDisabled(true); packWriter.setDeltaBaseAsOffset(true); - packWriter.setThin(!exc.isEmpty()); packWriter.setReuseValidatingObjects(false); - if (exc.isEmpty()) { - packWriter.setTagTargets(tagTargets); + if (cachedPacks.isEmpty()) { + HashSet<ObjectId> inc = new HashSet<>(); + HashSet<ObjectId> exc = new HashSet<>(); + inc.addAll(include.values()); + for (RevCommit r : assume) { + exc.add(r.getId()); + } + if (exc.isEmpty()) { + packWriter.setTagTargets(tagTargets); + } + packWriter.setThin(!exc.isEmpty()); + packWriter.preparePack(monitor, inc, exc); + } else { + packWriter.preparePack(cachedPacks); } - packWriter.preparePack(monitor, inc, exc); final Writer w = new OutputStreamWriter(os, UTF_8); w.write(TransportBundle.V2_BUNDLE_SIGNATURE); |