]> source.dussan.org Git - jgit.git/commitdiff
Add pack-refs command to the CLI 44/1202844/12 master
authorYash Chaturvedi <quic_zeref@quicinc.com>
Thu, 14 Nov 2024 14:33:08 +0000 (20:03 +0530)
committerYash Chaturvedi <quic_zeref@quicinc.com>
Fri, 22 Nov 2024 04:37:07 +0000 (10:07 +0530)
This command can be used to optimize storage of references.

For a RefDirectory database, it packs non-symbolic, loose refs into
packed-refs. By default, only the references under '$GIT_DIR/refs/tags'
are packed. The '--all' option can be used to pack all the references
under '$GIT_DIR/refs'.

For Reftable, all refs are compacted into a single table.

Change-Id: I92e786403f8638d35ae3037844a7ad89e8959e02

14 files changed:
org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/PackRefsTest.java [new file with mode: 0644]
org.eclipse.jgit.pgm/META-INF/services/org.eclipse.jgit.pgm.TextBuiltin
org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/PackRefs.java [new file with mode: 0644]
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/GcPackRefsTest.java
org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
org.eclipse.jgit/src/org/eclipse/jgit/api/PackRefsCommand.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileRepository.java
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/GC.java
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java

diff --git a/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/PackRefsTest.java b/org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/PackRefsTest.java
new file mode 100644 (file)
index 0000000..b4d4ea9
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License 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.pgm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
+import org.eclipse.jgit.lib.CLIRepositoryTestCase;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PackRefsTest extends CLIRepositoryTestCase {
+       private Git git;
+
+       @Override
+       @Before
+       public void setUp() throws Exception {
+               super.setUp();
+               git = new Git(db);
+               git.commit().setMessage("initial commit").call();
+       }
+
+       @Test
+       public void tagPacked() throws Exception {
+               git.tag().setName("test").call();
+               git.packRefs().call();
+               assertEquals(Ref.Storage.PACKED,
+                               git.getRepository().exactRef("refs/tags/test").getStorage());
+       }
+
+       @Test
+       public void nonTagRefNotPackedWithoutAll() throws Exception {
+               git.branchCreate().setName("test").call();
+               git.packRefs().call();
+               assertEquals(Ref.Storage.LOOSE,
+                               git.getRepository().exactRef("refs/heads/test").getStorage());
+       }
+
+       @Test
+       public void nonTagRefPackedWithAll() throws Exception {
+               git.branchCreate().setName("test").call();
+               git.packRefs().setAll(true).call();
+               assertEquals(Ref.Storage.PACKED,
+                               git.getRepository().exactRef("refs/heads/test").getStorage());
+       }
+
+       @Test
+       public void refTableCompacted() throws Exception {
+               ((FileRepository) git.getRepository()).convertRefStorage(
+                               ConfigConstants.CONFIG_REF_STORAGE_REFTABLE, false, false);
+
+               git.commit().setMessage("test commit").call();
+               File tableDir = new File(db.getDirectory(), Constants.REFTABLE);
+               File[] reftables = tableDir.listFiles();
+               assertNotNull(reftables);
+               assertTrue(reftables.length > 2);
+
+               git.packRefs().call();
+
+               reftables = tableDir.listFiles();
+               assertNotNull(reftables);
+               assertEquals(2, reftables.length);
+       }
+}
index 08d37278de0a85c681e59fb07ad697bad80f80ea..41b0091b77d6ce5ae81c8268a51146aa8b623eec 100644 (file)
@@ -26,6 +26,7 @@ org.eclipse.jgit.pgm.LsTree
 org.eclipse.jgit.pgm.Merge
 org.eclipse.jgit.pgm.MergeBase
 org.eclipse.jgit.pgm.MergeTool
+org.eclipse.jgit.pgm.PackRefs
 org.eclipse.jgit.pgm.Push
 org.eclipse.jgit.pgm.ReceivePack
 org.eclipse.jgit.pgm.Reflog
index 50ee809b9833ac5d1bea1fb018ca7c4f2f59b36c..d24b639a31971340e1ceb282427a8ea3cb628fb8 100644 (file)
@@ -257,6 +257,7 @@ updating=Updating {0}..{1}
 usage_Abbrev=Instead of using the default number of hexadecimal digits (which will vary according to the number of objects in the repository with a default of 7) of the abbreviated object name, use <n> digits, or as many digits as needed to form a unique object name. An <n> of 0 will suppress long format, only showing the closest tag.
 usage_addRenormalize=Apply the "clean" process freshly to tracked files to forcibly add them again to the index. This implies -u.
 usage_Aggressive=This option will cause gc to more aggressively optimize the repository at the expense of taking much more time
