1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.kuali.student.git.model;
16
17 import java.io.BufferedReader;
18 import java.io.ByteArrayInputStream;
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FileNotFoundException;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.io.OutputStream;
27 import java.io.PrintWriter;
28 import java.io.RandomAccessFile;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.LinkedList;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.TreeMap;
39
40 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
41 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
42 import org.apache.commons.io.FileUtils;
43 import org.apache.commons.io.IOUtils;
44 import org.apache.commons.io.output.ByteArrayOutputStream;
45 import org.apache.commons.lang3.StringUtils;
46 import org.eclipse.jgit.lib.Constants;
47 import org.eclipse.jgit.lib.ObjectId;
48 import org.eclipse.jgit.lib.Ref;
49 import org.eclipse.jgit.lib.Repository;
50 import org.kuali.student.git.model.branch.utils.GitBranchUtils;
51 import org.kuali.student.git.model.branch.utils.GitBranchUtils.ILargeBranchNameProvider;
52 import org.kuali.student.git.model.tree.utils.GitTreeDataUtils;
53 import org.kuali.student.git.model.tree.utils.GitTreeProcessor;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57
58
59
60
61
62 public class SvnRevisionMapper implements ILargeBranchNameProvider {
63
64 private static final Logger log = LoggerFactory
65 .getLogger(SvnRevisionMapper.class);
66
67 private static final String REVISION_MAP_FILE_NAME = "revisions.map";
68
69 private static final String REVISION_MAP_INDEX_FILE_NAME = "revisions.idx";
70
71 private static final String REVISION_BRANCH_MERGE_FILE_NAME = "merge.map";
72
73 private static final String REVISION_BRANCH_MERGE_INDEX_FILE_NAME = "merge.idx";
74
75 public static class SvnRevisionMap {
76 private long revision;
77 private String branchName;
78 private String branchPath;
79 private String commitId;
80
81
82
83
84
85 public SvnRevisionMap(long revision, String branchName,
86 String branchPath, String commitId) {
87 super();
88 this.revision = revision;
89 this.branchName = branchName;
90 this.branchPath = branchPath;
91 this.commitId = commitId;
92 }
93
94
95
96
97 public String getBranchPath() {
98 return branchPath;
99 }
100
101
102
103
104 public String getBranchName() {
105 return branchName;
106 }
107
108
109
110
111 public String getCommitId() {
112 return commitId;
113 }
114
115
116
117
118 public long getRevision() {
119 return revision;
120 }
121
122
123
124
125
126 public void setRevision(long revision) {
127 this.revision = revision;
128 }
129
130 }
131
132 private static class RevisionMapOffset {
133 private long revision;
134 private long startBtyeOffset;
135 private long totalBytes;
136
137
138
139
140
141
142 public RevisionMapOffset(long revision, long startBtyeOffset,
143 long totalBytes) {
144 super();
145 this.revision = revision;
146 this.startBtyeOffset = startBtyeOffset;
147 this.totalBytes = totalBytes;
148 }
149
150
151
152
153 public long getRevision() {
154 return revision;
155 }
156
157
158
159
160 public long getStartBtyeOffset() {
161 return startBtyeOffset;
162 }
163
164
165
166
167 public long getTotalBytes() {
168 return totalBytes;
169 }
170
171 }
172
173 private File revisonMappings;
174
175 private TreeMap<String, RevisionMapOffset> revisionMap = new TreeMap<>();
176
177 private File revisionMapDataFile;
178
179 private File revisionMapIndexFile;
180
181 private PrintWriter revisionMapIndexWriter;
182
183 private RandomAccessFile revisionMapDataRandomAccessFile;
184
185 private long endOfRevisionMapDataFileInBytes;
186
187 private File revisionBranchMergeDataFile;
188 private File revisionBranchMergeIndexFile;
189
190 private PrintWriter revisionBranchMergeIndexWriter;
191
192 private RandomAccessFile revisionBranchMergeDataRandomAccessFile;
193
194 private long endOfRevisionBranchMergeDataFileInBytes;
195
196 private TreeMap<String, Map<String, RevisionMapOffset>> revisionMergeMap = new TreeMap<>();
197
198 private GitTreeProcessor treeProcessor;
199
200 private static Comparator<? super String> STRING_LONG_VALUE_COMPARATOR = new Comparator<String>() {
201
202 @Override
203 public int compare(String o1, String o2) {
204 Long l1 = Long.valueOf(o1);
205 Long l2 = Long.valueOf(o2);
206
207 return l1.compareTo(l2);
208 }
209
210 };
211
212 private class MergeDataOffsetProvider implements RevisionMapOffsetProvider {
213
214 private long revision;
215 private String targetBranch;
216
217
218
219
220 public MergeDataOffsetProvider(long revision, String targetBranch) {
221 super();
222 this.revision = revision;
223 this.targetBranch = targetBranch;
224 }
225
226 @Override
227 public RevisionMapOffset getRevisionMapOffset() {
228
229 Map<String, RevisionMapOffset> map = revisionMergeMap.get(String
230 .valueOf(revision));
231
232 if (map == null)
233 return null;
234
235 return map.get(targetBranch);
236
237 }
238
239 }
240
241
242
243
244 public SvnRevisionMapper(Repository repo) {
245
246 treeProcessor = new GitTreeProcessor(repo);
247
248 revisonMappings = new File(repo.getDirectory(), "jsvn");
249
250 revisonMappings.mkdirs();
251
252 revisionMapDataFile = new File(revisonMappings, REVISION_MAP_FILE_NAME);
253
254 revisionMapIndexFile = new File(revisonMappings,
255 REVISION_MAP_INDEX_FILE_NAME);
256
257 revisionBranchMergeDataFile = new File(revisonMappings,
258 REVISION_BRANCH_MERGE_FILE_NAME);
259
260 revisionBranchMergeIndexFile = new File(revisonMappings,
261 REVISION_BRANCH_MERGE_INDEX_FILE_NAME);
262
263 }
264
265 public void initialize() throws IOException {
266
267
268 revisionMapDataRandomAccessFile = new RandomAccessFile(
269 revisionMapDataFile, "rw");
270
271 if (revisionMapIndexFile.exists()) {
272
273 loadRevisionMapIndexData();
274 }
275
276 endOfRevisionMapDataFileInBytes = revisionMapDataFile.length();
277
278 revisionMapIndexWriter = new PrintWriter(new FileOutputStream(
279 revisionMapIndexFile, true));
280
281
282
283 revisionBranchMergeDataRandomAccessFile = new RandomAccessFile(
284 revisionBranchMergeDataFile, "rwd");
285
286 if (revisionBranchMergeIndexFile.exists()) {
287
288 loadRevisionMergeIndexData();
289 }
290
291 endOfRevisionBranchMergeDataFileInBytes = revisionBranchMergeDataFile
292 .length();
293
294 revisionBranchMergeIndexWriter = new PrintWriter(new FileOutputStream(
295 revisionBranchMergeIndexFile, true));
296
297 }
298
299 public void shutdown() throws IOException {
300
301 revisionMapIndexWriter.flush();
302 revisionMapIndexWriter.close();
303
304 revisionMapDataRandomAccessFile.close();
305
306 revisionBranchMergeIndexWriter.flush();
307 revisionBranchMergeIndexWriter.close();
308
309 revisionBranchMergeDataRandomAccessFile.close();
310
311 }
312
313 private void loadRevisionMergeIndexData() throws IOException {
314
315 BufferedReader indexReader = new BufferedReader(new InputStreamReader(
316 new FileInputStream(revisionBranchMergeIndexFile)));
317
318 while (true) {
319
320 String line = indexReader.readLine();
321
322 if (line == null)
323 break;
324
325 String parts[] = line.split("::");
326
327 if (parts.length != 3)
328 continue;
329
330 long revision = Long.parseLong(parts[0]);
331 String targetBranch = parts[1];
332 long byteStartOffset = Long.parseLong(parts[2]);
333 long totalbytes = Long.parseLong(parts[3]);
334
335 Map<String, RevisionMapOffset> targetBranchOffsetMap = getRevisionMergeDataByTargetBranch(
336 parts[0], true);
337
338 targetBranchOffsetMap.put(targetBranch, new RevisionMapOffset(
339 revision, byteStartOffset, totalbytes));
340
341 }
342
343 indexReader.close();
344 }
345
346 private Map<String, RevisionMapOffset> getRevisionMergeDataByTargetBranch(
347 String revisionString, boolean createIfDoesNotExist) {
348
349 Map<String, RevisionMapOffset> targetBranchOffsetMap = revisionMergeMap
350 .get(revisionString);
351
352 if (targetBranchOffsetMap == null && createIfDoesNotExist) {
353 targetBranchOffsetMap = new HashMap<String, SvnRevisionMapper.RevisionMapOffset>();
354 revisionMergeMap.put(revisionString, targetBranchOffsetMap);
355 }
356
357 return targetBranchOffsetMap;
358 }
359
360 private void loadRevisionMapIndexData() throws IOException {
361
362 BufferedReader indexReader = new BufferedReader(new InputStreamReader(
363 new FileInputStream(revisionMapIndexFile)));
364
365 while (true) {
366
367 String line = indexReader.readLine();
368
369 if (line == null)
370 break;
371
372 String parts[] = line.split("::");
373
374 if (parts.length != 3)
375 continue;
376
377 long revision = Long.parseLong(parts[0]);
378 long byteStartOffset = Long.parseLong(parts[1]);
379 long totalbytes = Long.parseLong(parts[2]);
380
381 revisionMap.put(parts[0], new RevisionMapOffset(revision,
382 byteStartOffset, totalbytes));
383
384 }
385
386 indexReader.close();
387
388 }
389
390
391
392
393 private long createRevisionEntry(RandomAccessFile dataFile,
394 long endOfDataFileOffset, long revision, List<String> revisionLines)
395 throws IOException {
396
397 OutputStream revisionMappingStream = null;
398
399 ByteArrayOutputStream bytesOut;
400
401 revisionMappingStream = new BZip2CompressorOutputStream(
402 bytesOut = new ByteArrayOutputStream());
403
404 PrintWriter pw = new PrintWriter(revisionMappingStream);
405
406 IOUtils.writeLines(revisionLines, "\n", pw);
407
408 pw.flush();
409
410 pw.close();
411
412 byte[] data = bytesOut.toByteArray();
413
414 dataFile.seek(endOfDataFileOffset);
415
416 dataFile.write(data);
417
418 return data.length;
419 }
420
421 private void createRevisionMapEntry(long revision,
422 List<String> branchHeadLines) throws IOException {
423
424 long bytesWritten = createRevisionEntry(
425 revisionMapDataRandomAccessFile,
426 endOfRevisionMapDataFileInBytes, revision, branchHeadLines);
427
428
429
430
431
432 updateRevisionMapIndex(revision, endOfRevisionMapDataFileInBytes,
433 bytesWritten);
434
435 endOfRevisionMapDataFileInBytes += bytesWritten;
436
437 }
438
439 public void createRevisionMap(long revision, List<Ref> branchHeads)
440 throws IOException {
441
442 List<String> branchHeadLines = new ArrayList<>(branchHeads.size());
443
444 for (Ref branchHead : branchHeads) {
445
446
447
448 if (!branchHead.getName().contains("@"))
449 branchHeadLines.add(revision + "::" + branchHead.getName()
450 + "::" + branchHead.getObjectId().name());
451 }
452
453 createRevisionMapEntry(revision, branchHeadLines);
454
455 }
456
457 private void updateRevisionMapIndex(long revision,
458 long revisionStartByteIndex, long bytesWritten) {
459
460 revisionMap.put(String.valueOf(revision), new RevisionMapOffset(
461 revision, revisionStartByteIndex, bytesWritten));
462
463 revisionMapIndexWriter.println(revision + "::" + revisionStartByteIndex
464 + "::" + bytesWritten);
465
466 revisionMapIndexWriter.flush();
467 }
468
469 private void updateMergeDataIndex(long revision, String targetBranchName,
470 List<BranchMergeInfo> mergeInfo, long revisionStartByteIndex,
471 long bytesWritten) {
472
473 String revisionString = String.valueOf(revision);
474
475 Map<String, RevisionMapOffset> targetRevisionMap = getRevisionMergeDataByTargetBranch(
476 revisionString, true);
477
478 targetRevisionMap.put(targetBranchName, new RevisionMapOffset(revision,
479 revisionStartByteIndex, bytesWritten));
480
481 revisionBranchMergeIndexWriter.println(revision + "::"
482 + targetBranchName + "::" + revisionStartByteIndex + "::"
483 + bytesWritten);
484
485 revisionBranchMergeIndexWriter.flush();
486 }
487
488 private void updateIndex(Map<String, RevisionMapOffset> revisionMap,
489 PrintWriter indexWriter, long revision,
490 long revisionStartByteIndex, long bytesWritten) {
491 revisionMap.put(String.valueOf(revision), new RevisionMapOffset(
492 revision, revisionStartByteIndex, bytesWritten));
493
494 indexWriter.println(revision + "::" + revisionStartByteIndex + "::"
495 + bytesWritten);
496
497 indexWriter.flush();
498
499 }
500
501
502
503
504
505
506
507
508 public List<SvnRevisionMap> getRevisionHeads(long revision)
509 throws IOException {
510
511 InputStream inputStream = getRevisionInputStream(revision);
512
513 if (inputStream == null)
514 return null;
515
516 List<String> lines = IOUtils.readLines(inputStream, "UTF-8");
517
518 inputStream.close();
519
520 String revisionString = String.valueOf(revision);
521
522 List<SvnRevisionMap> revisionHeads = new ArrayList<SvnRevisionMap>();
523
524 for (String line : lines) {
525
526 String[] parts = line.split("::");
527
528 if (!parts[0].equals(revisionString)) {
529 log.warn(parts[0] + " is not a line for " + revisionString);
530 continue;
531 }
532
533 String branchName = parts[1];
534
535 String commitId = parts[2];
536
537 String branchPath = GitBranchUtils.getBranchPath(branchName,
538 revision, this);
539
540 revisionHeads.add(new SvnRevisionMap(revision, branchName,
541 branchPath, commitId));
542
543 }
544
545 return revisionHeads;
546
547 }
548
549 private InputStream getRevisionInputStream(final long revision)
550 throws IOException {
551
552 return getInputStream(new RevisionMapOffsetProvider() {
553
554 @Override
555 public RevisionMapOffset getRevisionMapOffset() {
556
557 return revisionMap.get(String.valueOf(revision));
558 }
559 }, revisionMapDataRandomAccessFile);
560
561 }
562
563 private InputStream getMergeDataInputStream(final long revision,
564 final String targetBranch) throws IOException {
565 return getInputStream(new MergeDataOffsetProvider(revision,
566 targetBranch), revisionBranchMergeDataRandomAccessFile);
567 }
568
569 private static interface RevisionMapOffsetProvider {
570 public RevisionMapOffset getRevisionMapOffset();
571 };
572
573 private InputStream getInputStream(
574 RevisionMapOffsetProvider offsetProvider, RandomAccessFile dataFile)
575 throws IOException {
576
577 RevisionMapOffset revisionOffset = offsetProvider
578 .getRevisionMapOffset();
579
580 if (revisionOffset == null)
581 return null;
582
583 byte[] data = new byte[(int) revisionOffset.getTotalBytes()];
584
585 dataFile.seek(revisionOffset.getStartBtyeOffset());
586
587 dataFile.readFully(data);
588
589 return new BZip2CompressorInputStream(new ByteArrayInputStream(data));
590
591 }
592
593
594
595
596
597
598
599
600
601
602 public ObjectId getRevisionBranchHead(long revision, String branchName)
603 throws IOException {
604
605 InputStream inputStream = getRevisionInputStream(revision);
606
607 if (inputStream == null)
608 return null;
609
610 List<String> lines = IOUtils.readLines(inputStream, "UTF-8");
611
612 inputStream.close();
613
614 String revisionString = String.valueOf(revision);
615
616 String adjustedBranchName = branchName;
617
618 if (!adjustedBranchName.startsWith(Constants.R_HEADS))
619 adjustedBranchName = Constants.R_HEADS + branchName;
620
621 for (String line : lines) {
622
623 String[] parts = line.split("::");
624
625 if (!parts[0].equals(revisionString)) {
626 log.warn("incorrect version");
627 continue;
628 }
629
630 if (parts[1].equals(adjustedBranchName)) {
631 ObjectId id = ObjectId.fromString(parts[2]);
632
633 return id;
634
635 }
636
637 }
638
639
640
641
642 return null;
643 }
644
645
646
647
648
649 public static class SvnRevisionMapResults {
650
651 private String copyFromPath;
652
653 private final SvnRevisionMap revMap;
654
655 private final String subPath;
656
657 public SvnRevisionMapResults(SvnRevisionMap revMap,
658 String copyFromPath, String subPath) {
659 this.revMap = revMap;
660 this.copyFromPath = copyFromPath;
661 this.subPath = subPath;
662 }
663
664 public SvnRevisionMapResults(SvnRevisionMap revMap, String copyFromPath) {
665 this(revMap, copyFromPath, "");
666 }
667
668
669
670
671 public SvnRevisionMap getRevMap() {
672 return revMap;
673 }
674
675
676
677
678 public String getSubPath() {
679 return subPath;
680 }
681
682
683
684
685 public String getCopyFromPath() {
686 return copyFromPath;
687 }
688
689
690
691
692
693 public void setCopyFromPath(String copyFromPath) {
694 this.copyFromPath = copyFromPath;
695 }
696
697 }
698
699 public List<SvnRevisionMapResults> getRevisionBranches(long targetRevision,
700 String targetPath) throws IOException {
701
702 ArrayList<SvnRevisionMapResults> branches = new ArrayList<>();
703
704 List<SvnRevisionMap> heads = this.getRevisionHeads(targetRevision);
705
706 if (heads == null)
707 return branches;
708
709 for (SvnRevisionMap revMap : heads) {
710
711 SvnRevisionMapResults results = findResults(revMap, targetPath);
712
713 if (results != null)
714 branches.add(results);
715
716 }
717
718 return branches;
719 }
720
721 private SvnRevisionMapResults findResults(SvnRevisionMap revMap,
722 String copyFromPath) {
723
724
725
726
727
728
729
730
731
732
733 String candidateBranchPath = revMap.getBranchPath().substring(
734 Constants.R_HEADS.length());
735
736 String candidateBranchParts[] = candidateBranchPath.split("\\/");
737
738 String copyFromPathParts[] = copyFromPath.split("\\/");
739
740 int smallestLength = Math.min(candidateBranchParts.length,
741 copyFromPathParts.length);
742
743 boolean allEquals = true;
744
745 for (int i = 0; i < smallestLength; i++) {
746
747 String candidatePart = candidateBranchParts[i];
748 String copyFromPart = copyFromPathParts[i];
749
750 if (!copyFromPart.equals(candidatePart)) {
751 allEquals = false;
752 break;
753 }
754
755 }
756
757 if (allEquals) {
758
759 if (copyFromPathParts.length > smallestLength) {
760
761 ObjectId commitId = ObjectId.fromString(revMap.getCommitId());
762
763 String insidePath = StringUtils.join(copyFromPathParts, "/",
764 smallestLength, copyFromPathParts.length);
765
766 try {
767 if (treeProcessor.treeContainsPath(commitId, insidePath)) {
768 return new SvnRevisionMapResults(revMap, copyFromPath,
769 insidePath);
770 }
771
772 } catch (Exception e) {
773 log.error("Failed to find paths for commit {}", commitId);
774
775 }
776 } else {
777 return new SvnRevisionMapResults(revMap, copyFromPath);
778 }
779 }
780
781 return null;
782 }
783
784
785
786
787
788
789
790 @Override
791 public String getBranchName(String longBranchId, long revision) {
792
793 try {
794 File revisionFile = new File(revisonMappings, "r" + revision
795 + "-large-branches");
796
797 List<String> lines = FileUtils.readLines(revisionFile, "UTF-8");
798
799 for (String line : lines) {
800
801 String[] parts = line.split("::");
802
803 if (parts.length != 2) {
804 continue;
805 }
806
807 if (parts[0].equals(longBranchId)) {
808 return parts[1].trim();
809 }
810
811 }
812
813
814 return null;
815 } catch (IOException e) {
816 log.debug("failed to find longbranch for id = {}", longBranchId);
817 return null;
818 }
819
820 }
821
822
823
824
825
826
827
828 @Override
829 public String storeLargeBranchName(String branchName, long revision) {
830
831 try {
832 ObjectId largeBranchNameId = GitBranchUtils
833 .getBranchNameObjectId(branchName);
834
835 String existingBranchName = getBranchName(largeBranchNameId.name(),
836 revision);
837
838 if (existingBranchName != null)
839 return largeBranchNameId.getName();
840
841 File revisionFile = new File(revisonMappings, "r" + revision
842 + "-large-branches");
843
844 PrintWriter pw = new PrintWriter(new FileOutputStream(revisionFile,
845 true));
846
847 pw.println(largeBranchNameId.name() + "::" + branchName);
848
849 pw.flush();
850 pw.close();
851
852 return largeBranchNameId.name();
853 } catch (FileNotFoundException e) {
854 log.warn("storeLargeBranchName: failed to open r" + revision
855 + "-large-branches");
856 return null;
857 }
858 }
859
860 public void repackMapFile() throws IOException {
861
862
863 revisionMapDataRandomAccessFile.close();
864
865
866 revisionMapIndexWriter.close();
867
868 revisionMapIndexFile.delete();
869
870 endOfRevisionMapDataFileInBytes = 0L;
871
872 revisionMapIndexWriter = new PrintWriter(new FileOutputStream(new File(
873 revisonMappings, REVISION_MAP_INDEX_FILE_NAME), true));
874
875
876 revisionMap.clear();
877
878 File copy = new File(revisonMappings, "repack-source.dat");
879
880 FileUtils.copyFile(revisionMapDataFile, copy);
881
882 revisionMapDataFile.delete();
883
884 revisionMapDataRandomAccessFile = new RandomAccessFile(
885 revisionMapDataFile, "rwd");
886
887 BufferedReader reader = new BufferedReader(
888 new InputStreamReader(new BZip2CompressorInputStream(
889 new FileInputStream(copy), true)));
890
891 String currentRevision = null;
892
893 List<String> currentRevisionHeads = new ArrayList<String>();
894
895 while (true) {
896
897 String line = reader.readLine();
898
899 if (line == null) {
900 if (currentRevision != null) {
901
902 createRevisionMapEntry(Long.parseLong(currentRevision),
903 currentRevisionHeads);
904
905 }
906 break;
907 }
908
909 String parts[] = line.split("::");
910
911 String revisionString = parts[0];
912
913 if (currentRevision == null)
914 currentRevision = revisionString;
915
916 if (!currentRevision.equals(revisionString)) {
917
918
919 createRevisionMapEntry(Long.parseLong(currentRevision),
920 currentRevisionHeads);
921
922 currentRevision = revisionString;
923
924 currentRevisionHeads.clear();
925
926 }
927
928 currentRevisionHeads.add(line);
929
930 }
931
932 reader.close();
933
934 copy.delete();
935
936 }
937
938 public void createMergeData(long revision, String targetBranch,
939 List<BranchMergeInfo> mergeInfo) throws IOException {
940
941 List<String> dataLines = new LinkedList<>();
942
943
944
945
946 for (BranchMergeInfo bmi : mergeInfo) {
947
948 List<String> lineParts = new LinkedList<>();
949
950 lineParts.add(String.valueOf(revision));
951
952 lineParts.add(targetBranch);
953
954 lineParts.add(bmi.getBranchName());
955
956 lineParts.add(StringUtils.join(bmi.getMergedRevisions().iterator(),
957 ","));
958
959 dataLines.add(StringUtils.join(lineParts, "::"));
960
961 }
962
963 long bytesWritten = createRevisionEntry(
964 revisionBranchMergeDataRandomAccessFile,
965 endOfRevisionBranchMergeDataFileInBytes, revision, dataLines);
966
967
968
969
970
971 updateMergeDataIndex(revision, targetBranch, mergeInfo,
972 endOfRevisionBranchMergeDataFileInBytes, bytesWritten);
973
974 endOfRevisionBranchMergeDataFileInBytes += bytesWritten;
975
976 }
977
978 private BranchMergeInfo extractBranchMergeInfoFromLine(String branchName,
979 String revisionParts[]) {
980
981 BranchMergeInfo bmi = new BranchMergeInfo(branchName);
982
983 for (String revisionString : revisionParts) {
984
985 bmi.addMergeRevision(Long.valueOf(revisionString));
986 }
987
988 return bmi;
989
990 }
991
992
993
994
995
996
997
998
999
1000
1001 public List<BranchMergeInfo> getMergeBranches(long revision,
1002 String targetBranch) throws IOException {
1003
1004 List<BranchMergeInfo> bmiList = new LinkedList<>();
1005
1006 InputStream inputStream = getMergeDataInputStream(revision,
1007 targetBranch);
1008
1009 if (inputStream == null)
1010 return null;
1011
1012 List<String> lines = IOUtils.readLines(inputStream, "UTF-8");
1013
1014 inputStream.close();
1015
1016 String revisionString = String.valueOf(revision);
1017
1018 for (String line : lines) {
1019
1020 String[] parts = line.split("::");
1021
1022 if (!parts[0].equals(revisionString)) {
1023 log.warn(parts[0] + " is not a line for " + revisionString);
1024 continue;
1025 }
1026
1027 String targetBranchName = parts[1];
1028
1029 if (targetBranch.equals(targetBranchName)) {
1030
1031 String mergeBranchName = parts[2];
1032
1033 String mergedRevisionStrings[] = parts[3].split(",");
1034
1035 BranchMergeInfo bmi = extractBranchMergeInfoFromLine(
1036 mergeBranchName, mergedRevisionStrings);
1037
1038 bmiList.add(bmi);
1039
1040 } else {
1041 log.warn(
1042 line
1043 + " is not a valid line for revision {} and target branch {}",
1044 revision, targetBranch);
1045 }
1046
1047 }
1048
1049 return bmiList;
1050
1051 }
1052
1053 public Set<Long> getMergeBranchRevisions(long revision,
1054 String targetBranch, String mergeBranch) throws IOException {
1055
1056 List<BranchMergeInfo> bmiList = getMergeBranches(revision, targetBranch);
1057
1058 for (BranchMergeInfo bmi : bmiList) {
1059
1060 if (bmi.getBranchName().equals(mergeBranch)) {
1061 return bmi.getMergedRevisions();
1062 }
1063 }
1064
1065
1066 return new HashSet<>();
1067
1068 }
1069
1070 public void truncateTo(long longRevision) throws IOException {
1071
1072 Map<String, RevisionMapOffset> branchOffsets = revisionMergeMap
1073 .get(longRevision);
1074
1075 long maxEndOfFile = endOfRevisionBranchMergeDataFileInBytes;
1076
1077 if (branchOffsets != null) {
1078 for (RevisionMapOffset candidateOffset : branchOffsets.values()) {
1079
1080 long candidateEndOfFile = candidateOffset.getStartBtyeOffset()
1081 + candidateOffset.getTotalBytes();
1082
1083 if (candidateEndOfFile > maxEndOfFile)
1084 maxEndOfFile = candidateEndOfFile;
1085 }
1086 }
1087
1088 RevisionMapOffset revisionMapOffset = revisionMap.get(String
1089 .valueOf(longRevision));
1090
1091 long revMapEndOfFile = revisionMapOffset.getStartBtyeOffset()
1092 + revisionMapOffset.getTotalBytes();
1093
1094
1095
1096 endOfRevisionBranchMergeDataFileInBytes = maxEndOfFile;
1097 endOfRevisionMapDataFileInBytes = revMapEndOfFile;
1098
1099 revisionBranchMergeDataRandomAccessFile
1100 .setLength(endOfRevisionBranchMergeDataFileInBytes);
1101
1102 revisionMapDataRandomAccessFile
1103 .setLength(endOfRevisionMapDataFileInBytes);
1104
1105
1106
1107 List<String> revisions = new ArrayList<>();
1108
1109 revisions.addAll(this.revisionMergeMap.keySet());
1110
1111 Collections.sort(revisions, STRING_LONG_VALUE_COMPARATOR);
1112
1113 int targetRevisionIndex = revisions.indexOf(String
1114 .valueOf(longRevision));
1115
1116 Set<String> keysToRemove = new HashSet<>(revisions.subList(
1117 targetRevisionIndex + 1, revisions.size()));
1118
1119 for (String key : keysToRemove) {
1120
1121 this.revisionMergeMap.remove(key);
1122
1123 }
1124
1125 revisions = new ArrayList<>(this.revisionMap.keySet());
1126
1127 Collections.sort(revisions, STRING_LONG_VALUE_COMPARATOR);
1128
1129 targetRevisionIndex = revisions.indexOf(String.valueOf(longRevision));
1130
1131 keysToRemove = new HashSet<>(revisions.subList(targetRevisionIndex + 1,
1132 revisions.size()));
1133
1134 for (String key : keysToRemove) {
1135
1136 this.revisionMap.remove(key);
1137
1138 }
1139
1140 }
1141
1142 }