comparison src/org/tmatesoft/hg/repo/HgBundle.java @ 74:6f1b88693d48

Complete refactoring to org.tmatesoft
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Mon, 24 Jan 2011 03:14:45 +0100
parents src/com/tmate/hgkit/ll/HgBundle.java@b01500fe2604
children c677e1593919
comparison
equal deleted inserted replaced
73:0d279bcc4442 74:6f1b88693d48
1 /*
2 * Copyright (c) 2011 TMate Software Ltd
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * For information on how to redistribute this software under
14 * the terms of a license other than GNU General Public License
15 * contact TMate Software at support@svnkit.com
16 */
17 package org.tmatesoft.hg.repo;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.util.LinkedList;
22 import java.util.List;
23
24 import org.tmatesoft.hg.core.Nodeid;
25 import org.tmatesoft.hg.internal.DataAccess;
26 import org.tmatesoft.hg.internal.DataAccessProvider;
27 import org.tmatesoft.hg.internal.DigestHelper;
28
29
30 /**
31 * @see http://mercurial.selenic.com/wiki/BundleFormat
32 *
33 * @author Artem Tikhomirov
34 * @author TMate Software Ltd.
35 */
36 public class HgBundle {
37
38 private final File bundleFile;
39 private final DataAccessProvider accessProvider;
40
41 public HgBundle(DataAccessProvider dap, File bundle) {
42 accessProvider = dap;
43 bundleFile = bundle;
44 }
45
46 public void changes(HgRepository hgRepo) throws IOException {
47 DataAccess da = accessProvider.create(bundleFile);
48 DigestHelper dh = new DigestHelper();
49 try {
50 List<GroupElement> changelogGroup = readGroup(da);
51 if (changelogGroup.isEmpty()) {
52 throw new IllegalStateException("No changelog group in the bundle"); // XXX perhaps, just be silent and/or log?
53 }
54 // XXX in fact, bundle not necessarily starts with the first revision missing in hgRepo
55 // need to 'scroll' till the last one common.
56 final Nodeid base = changelogGroup.get(0).firstParent();
57 if (!hgRepo.getChangelog().isKnown(base)) {
58 throw new IllegalArgumentException("unknown parent");
59 }
60 // BundleFormat wiki says:
61 // Each Changelog entry patches the result of all previous patches
62 // (the previous, or parent patch of a given patch p is the patch that has a node equal to p's p1 field)
63 byte[] baseRevContent = hgRepo.getChangelog().content(base);
64 for (GroupElement ge : changelogGroup) {
65 byte[] csetContent = RevlogStream.apply(baseRevContent, -1, ge.patches);
66 dh = dh.sha1(ge.firstParent(), ge.secondParent(), csetContent); // XXX ge may give me access to byte[] content of nodeid directly, perhaps, I don't need DH to be friend of Nodeid?
67 if (!ge.node().equalsTo(dh.asBinary())) {
68 throw new IllegalStateException("Integrity check failed on " + bundleFile + ", node:" + ge.node());
69 }
70 Changeset cs = Changeset.parse(csetContent, 0, csetContent.length);
71 System.out.println(cs.toString());
72 baseRevContent = csetContent;
73 }
74 } finally {
75 da.done();
76 }
77 }
78
79 public void dump() throws IOException {
80 DataAccess da = accessProvider.create(bundleFile);
81 try {
82 LinkedList<String> names = new LinkedList<String>();
83 if (!da.isEmpty()) {
84 System.out.println("Changelog group");
85 List<GroupElement> changelogGroup = readGroup(da);
86 for (GroupElement ge : changelogGroup) {
87 System.out.printf(" %s %s %s %s; patches:%d\n", ge.node(), ge.firstParent(), ge.secondParent(), ge.cset(), ge.patches.size());
88 }
89 System.out.println("Manifest group");
90 List<GroupElement> manifestGroup = readGroup(da);
91 for (GroupElement ge : manifestGroup) {
92 System.out.printf(" %s %s %s %s; patches:%d\n", ge.node(), ge.firstParent(), ge.secondParent(), ge.cset(), ge.patches.size());
93 }
94 while (!da.isEmpty()) {
95 int fnameLen = da.readInt();
96 if (fnameLen <= 4) {
97 break; // null chunk, the last one.
98 }
99 byte[] fname = new byte[fnameLen - 4];
100 da.readBytes(fname, 0, fname.length);
101 names.add(new String(fname));
102 List<GroupElement> fileGroup = readGroup(da);
103 System.out.println(names.getLast());
104 for (GroupElement ge : fileGroup) {
105 System.out.printf(" %s %s %s %s; patches:%d\n", ge.node(), ge.firstParent(), ge.secondParent(), ge.cset(), ge.patches.size());
106 }
107 }
108 }
109 System.out.println(names.size());
110 for (String s : names) {
111 System.out.println(s);
112 }
113 } finally {
114 da.done();
115 }
116 }
117
118 private static List<GroupElement> readGroup(DataAccess da) throws IOException {
119 int len = da.readInt();
120 LinkedList<GroupElement> rv = new LinkedList<HgBundle.GroupElement>();
121 while (len > 4 && !da.isEmpty()) {
122 byte[] nb = new byte[80];
123 da.readBytes(nb, 0, 80);
124 int dataLength = len-84;
125 LinkedList<RevlogStream.PatchRecord> patches = new LinkedList<RevlogStream.PatchRecord>();
126 while (dataLength > 0) {
127 RevlogStream.PatchRecord pr = RevlogStream.PatchRecord.read(da);
128 patches.add(pr);
129 dataLength -= pr.len + 12;
130 }
131 rv.add(new GroupElement(nb, patches));
132 len = da.isEmpty() ? 0 : da.readInt();
133 }
134 return rv;
135 }
136
137 static class GroupElement {
138 private byte[] header; // byte[80] takes 120 bytes, 4 Nodeids - 192
139 private List<RevlogStream.PatchRecord> patches;
140
141 GroupElement(byte[] fourNodeids, List<RevlogStream.PatchRecord> patchList) {
142 assert fourNodeids != null && fourNodeids.length == 80;
143 // patchList.size() > 0
144 header = fourNodeids;
145 patches = patchList;
146 }
147 public Nodeid node() {
148 return Nodeid.fromBinary(header, 0);
149 }
150 public Nodeid firstParent() {
151 return Nodeid.fromBinary(header, 20);
152 }
153 public Nodeid secondParent() {
154 return Nodeid.fromBinary(header, 40);
155 }
156 public Nodeid cset() { // cs seems to be changeset
157 return Nodeid.fromBinary(header, 60);
158 }
159 }
160 }