+usage_All=Pack all refs, except hidden refs, broken refs, and symbolic refs.
 usage_AlwaysFallback=Show uniquely abbreviated commit object as fallback
 usage_bareClone=Make a bare Git repository. That is, instead of creating [DIRECTORY] and placing the administrative files in [DIRECTORY]/.git, make the [DIRECTORY] itself the $GIT_DIR.
 usage_extraArgument=Pass an extra argument to a merge driver. Currently supported are "-X ours" and "-X theirs".
@@ -300,6 +301,7 @@ usage_Match=Only consider tags matching the given glob(7) pattern or patterns, e
 usage_MergeBase=Find as good common ancestors as possible for a merge
 usage_MergesTwoDevelopmentHistories=Merges two development histories
 usage_PackKeptObjects=Include objects in packs locked by a ".keep" file when repacking
+usage_PackRefs=Pack heads and tags for efficient repository access
 usage_PreserveOldPacks=Preserve old pack files by moving them into the preserved subdirectory instead of deleting them after repacking
 usage_PrunePreserved=Remove the preserved subdirectory containing previously preserved old pack files before repacking, and before preserving more old pack files
 usage_ReadDirCache= Read the DirCache 100 times
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/PackRefs.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/PackRefs.java
new file mode 100644 (file)
index 0000000..ee05f5c
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License 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.pgm;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.kohsuke.args4j.Option;
+
+@Command(common = true, usage = "usage_PackRefs")
+class PackRefs extends TextBuiltin {
+       @Option(name = "--all", usage = "usage_All")
+       private boolean all;
+
+       @Override
+       protected void run() {
+               Git git = Git.wrap(db);
+               try {
+                       git.packRefs().setProgressMonitor(new TextProgressMonitor(errw))
+                                       .setAll(all).call();
+               } catch (GitAPIException e) {
+                       throw die(e.getMessage(), e);
+               }
+       }
+}
index c57295518d632a42463ae21dd13cc7ec001941dd..f84be21e82c7d3c4dc0df8889710b2eb3b706033 100644 (file)
@@ -19,7 +19,6 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 
 import java.io.File;
-import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.concurrent.BrokenBarrierException;
@@ -31,6 +30,8 @@ import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
 import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.PackRefsCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
 import org.eclipse.jgit.lib.ConfigConstants;
 import org.eclipse.jgit.lib.Constants;
@@ -49,7 +50,7 @@ public class GcPackRefsTest extends GcTestCase {
                RevBlob a = tr.blob("a");
                tr.lightweightTag("t", a);
 
-               gc.packRefs();
+               packRefs(false);
                assertSame(repo.exactRef("refs/tags/t").getStorage(), Storage.PACKED);
        }
 
@@ -60,7 +61,7 @@ public class GcPackRefsTest extends GcTestCase {
                String name = repo.findRef(ref).getName();
                Path dir = repo.getCommonDirectory().toPath().resolve(name).getParent();
                assertNotNull(dir);
-               gc.packRefs();
+               packRefs(true);
                assertFalse(Files.exists(dir));
        }
 
@@ -75,9 +76,9 @@ public class GcPackRefsTest extends GcTestCase {
                Callable<Integer> packRefs = () -> {
                        syncPoint.await();
                        try {
-                               gc.packRefs();
+                               packRefs(false);
                                return 0;
-                       } catch (IOException e) {
+                       } catch (GitAPIException e) {
                                return 1;
                        }
                };
@@ -102,7 +103,7 @@ public class GcPackRefsTest extends GcTestCase {
                                "refs/tags/t1"));
                try {
                        refLock.lock();
-                       gc.packRefs();
+                       packRefs(false);
                } finally {
                        refLock.unlock();
                }
