2
This code is derived from jgit (http://eclipse.org/jgit).
3
Copyright owners are documented in jgit's IP log.
5
This program and the accompanying materials are made available
6
under the terms of the Eclipse Distribution License v1.0 which
7
accompanies this distribution, is reproduced below, and is
8
available at http://www.eclipse.org/org/documents/edl-v10.php
12
Redistribution and use in source and binary forms, with or
13
without modification, are permitted provided that the following
16
- Redistributions of source code must retain the above copyright
17
notice, this list of conditions and the following disclaimer.
19
- Redistributions in binary form must reproduce the above
20
copyright notice, this list of conditions and the following
21
disclaimer in the documentation and/or other materials provided
22
with the distribution.
24
- Neither the name of the Eclipse Foundation, Inc. nor the
25
names of its contributors may be used to endorse or promote
26
products derived from this software without specific prior
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45
using System.Collections.Generic;
52
using NGit.Storage.File;
53
using NGit.Storage.Pack;
55
using NGit.Treewalk.Filter;
59
namespace NGit.Storage.File
62
/// A garbage collector for git
63
/// <see cref="FileRepository">FileRepository</see>
64
/// . Instances of this class
65
/// are not thread-safe. Don't use the same instance from multiple threads.
66
/// This class started as a copy of DfsGarbageCollector from Shawn O. Pearce
67
/// adapted to FileRepositories.
71
private static readonly string PRUNE_EXPIRE_DEFAULT = "2.weeks.ago";
73
private readonly FileRepository repo;
75
private ProgressMonitor pm;
77
private long expireAgeMillis = -1;
79
private DateTime? expire;
82
/// the refs which existed during the last call to
83
/// <see cref="Repack()">Repack()</see>
86
/// <see cref="Prune(System.Collections.Generic.ICollection{E})">Prune(System.Collections.Generic.ICollection<E>)
88
/// where we can optimize by looking at the
89
/// difference between the current refs and the refs which existed during
91
/// <see cref="Repack()">Repack()</see>
94
private IDictionary<string, Ref> lastPackedRefs;
96
/// <summary>Holds the starting time of the last repack() execution.</summary>
98
/// Holds the starting time of the last repack() execution. This is needed in
99
/// prune() to inspect only those reflog entries which have been added since
102
private long lastRepackTime;
104
/// <summary>Creates a new garbage collector with default values.</summary>
106
/// Creates a new garbage collector with default values. An expirationTime of
107
/// two weeks and <code>null</code> as progress monitor will be used.
109
/// <param name="repo">the repo to work on</param>
110
public GC(FileRepository repo)
113
this.pm = NullProgressMonitor.INSTANCE;
117
/// Runs a garbage collector on a
118
/// <see cref="FileRepository">FileRepository</see>
121
/// <li>pack loose references into packed-refs</li>
122
/// <li>repack all reachable objects into new pack files and delete the old
124
/// <li>prune all loose objects which are now reachable by packs</li>
128
/// the collection of
129
/// <see cref="PackFile">PackFile</see>
130
/// 's which are newly created
132
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
133
/// <exception cref="Sharpen.ParseException">
134
/// If the configuration parameter "gc.pruneexpire" couldn't be
137
public virtual ICollection<PackFile> Gc()
141
// TODO: implement reflog_expire(pm, repo);
142
ICollection<PackFile> newPacks = Repack();
143
Prune(Sharpen.Collections.EmptySet<ObjectId>());
144
// TODO: implement rerere_gc(pm);
148
/// <summary>Delete old pack files.</summary>
150
/// Delete old pack files. What is 'old' is defined by specifying a set of
151
/// old pack files and a set of new pack files. Each pack file contained in
152
/// old pack files but not contained in new pack files will be deleted.
154
/// <param name="oldPacks"></param>
155
/// <param name="newPacks"></param>
156
/// <param name="ignoreErrors">
157
/// <code>true</code> if we should ignore the fact that a certain
158
/// pack files or index files couldn't be deleted.
159
/// <code>false</code> if an exception should be thrown in such
162
/// <exception cref="System.IO.IOException">
163
/// if a pack file couldn't be deleted and
164
/// <code>ignoreErrors</code> is set to <code>false</code>
166
private void DeleteOldPacks(ICollection<PackFile> oldPacks, ICollection<PackFile>
167
newPacks, bool ignoreErrors)
169
int deleteOptions = FileUtils.RETRY | FileUtils.SKIP_MISSING;
172
deleteOptions |= FileUtils.IGNORE_ERRORS;
174
foreach (PackFile oldPack in oldPacks)
176
bool retainPack = false;
177
string oldName = oldPack.GetPackName();
178
// check whether an old pack file is also among the list of new
179
// pack files. Then we must not delete it.
180
foreach (PackFile newPack in newPacks)
182
if (oldName.Equals(newPack.GetPackName()))
188
if (!retainPack && !oldPack.ShouldBeKept())
191
FileUtils.Delete(NameFor(oldName, ".pack"), deleteOptions);
192
FileUtils.Delete(NameFor(oldName, ".idx"), deleteOptions);
196
// close the complete object database. Thats my only chance to force
197
// rescanning and to detect that certain pack files are now deleted.
198
((ObjectDirectory)repo.ObjectDatabase).Close();
202
/// Like "git prune-packed" this method tries to prune all loose objects
203
/// which can be found in packs.
206
/// Like "git prune-packed" this method tries to prune all loose objects
207
/// which can be found in packs. If certain objects can't be pruned (e.g.
208
/// because the filesystem delete operation fails) this is silently ignored.
210
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
211
public virtual void PrunePacked()
213
ObjectDirectory objdb = ((ObjectDirectory)repo.ObjectDatabase);
214
ICollection<PackFile> packs = objdb.GetPacks();
215
FilePath objects = repo.ObjectsDirectory;
216
string[] fanout = objects.List();
217
if (fanout != null && fanout.Length > 0)
219
pm.BeginTask(JGitText.Get().pruneLoosePackedObjects, fanout.Length);
222
foreach (string d in fanout)
229
string[] entries = new FilePath(objects, d).List();
234
foreach (string e in entries)
236
if (e.Length != Constants.OBJECT_ID_STRING_LENGTH - 2)
243
id = ObjectId.FromString(d + e);
245
catch (ArgumentException)
247
// ignoring the file that does not represent loose
252
foreach (PackFile p in packs)
262
FileUtils.Delete(objdb.FileFor(id), FileUtils.RETRY | FileUtils.SKIP_MISSING | FileUtils
276
/// Like "git prune" this method tries to prune all loose objects which are
280
/// Like "git prune" this method tries to prune all loose objects which are
281
/// unreferenced. If certain objects can't be pruned (e.g. because the
282
/// filesystem delete operation fails) this is silently ignored.
284
/// <param name="objectsToKeep">a set of objects which should explicitly not be pruned
286
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
287
/// <exception cref="Sharpen.ParseException">
288
/// If the configuration parameter "gc.pruneexpire" couldn't be
291
public virtual void Prune(ICollection<ObjectId> objectsToKeep)
293
long expireDate = long.MaxValue;
294
if (expire == null && expireAgeMillis == -1)
296
string pruneExpireStr = ((FileBasedConfig)repo.GetConfig()).GetString(ConfigConstants
297
.CONFIG_GC_SECTION, null, ConfigConstants.CONFIG_KEY_PRUNEEXPIRE);
298
if (pruneExpireStr == null)
300
pruneExpireStr = PRUNE_EXPIRE_DEFAULT;
302
expire = GitDateParser.Parse(pruneExpireStr, null);
303
expireAgeMillis = -1;
307
expireDate = expire.Value.GetTime();
309
if (expireAgeMillis != -1)
311
expireDate = Runtime.CurrentTimeMillis() - expireAgeMillis;
313
// Collect all loose objects which are old enough, not referenced from
314
// the index and not in objectsToKeep
315
IDictionary<ObjectId, FilePath> deletionCandidates = new Dictionary<ObjectId, FilePath
317
ICollection<ObjectId> indexObjects = null;
318
FilePath objects = repo.ObjectsDirectory;
319
string[] fanout = objects.List();
320
if (fanout != null && fanout.Length > 0)
322
pm.BeginTask(JGitText.Get().pruneLooseUnreferencedObjects, fanout.Length);
325
foreach (string d in fanout)
332
FilePath[] entries = new FilePath(objects, d).ListFiles();
337
foreach (FilePath f in entries)
339
string fName = f.GetName();
340
if (fName.Length != Constants.OBJECT_ID_STRING_LENGTH - 2)
344
if (f.LastModified() >= expireDate)
350
ObjectId id = ObjectId.FromString(d + fName);
351
if (objectsToKeep.Contains(id))
355
if (indexObjects == null)
357
indexObjects = ListNonHEADIndexObjects();
359
if (indexObjects.Contains(id))
363
deletionCandidates.Put(id, f);
365
catch (ArgumentException)
367
// ignoring the file that does not represent loose
379
if (deletionCandidates.IsEmpty())
383
// From the set of current refs remove all those which have been handled
384
// during last repack(). Only those refs will survive which have been
385
// added or modified since the last repack. Only these can save existing
386
// loose refs from being pruned.
387
IDictionary<string, Ref> newRefs;
388
if (lastPackedRefs == null || lastPackedRefs.IsEmpty())
390
newRefs = GetAllRefs();
394
newRefs = new Dictionary<string, Ref>();
395
for (Iterator<KeyValuePair<string, Ref>> i = GetAllRefs().EntrySet().Iterator();
398
KeyValuePair<string, Ref> newEntry = i.Next();
399
Ref old = lastPackedRefs.Get(newEntry.Key);
400
if (!Equals(newEntry.Value, old))
402
newRefs.Put(newEntry.Key, newEntry.Value);
406
if (!newRefs.IsEmpty())
408
// There are new/modified refs! Check which loose objects are now
409
// referenced by these modified refs (or their reflogentries).
410
// Remove these loose objects
411
// from the deletionCandidates. When the last candidate is removed
412
// leave this method.
413
ObjectWalk w = new ObjectWalk(repo);
416
foreach (Ref cr in newRefs.Values)
418
w.MarkStart(w.ParseAny(cr.GetObjectId()));
420
if (lastPackedRefs != null)
422
foreach (Ref lpr in lastPackedRefs.Values)
424
w.MarkUninteresting(w.ParseAny(lpr.GetObjectId()));
427
RemoveReferenced(deletionCandidates, w);
434
if (deletionCandidates.IsEmpty())
438
// Since we have not left the method yet there are still
439
// deletionCandidates. Last chance for these objects not to be pruned is
440
// that they are referenced by reflog entries. Even refs which currently
441
// point to the same object as during last repack() may have
442
// additional reflog entries not handled during last repack()
443
ObjectWalk w_1 = new ObjectWalk(repo);
446
foreach (Ref ar in GetAllRefs().Values)
448
foreach (ObjectId id in ListRefLogObjects(ar, lastRepackTime))
450
w_1.MarkStart(w_1.ParseAny(id));
453
if (lastPackedRefs != null)
455
foreach (Ref lpr in lastPackedRefs.Values)
457
w_1.MarkUninteresting(w_1.ParseAny(lpr.GetObjectId()));
460
RemoveReferenced(deletionCandidates, w_1);
466
if (deletionCandidates.IsEmpty())
470
// delete all candidates which have survived: these are unreferenced
472
foreach (FilePath f_1 in deletionCandidates.Values)
476
((ObjectDirectory)repo.ObjectDatabase).Close();
480
/// Remove all entries from a map which key is the id of an object referenced
481
/// by the given ObjectWalk
483
/// <param name="id2File"></param>
484
/// <param name="w"></param>
485
/// <exception cref="NGit.Errors.MissingObjectException">NGit.Errors.MissingObjectException
487
/// <exception cref="NGit.Errors.IncorrectObjectTypeException">NGit.Errors.IncorrectObjectTypeException
489
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
490
private void RemoveReferenced(IDictionary<ObjectId, FilePath> id2File, ObjectWalk
493
RevObject ro = w.Next();
496
if (Sharpen.Collections.Remove(id2File, ro.Id) != null)
498
if (id2File.IsEmpty())
508
if (Sharpen.Collections.Remove(id2File, ro.Id) != null)
510
if (id2File.IsEmpty())
519
private static bool Equals(Ref r1, Ref r2)
521
if (r1 == null || r2 == null)
527
if (!r2.IsSymbolic())
531
return r1.GetTarget().GetName().Equals(r2.GetTarget().GetName());
539
return r1.GetObjectId().Equals(r2.GetObjectId());
543
/// <summary>Packs all non-symbolic, loose refs into packed-refs.</summary>
544
/// <remarks>Packs all non-symbolic, loose refs into packed-refs.</remarks>
545
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
546
public virtual void PackRefs()
548
ICollection<Ref> refs = repo.GetAllRefs().Values;
549
IList<string> refsToBePacked = new AList<string>(refs.Count);
550
pm.BeginTask(JGitText.Get().packRefs, refs.Count);
553
foreach (Ref @ref in refs)
555
if (!@ref.IsSymbolic() && @ref.GetStorage().IsLoose())
557
refsToBePacked.AddItem(@ref.GetName());
561
((RefDirectory)repo.RefDatabase).Pack(refsToBePacked);
570
/// Packs all objects which reachable from any of the heads into one pack
574
/// Packs all objects which reachable from any of the heads into one pack
575
/// file. Additionally all objects which are not reachable from any head but
576
/// which are reachable from any of the other refs (e.g. tags), special refs
577
/// (e.g. FETCH_HEAD) or index are packed into a separate pack file. Objects
578
/// included in pack files which have a .keep file associated are never
579
/// repacked. All old pack files which existed before are deleted.
581
/// <returns>a collection of the newly created pack files</returns>
582
/// <exception cref="System.IO.IOException">
583
/// when during reading of refs, index, packfiles, objects,
584
/// reflog-entries or during writing to the packfiles
585
/// <see cref="System.IO.IOException">System.IO.IOException</see>
588
public virtual ICollection<PackFile> Repack()
590
ICollection<PackFile> toBeDeleted = ((ObjectDirectory)repo.ObjectDatabase).GetPacks
592
long time = Runtime.CurrentTimeMillis();
593
IDictionary<string, Ref> refsBefore = GetAllRefs();
594
ICollection<ObjectId> allHeads = new HashSet<ObjectId>();
595
ICollection<ObjectId> nonHeads = new HashSet<ObjectId>();
596
ICollection<ObjectId> tagTargets = new HashSet<ObjectId>();
597
ICollection<ObjectId> indexObjects = ListNonHEADIndexObjects();
598
foreach (Ref @ref in refsBefore.Values)
600
Sharpen.Collections.AddAll(nonHeads, ListRefLogObjects(@ref, 0));
601
if (@ref.IsSymbolic() || @ref.GetObjectId() == null)
605
if (@ref.GetName().StartsWith(Constants.R_HEADS))
607
allHeads.AddItem(@ref.GetObjectId());
611
nonHeads.AddItem(@ref.GetObjectId());
613
if (@ref.GetPeeledObjectId() != null)
615
tagTargets.AddItem(@ref.GetPeeledObjectId());
618
IList<PackIndex> excluded = new List<PackIndex>();
619
foreach (PackFile f in ((ObjectDirectory)repo.ObjectDatabase).GetPacks())
621
if (f.ShouldBeKept())
623
excluded.AddItem(f.GetIndex());
626
Sharpen.Collections.AddAll(tagTargets, allHeads);
627
Sharpen.Collections.AddAll(nonHeads, indexObjects);
628
IList<PackFile> ret = new AList<PackFile>(2);
629
PackFile heads = null;
630
if (!allHeads.IsEmpty())
632
heads = WritePack(allHeads, Sharpen.Collections.EmptySet<ObjectId>(), tagTargets,
637
excluded.Add(0, heads.GetIndex());
640
if (!nonHeads.IsEmpty())
642
PackFile rest = WritePack(nonHeads, allHeads, tagTargets, excluded);
648
DeleteOldPacks(toBeDeleted, ret, true);
650
lastPackedRefs = refsBefore;
651
lastRepackTime = time;
655
/// <param name="ref">the ref which log should be inspected</param>
656
/// <param name="minTime">only reflog entries not older then this time are processed</param>
659
/// <see cref="NGit.ObjectId">NGit.ObjectId</see>
660
/// s contained in the reflog
662
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
663
private ICollection<ObjectId> ListRefLogObjects(Ref @ref, long minTime)
665
IList<ReflogEntry> rlEntries = repo.GetReflogReader(@ref.GetName()).GetReverseEntries
667
if (rlEntries == null || rlEntries.IsEmpty())
669
return Sharpen.Collections.EmptySet<ObjectId>();
671
ICollection<ObjectId> ret = new HashSet<ObjectId>();
672
foreach (ReflogEntry e in rlEntries)
674
if (e.GetWho().GetWhen().GetTime() < minTime)
678
ret.AddItem(e.GetNewId());
679
ObjectId oldId = e.GetOldId();
680
if (oldId != null && !ObjectId.ZeroId.Equals(oldId))
688
/// <summary>Returns a map of all refs and additional refs (e.g.</summary>
690
/// Returns a map of all refs and additional refs (e.g. FETCH_HEAD,
693
/// <returns>a map where names of refs point to ref objects</returns>
694
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
695
private IDictionary<string, Ref> GetAllRefs()
697
IDictionary<string, Ref> ret = repo.GetAllRefs();
698
foreach (Ref @ref in repo.RefDatabase.GetAdditionalRefs())
700
ret.Put(@ref.GetName(), @ref);
706
/// Return a list of those objects in the index which differ from whats in
709
/// <returns>a set of ObjectIds of changed objects in the index</returns>
710
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
711
/// <exception cref="NGit.Errors.CorruptObjectException">NGit.Errors.CorruptObjectException
713
/// <exception cref="NGit.Errors.NoWorkTreeException">NGit.Errors.NoWorkTreeException
715
private ICollection<ObjectId> ListNonHEADIndexObjects()
717
RevWalk revWalk = null;
720
if (repo.GetIndexFile() == null)
722
return Sharpen.Collections.EmptySet<ObjectId>();
725
catch (NoWorkTreeException)
727
return Sharpen.Collections.EmptySet<ObjectId>();
729
TreeWalk treeWalk = new TreeWalk(repo);
732
treeWalk.AddTree(new DirCacheIterator(repo.ReadDirCache()));
733
ObjectId headID = repo.Resolve(Constants.HEAD);
736
revWalk = new RevWalk(repo);
737
treeWalk.AddTree(revWalk.ParseTree(headID));
741
treeWalk.Filter = TreeFilter.ANY_DIFF;
742
treeWalk.Recursive = true;
743
ICollection<ObjectId> ret = new HashSet<ObjectId>();
744
while (treeWalk.Next())
746
ObjectId objectId = treeWalk.GetObjectId(0);
747
switch (treeWalk.GetRawMode(0) & FileMode.TYPE_MASK)
749
case FileMode.TYPE_MISSING:
750
case FileMode.TYPE_GITLINK:
753
goto case FileMode.TYPE_TREE;
756
case FileMode.TYPE_TREE:
757
case FileMode.TYPE_FILE:
758
case FileMode.TYPE_SYMLINK:
760
ret.AddItem(objectId);
767
throw new IOException(MessageFormat.Format(JGitText.Get().corruptObjectInvalidMode3
768
, string.Format("%o", Sharpen.Extensions.ValueOf(treeWalk.GetRawMode(0)), (objectId
769
== null) ? "null" : objectId.Name, treeWalk.PathString, repo.GetIndexFile())));
785
/// <exception cref="System.IO.IOException"></exception>
786
private PackFile WritePack<_T0, _T1>(ICollection<_T0> want, ICollection<_T1> have
787
, ICollection<ObjectId> tagTargets, IList<PackIndex> excludeObjects) where _T0:ObjectId
790
FilePath tmpPack = null;
791
FilePath tmpIdx = null;
792
PackWriter pw = new PackWriter(repo);
795
// prepare the PackWriter
796
pw.SetDeltaBaseAsOffset(true);
797
pw.SetReuseDeltaCommits(false);
798
if (tagTargets != null)
800
pw.SetTagTargets(tagTargets);
802
if (excludeObjects != null)
804
foreach (PackIndex idx in excludeObjects)
806
pw.ExcludeObjects(idx);
809
pw.PreparePack(pm, want, have);
810
if (pw.GetObjectCount() == 0)
814
// create temporary files
815
string id = pw.ComputeName().GetName();
816
FilePath packdir = new FilePath(repo.ObjectsDirectory, "pack");
817
tmpPack = FilePath.CreateTempFile("gc_", ".pack_tmp", packdir);
818
tmpIdx = new FilePath(packdir, Sharpen.Runtime.Substring(tmpPack.GetName(), 0, tmpPack
819
.GetName().LastIndexOf('.')) + ".idx_tmp");
820
if (!tmpIdx.CreateNewFile())
822
throw new IOException(MessageFormat.Format(JGitText.Get().cannotCreateIndexfile,
825
// write the packfile
826
FileChannel channel = new FileOutputStream(tmpPack).GetChannel();
827
OutputStream channelStream = Channels.NewOutputStream(channel);
830
pw.WritePack(pm, pm, channelStream);
835
channelStream.Close();
838
// write the packindex
839
FileChannel idxChannel = new FileOutputStream(tmpIdx).GetChannel();
840
OutputStream idxStream = Channels.NewOutputStream(idxChannel);
843
pw.WriteIndex(idxStream);
847
idxChannel.Force(true);
851
// rename the temporary files to real files
852
FilePath realPack = NameFor(id, ".pack");
853
tmpPack.SetReadOnly();
854
FilePath realIdx = NameFor(id, ".idx");
855
realIdx.SetReadOnly();
859
if (!tmpPack.RenameTo(realPack))
864
if (!tmpIdx.RenameTo(realIdx))
866
FilePath newIdx = new FilePath(realIdx.GetParentFile(), realIdx.GetName() + ".new"
868
if (!tmpIdx.RenameTo(newIdx))
872
throw new IOException(MessageFormat.Format(JGitText.Get().panicCantRenameIndexFile
878
if (delete && tmpPack.Exists())
882
if (delete && tmpIdx.Exists())
887
return ((ObjectDirectory)repo.ObjectDatabase).OpenPack(realPack, realIdx);
892
if (tmpPack != null && tmpPack.Exists())
896
if (tmpIdx != null && tmpIdx.Exists())
903
private FilePath NameFor(string name, string ext)
905
FilePath packdir = new FilePath(repo.ObjectsDirectory, "pack");
906
return new FilePath(packdir, "pack-" + name + ext);
910
/// A class holding statistical data for a FileRepository regarding how many
911
/// objects are stored as loose or packed objects
913
public class RepoStatistics
915
/// <summary>The number of objects stored in pack files.</summary>
917
/// The number of objects stored in pack files. If the same object is
918
/// stored in multiple pack files then it is counted as often as it
919
/// occurs in pack files.
921
public long numberOfPackedObjects;
923
/// <summary>The number of pack files</summary>
924
public long numberOfPackFiles;
926
/// <summary>The number of objects stored as loose objects.</summary>
927
/// <remarks>The number of objects stored as loose objects.</remarks>
928
public long numberOfLooseObjects;
930
/// <summary>The sum of the sizes of all files used to persist loose objects.</summary>
931
/// <remarks>The sum of the sizes of all files used to persist loose objects.</remarks>
932
public long sizeOfLooseObjects;
934
/// <summary>The sum of the sizes of all pack files.</summary>
935
/// <remarks>The sum of the sizes of all pack files.</remarks>
936
public long sizeOfPackedObjects;
938
/// <summary>The number of loose refs.</summary>
939
/// <remarks>The number of loose refs.</remarks>
940
public long numberOfLooseRefs;
942
/// <summary>The number of refs stored in pack files.</summary>
943
/// <remarks>The number of refs stored in pack files.</remarks>
944
public long numberOfPackedRefs;
946
internal RepoStatistics(GC _enclosing)
948
this._enclosing = _enclosing;
951
private readonly GC _enclosing;
954
/// <summary>Returns the number of objects stored in pack files.</summary>
956
/// Returns the number of objects stored in pack files. If an object is
957
/// contained in multiple pack files it is counted as often as it occurs.
959
/// <returns>the number of objects stored in pack files</returns>
960
/// <exception cref="System.IO.IOException">System.IO.IOException</exception>
961
public virtual GC.RepoStatistics GetStatistics()
963
GC.RepoStatistics ret = new GC.RepoStatistics(this);
964
ICollection<PackFile> packs = ((ObjectDirectory)repo.ObjectDatabase).GetPacks();
965
foreach (PackFile f in packs)
967
ret.numberOfPackedObjects += f.GetIndex().GetObjectCount();
968
ret.numberOfPackFiles++;
969
ret.sizeOfPackedObjects += f.GetPackFile().Length();
971
FilePath objDir = repo.ObjectsDirectory;
972
string[] fanout = objDir.List();
973
if (fanout != null && fanout.Length > 0)
975
foreach (string d in fanout)
981
FilePath[] entries = new FilePath(objDir, d).ListFiles();
986
foreach (FilePath f_1 in entries)
988
if (f_1.GetName().Length != Constants.OBJECT_ID_STRING_LENGTH - 2)
992
ret.numberOfLooseObjects++;
993
ret.sizeOfLooseObjects += f_1.Length();
997
RefDatabase refDb = repo.RefDatabase;
998
foreach (Ref r in refDb.GetRefs(RefDatabase.ALL).Values)
1000
RefStorage storage = r.GetStorage();
1001
if (storage == RefStorage.LOOSE || storage == RefStorage.LOOSE_PACKED)
1003
ret.numberOfLooseRefs++;
1005
if (storage == RefStorage.PACKED || storage == RefStorage.LOOSE_PACKED)
1007
ret.numberOfPackedRefs++;
1013
/// <summary>Set the progress monitor used for garbage collection methods.</summary>
1014
/// <remarks>Set the progress monitor used for garbage collection methods.</remarks>
1015
/// <param name="pm"></param>
1016
/// <returns>this</returns>
1017
public virtual GC SetProgressMonitor(ProgressMonitor pm)
1019
this.pm = (pm == null) ? NullProgressMonitor.INSTANCE : pm;
1024
/// During gc() or prune() each unreferenced, loose object which has been
1025
/// created or modified in the last <code>expireAgeMillis</code> milliseconds
1026
/// will not be pruned.
1029
/// During gc() or prune() each unreferenced, loose object which has been
1030
/// created or modified in the last <code>expireAgeMillis</code> milliseconds
1031
/// will not be pruned. Only older objects may be pruned. If set to 0 then
1032
/// every object is a candidate for pruning.
1034
/// <param name="expireAgeMillis">minimal age of objects to be pruned in milliseconds.
1036
public virtual void SetExpireAgeMillis(long expireAgeMillis)
1038
this.expireAgeMillis = expireAgeMillis;
1043
/// During gc() or prune() each unreferenced, loose object which has been
1044
/// created or modified after or at <code>expire</code> will not be pruned.
1047
/// During gc() or prune() each unreferenced, loose object which has been
1048
/// created or modified after or at <code>expire</code> will not be pruned.
1049
/// Only older objects may be pruned. If set to null then every object is a
1050
/// candidate for pruning.
1052
/// <param name="expire">
1053
/// instant in time which defines object expiration
1054
/// objects with modification time before this instant are expired
1055
/// objects with modification time newer or equal to this instant
1058
public virtual void SetExpire(DateTime expire)
1060
this.expire = expire;
1061
expireAgeMillis = -1;