@@ -145,7 +146,7 @@ public class GcPackRefsTest extends GcTestCase {
 
                        Future<Result> result2 = pool.submit(() -> {
                                refUpdateLockedRef.await();
-                               gc.packRefs();
+                               packRefs(false);
                                packRefsDone.await();
                                return null;
                        });
@@ -173,19 +174,20 @@ public class GcPackRefsTest extends GcTestCase {
                assertEquals(repo.exactRef("HEAD").getTarget().getName(),
                                "refs/heads/master");
                assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
-               gc.packRefs();
+               PackRefsCommand packRefsCommand = git.packRefs().setAll(true);
+               packRefsCommand.call();
                assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
                assertEquals(repo.exactRef("HEAD").getTarget().getName(),
                                "refs/heads/master");
                assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
 
                git.checkout().setName("refs/heads/side").call();
-               gc.packRefs();
+               packRefsCommand.call();
                assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
 
                // check for detached HEAD
                git.checkout().setName(first.getName()).call();
-               gc.packRefs();
+               packRefsCommand.call();
                assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
        }
 
@@ -208,7 +210,7 @@ public class GcPackRefsTest extends GcTestCase {
                assertEquals(repo.exactRef("HEAD").getTarget().getName(),
                                "refs/heads/master");
                assertNull(repo.exactRef("HEAD").getTarget().getObjectId());
-               gc.packRefs();
+               packRefs(true);
                assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
                assertEquals(repo.exactRef("HEAD").getTarget().getName(),
                                "refs/heads/master");
@@ -216,9 +218,14 @@ public class GcPackRefsTest extends GcTestCase {
 
                // check for non-detached HEAD
                repo.updateRef(Constants.HEAD).link("refs/heads/side");
-               gc.packRefs();
+               packRefs(true);
                assertSame(repo.exactRef("HEAD").getStorage(), Storage.LOOSE);
                assertEquals(repo.exactRef("HEAD").getTarget().getObjectId(),
                                second.getId());
        }
+
+       private void packRefs(boolean all) throws GitAPIException {
+               new PackRefsCommand(repo).setAll(all).call();
+       }
+
 }
index 024ca77f21a34253f1c2dabdff3a0faa06b6a784..acfe812a2008bc67a84d9d8b7e1fafb2275ce1ae 100644 (file)
@@ -597,6 +597,8 @@ packInaccessible=Failed to access pack file {0}, caught {1} consecutive errors w
 packingCancelledDuringObjectsWriting=Packing cancelled during objects writing
 packObjectCountMismatch=Pack object count mismatch: pack {0} index {1}: {2}
 packRefs=Pack refs
+packRefsFailed=Packing refs failed
+packRefsSuccessful=Packed refs successfully
 packSizeNotSetYet=Pack size not yet set since it has not yet been received
 packTooLargeForIndexVersion1=Pack too large for index version 1
 packWasDeleted=Pack file {0} was deleted, removing it from pack list
index 3dc53ec2486a6e2a7445ede26ace7d2afa7ccf80..5bc035a46af60ebf3b130b1e7874486bdfd71490 100644 (file)
@@ -713,6 +713,16 @@ public class Git implements AutoCloseable {
                return new GarbageCollectCommand(repo);
        }
 
+       /**
+        * Return a command object to execute a {@code PackRefs} command
+        *
+        * @return a {@link org.eclipse.jgit.api.PackRefsCommand}
+        * @since 7.1
+        */
+       public PackRefsCommand packRefs() {
+               return new PackRefsCommand(repo);
+       }
+
        /**
         * Return a command object to find human-readable names of revisions.
         *
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PackRefsCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PackRefsCommand.java
new file mode 100644 (file)
index 0000000..29a69c5
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License 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.api;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Optimize storage of references.
+ *
+ * @since 7.1
+ */
+public class PackRefsCommand extends GitCommand<String> {
+       private ProgressMonitor monitor;
+
+       private boolean all;
+
+       /**
+        * Creates a new {@link PackRefsCommand} instance with default values.
+        *
+        * @param repo
+        *              the repository this command will be used on
+        */
+       public PackRefsCommand(Repository repo) {
+               super(repo);
+               this.monitor = NullProgressMonitor.INSTANCE;
+       }
+
+       /**
+        * Set progress monitor
+        *
+        * @param monitor
+        *              a progress monitor
+        * @return this instance
+        */
+       public PackRefsCommand setProgressMonitor(ProgressMonitor monitor) {
+               this.monitor = monitor;
+               return this;
+       }
+
+       /**
+        * Specify whether to pack all the references.
+        *
+        * @param all
+        *              if <code>true</code> all the loose refs will be packed
+        * @return this instance
+        */
+       public PackRefsCommand setAll(boolean all) {
+               this.all = all;
+               return this;
+       }
+
+       /**
+        * Whether to pack all the references
+        *
+        * @return whether to pack all the references
+        */
+       public boolean isAll() {
+               return all;
+       }
+
+       @Override
+       public String call() throws GitAPIException {
+               checkCallable();
+               try {
+                       repo.getRefDatabase().packRefs(monitor, this);
+                       return JGitText.get().packRefsSuccessful;
+               } catch (IOException e) {
+                       throw new JGitInternalException(JGitText.get().packRefsFailed, e);
+               }
+       }
+}
index 0980219e25cbf65b05a24f7f708ab7c4830b03f7..2d9d2c527ce291342a0b23692017ad5758355b93 100644 (file)
@@ -627,6 +627,8 @@ public class JGitText extends TranslationBundle {
        /***/ public String packingCancelledDuringObjectsWriting;
        /***/ public String packObjectCountMismatch;
        /***/ public String packRefs;
+       /***/ public String packRefsFailed;
+       /***/ public String packRefsSuccessful;
        /***/ public String packSizeNotSetYet;
        /***/ public String packTooLargeForIndexVersion1;
        /***/ public String packWasDeleted;
index 80240e5062be9eee26d636b2c8da9f33157397c4..25b7583b95d502029f8f87b99f7813be4c213e14 100644 (file)
@@ -28,8 +28,10 @@ import java.util.concurrent.locks.ReentrantLock;
 import java.util.stream.Collectors;
 
 import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.PackRefsCommand;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.events.RefsChangedEvent;
+import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.internal.storage.reftable.MergedReftable;
 import org.eclipse.jgit.internal.storage.reftable.ReftableBatchRefUpdate;
 import org.eclipse.jgit.internal.storage.reftable.ReftableDatabase;
@@ -39,6 +41,7 @@ import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdRef;
 import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefDatabase;
 import org.eclipse.jgit.lib.RefRename;
@@ -107,6 +110,22 @@ public class FileReftableDatabase extends RefDatabase {
                return reftableDatabase.hasFastTipsWithSha1();
        }
 
+       /**
+        * {@inheritDoc}
+        *
+        * For Reftable, all the data is compacted into a single table.
+        */
+       @Override
+       public void packRefs(ProgressMonitor pm, PackRefsCommand packRefs)
+                       throws IOException {
+               pm.beginTask(JGitText.get().packRefs, 1);
+               try {
+                       compactFully();
+               } finally {
+                       pm.endTask();
+               }
+       }
+
        /**
         * Runs a full compaction for GC purposes.
         * @throws IOException on I/O errors
index 230dc6f45ac27fad0019b80e9ac53a857ca61e2c..84c85659ff74aea48c9a52cd61946b6a3cf615f8 100644 (file)
@@ -33,6 +33,7 @@ import java.util.Set;
 
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.attributes.AttributesNode;
 import org.eclipse.jgit.attributes.AttributesNodeProvider;
@@ -599,7 +600,7 @@ public class FileRepository extends Repository {
                gc.setBackground(shouldAutoDetach());
                try {
                        gc.gc();
-               } catch (ParseException | IOException e) {
+               } catch (ParseException | IOException | GitAPIException e) {
                        throw new JGitInternalException(JGitText.get().gcFailed, e);
                }
        }
index c9da4d8d6702ac5a8f65211b79939b48f1c76fac..7f3369364b8aa23bede4ceb462e20f35a58d663f 100644 (file)
@@ -63,6 +63,8 @@ import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.PackRefsCommand;
+import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.dircache.DirCacheIterator;
 import org.eclipse.jgit.errors.CancelledException;
 import org.eclipse.jgit.errors.CorruptObjectException;
@@ -233,9 +235,11 @@ public class GC {
         * @throws java.text.ParseException
         *             If the configuration parameter "gc.pruneexpire" couldn't be
         *             parsed
+        * @throws GitAPIException
+        *             If packing refs failed
         */
        public CompletableFuture<Collection<Pack>> gc()
-                       throws IOException, ParseException {
+                       throws IOException, ParseException, GitAPIException {
                if (!background) {
                        return CompletableFuture.completedFuture(doGc());
                }
@@ -254,7 +258,7 @@ public class GC {
                                        gcLog.commit();
                                }
                                return newPacks;
-                       } catch (IOException | ParseException e) {
+                       } catch (IOException | ParseException | GitAPIException e) {
                                try {
                                        gcLog.write(e.getMessage());
                                        StringWriter sw = new StringWriter();
@@ -277,7 +281,8 @@ public class GC {
                return (executor != null) ? executor : WorkQueue.getExecutor();
        }
 
-       private Collection<Pack> doGc() throws IOException, ParseException {
+       private Collection<Pack> doGc()
+                       throws IOException, ParseException, GitAPIException {
                if (automatic && !needGc()) {
                        return Collections.emptyList();
                }
@@ -286,7 +291,8 @@ public class GC {
                                return Collections.emptyList();
                        }
                        pm.start(6 /* tasks */);
-                       packRefs();
+                       new PackRefsCommand(repo).setProgressMonitor(pm).setAll(true)
+                                       .call();
                        // TODO: implement reflog_expire(pm, repo);
                        Collection<Pack> newPacks = repack();
                        prune(Collections.emptySet());
@@ -779,43 +785,6 @@ public class GC {
                                && Objects.equals(r1.getObjectId(), r2.getObjectId());
        }
 
-       /**
-        * Pack ref storage. For a RefDirectory database, this packs all
-        * non-symbolic, loose refs into packed-refs. For Reftable, all of the data
-        * is compacted into a single table.
-        *
-        * @throws java.io.IOException
-        *             if an IO error occurred
-        */
-       public void packRefs() throws IOException {
-               RefDatabase refDb = repo.getRefDatabase();
-               if (refDb instanceof FileReftableDatabase) {
-                       // TODO: abstract this more cleanly.
-                       pm.beginTask(JGitText.get().packRefs, 1);
-                       try {
-                               ((FileReftableDatabase) refDb).compactFully();
-                       } finally {
-                               pm.endTask();
-                       }
-                       return;
-               }
-
-               Collection<Ref> refs = refDb.getRefsByPrefix(Constants.R_REFS);
-               List<String> refsToBePacked = new ArrayList<>(refs.size());
-               pm.beginTask(JGitText.get().packRefs, refs.size());
-               try {
-                       for (Ref ref : refs) {
-                               checkCancelled();
-                               if (!ref.isSymbolic() && ref.getStorage().isLoose())
-                                       refsToBePacked.add(ref.getName());
-                               pm.update(1);
-                       }
-                       ((RefDirectory) repo.getRefDatabase()).pack(refsToBePacked);
-               } finally {
-                       pm.endTask();
-               }
-       }
-
        /**
         * Packs all objects which reachable from any of the heads into one pack
         * file. Additionally all objects which are not reachable from any head but
index 604868133e5e6425efab80339ec7a36c34ac7d5d..6aa1157e37762a0ab4790737320671e7649a706f 100644 (file)
@@ -57,6 +57,7 @@ import java.util.stream.Stream;
 
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.PackRefsCommand;
 import org.eclipse.jgit.errors.InvalidObjectIdException;
 import org.eclipse.jgit.errors.LockFailedException;
 import org.eclipse.jgit.errors.MissingObjectException;
@@ -69,6 +70,7 @@ import org.eclipse.jgit.lib.CoreConfig.TrustLooseRefStat;
 import org.eclipse.jgit.lib.CoreConfig.TrustPackedRefsStat;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdRef;
+import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.RefComparator;
 import org.eclipse.jgit.lib.RefDatabase;
@@ -287,6 +289,33 @@ public class RefDirectory extends RefDatabase {
                clearReferences();
        }
 
+       /**
+        * {@inheritDoc}
+        *
+        * For a RefDirectory database, by default this packs non-symbolic, loose
+        * tag refs into packed-refs. If {@code all} flag is set, this packs all the
+        * non-symbolic, loose refs.
+        */
+       @Override
+       public void packRefs(ProgressMonitor pm, PackRefsCommand packRefs)
+                       throws IOException {
+               String prefix = packRefs.isAll() ? R_REFS : R_TAGS;
+               Collection<Ref> refs = getRefsByPrefix(prefix);
+               List<String> refsToBePacked = new ArrayList<>(refs.size());
+               pm.beginTask(JGitText.get().packRefs, refs.size());
+               try {
+                       for (Ref ref : refs) {
+                               if (!ref.isSymbolic() && ref.getStorage().isLoose()) {
+                                       refsToBePacked.add(ref.getName());
+                               }
+                               pm.update(1);
+                       }
+                       pack(refsToBePacked);
+               } finally {
+                       pm.endTask();
+               }
+       }
+
        @Override
        public boolean isNameConflicting(String name) throws IOException {
                // Cannot be nested within an existing reference.
index 114246beb2094348366afdc6aa27d508e6d28420..09cb5a83dd7a702dc7b032927f99c0911608e6bc 100644 (file)
@@ -26,6 +26,7 @@ import java.util.stream.Stream;
 
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.PackRefsCommand;
 
 /**
  * Abstraction of name to {@link org.eclipse.jgit.lib.ObjectId} mapping.
@@ -593,4 +594,22 @@ public abstract class RefDatabase {
                }
                return null;
        }
+
+       /**
+        * Optimize pack ref storage.
+        *
+        * @param pm
+        *            a progress monitor
+        *
+        * @param packRefs
+        *            {@link PackRefsCommand} to control ref packing behavior
+        *
+        * @throws java.io.IOException
+        *             if an IO error occurred
+        * @since 7.1
+        */
+       public void packRefs(ProgressMonitor pm, PackRefsCommand packRefs)
+                       throws IOException {
+               // nothing
+       }
 }