Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/CommitFacility.java @ 617:65c01508f002
Rollback support for commands that modify repository. Strategy to keep complete copy of a file being changed
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Wed, 15 May 2013 20:10:09 +0200 |
parents | 5e0313485eef |
children | 7c0d2ce340b8 |
comparison
equal
deleted
inserted
replaced
616:5e0313485eef | 617:65c01508f002 |
---|---|
17 package org.tmatesoft.hg.internal; | 17 package org.tmatesoft.hg.internal; |
18 | 18 |
19 import static org.tmatesoft.hg.repo.HgRepository.DEFAULT_BRANCH_NAME; | 19 import static org.tmatesoft.hg.repo.HgRepository.DEFAULT_BRANCH_NAME; |
20 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; | 20 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; |
21 import static org.tmatesoft.hg.repo.HgRepositoryFiles.Branch; | 21 import static org.tmatesoft.hg.repo.HgRepositoryFiles.Branch; |
22 import static org.tmatesoft.hg.repo.HgRepositoryFiles.UndoBranch; | |
22 import static org.tmatesoft.hg.util.LogFacility.Severity.Error; | 23 import static org.tmatesoft.hg.util.LogFacility.Severity.Error; |
23 | 24 |
24 import java.io.File; | 25 import java.io.File; |
25 import java.io.FileOutputStream; | 26 import java.io.FileOutputStream; |
26 import java.io.IOException; | 27 import java.io.IOException; |
92 | 93 |
93 public void user(String userName) { | 94 public void user(String userName) { |
94 user = userName; | 95 user = userName; |
95 } | 96 } |
96 | 97 |
97 public Nodeid commit(String message) throws HgIOException, HgRepositoryLockException { | 98 // this method doesn't roll transaction back in case of failure, caller's responsibility |
99 // this method expects repository to be locked, if needed | |
100 public Nodeid commit(String message, Transaction transaction) throws HgIOException, HgRepositoryLockException { | |
98 final HgChangelog clog = repo.getRepo().getChangelog(); | 101 final HgChangelog clog = repo.getRepo().getChangelog(); |
99 final int clogRevisionIndex = clog.getRevisionCount(); | 102 final int clogRevisionIndex = clog.getRevisionCount(); |
100 ManifestRevision c1Manifest = new ManifestRevision(null, null); | 103 ManifestRevision c1Manifest = new ManifestRevision(null, null); |
101 ManifestRevision c2Manifest = new ManifestRevision(null, null); | 104 ManifestRevision c2Manifest = new ManifestRevision(null, null); |
102 final Nodeid p1Cset = p1Commit == NO_REVISION ? null : clog.getRevision(p1Commit); | 105 final Nodeid p1Cset = p1Commit == NO_REVISION ? null : clog.getRevision(p1Commit); |
159 newlyAddedFiles.put(df.getPath(), contentStream); | 162 newlyAddedFiles.put(df.getPath(), contentStream); |
160 // FIXME df doesn't get df.content updated, and clients | 163 // FIXME df doesn't get df.content updated, and clients |
161 // that would attempt to access newly added file after commit would fail | 164 // that would attempt to access newly added file after commit would fail |
162 // (despite the fact the file is in there) | 165 // (despite the fact the file is in there) |
163 } | 166 } |
164 RevlogStreamWriter fileWriter = new RevlogStreamWriter(repo, contentStream); | 167 RevlogStreamWriter fileWriter = new RevlogStreamWriter(repo, contentStream, transaction); |
165 Nodeid fileRev = fileWriter.addRevision(bac.toArray(), clogRevisionIndex, fp.first(), fp.second()); | 168 Nodeid fileRev = fileWriter.addRevision(bac.toArray(), clogRevisionIndex, fp.first(), fp.second()); |
166 newManifestRevision.put(df.getPath(), fileRev); | 169 newManifestRevision.put(df.getPath(), fileRev); |
167 touchInDirstate.add(df.getPath()); | 170 touchInDirstate.add(df.getPath()); |
168 } | 171 } |
169 // | 172 // |
170 // Manifest | 173 // Manifest |
171 final ManifestEntryBuilder manifestBuilder = new ManifestEntryBuilder(); | 174 final ManifestEntryBuilder manifestBuilder = new ManifestEntryBuilder(); |
172 for (Map.Entry<Path, Nodeid> me : newManifestRevision.entrySet()) { | 175 for (Map.Entry<Path, Nodeid> me : newManifestRevision.entrySet()) { |
173 manifestBuilder.add(me.getKey().toString(), me.getValue()); | 176 manifestBuilder.add(me.getKey().toString(), me.getValue()); |
174 } | 177 } |
175 RevlogStreamWriter manifestWriter = new RevlogStreamWriter(repo, repo.getImplAccess().getManifestStream()); | 178 RevlogStreamWriter manifestWriter = new RevlogStreamWriter(repo, repo.getImplAccess().getManifestStream(), transaction); |
176 Nodeid manifestRev = manifestWriter.addRevision(manifestBuilder.build(), clogRevisionIndex, manifestParents.first(), manifestParents.second()); | 179 Nodeid manifestRev = manifestWriter.addRevision(manifestBuilder.build(), clogRevisionIndex, manifestParents.first(), manifestParents.second()); |
177 // | 180 // |
178 // Changelog | 181 // Changelog |
179 final ChangelogEntryBuilder changelogBuilder = new ChangelogEntryBuilder(); | 182 final ChangelogEntryBuilder changelogBuilder = new ChangelogEntryBuilder(); |
180 changelogBuilder.setModified(files.keySet()); | 183 changelogBuilder.setModified(files.keySet()); |
181 changelogBuilder.branch(branch == null ? DEFAULT_BRANCH_NAME : branch); | 184 changelogBuilder.branch(branch == null ? DEFAULT_BRANCH_NAME : branch); |
182 changelogBuilder.user(String.valueOf(user)); | 185 changelogBuilder.user(String.valueOf(user)); |
183 byte[] clogContent = changelogBuilder.build(manifestRev, message); | 186 byte[] clogContent = changelogBuilder.build(manifestRev, message); |
184 RevlogStreamWriter changelogWriter = new RevlogStreamWriter(repo, repo.getImplAccess().getChangelogStream()); | 187 RevlogStreamWriter changelogWriter = new RevlogStreamWriter(repo, repo.getImplAccess().getChangelogStream(), transaction); |
185 Nodeid changesetRev = changelogWriter.addRevision(clogContent, clogRevisionIndex, p1Commit, p2Commit); | 188 Nodeid changesetRev = changelogWriter.addRevision(clogContent, clogRevisionIndex, p1Commit, p2Commit); |
186 // TODO move fncache update to an external facility, along with dirstate and bookmark update | 189 // TODO move fncache update to an external facility, along with dirstate and bookmark update |
187 if (!newlyAddedFiles.isEmpty() && repo.fncacheInUse()) { | 190 if (!newlyAddedFiles.isEmpty() && repo.fncacheInUse()) { |
188 FNCacheFile fncache = new FNCacheFile(repo); | 191 FNCacheFile fncache = new FNCacheFile(repo); |
189 for (Path p : newlyAddedFiles.keySet()) { | 192 for (Path p : newlyAddedFiles.keySet()) { |
199 repo.getLog().dump(getClass(), Error, ex, "Failed to write fncache, error ignored"); | 202 repo.getLog().dump(getClass(), Error, ex, "Failed to write fncache, error ignored"); |
200 } | 203 } |
201 } | 204 } |
202 String oldBranchValue = DirstateReader.readBranch(repo); | 205 String oldBranchValue = DirstateReader.readBranch(repo); |
203 String newBranchValue = branch == null ? DEFAULT_BRANCH_NAME : branch; | 206 String newBranchValue = branch == null ? DEFAULT_BRANCH_NAME : branch; |
207 // TODO undo.dirstate and undo.branch as described in http://mercurial.selenic.com/wiki/FileFormats#undo..2A | |
204 if (!oldBranchValue.equals(newBranchValue)) { | 208 if (!oldBranchValue.equals(newBranchValue)) { |
205 File branchFile = repo.getRepositoryFile(Branch); | 209 File branchFile = transaction.prepare(repo.getRepositoryFile(Branch), repo.getRepositoryFile(UndoBranch)); |
206 FileOutputStream fos = null; | 210 FileOutputStream fos = null; |
207 try { | 211 try { |
208 fos = new FileOutputStream(branchFile); | 212 fos = new FileOutputStream(branchFile); |
209 fos.write(newBranchValue.getBytes(EncodingHelper.getUTF8())); | 213 fos.write(newBranchValue.getBytes(EncodingHelper.getUTF8())); |
210 fos.flush(); | 214 fos.flush(); |
215 fos.close(); | |
216 fos = null; | |
217 transaction.done(branchFile); | |
211 } catch (IOException ex) { | 218 } catch (IOException ex) { |
219 transaction.failure(branchFile, ex); | |
212 repo.getLog().dump(getClass(), Error, ex, "Failed to write branch information, error ignored"); | 220 repo.getLog().dump(getClass(), Error, ex, "Failed to write branch information, error ignored"); |
213 } finally { | 221 } finally { |
214 try { | 222 try { |
215 if (fos != null) { | 223 if (fos != null) { |
216 fos.close(); | 224 fos.close(); |
218 } catch (IOException ex) { | 226 } catch (IOException ex) { |
219 repo.getLog().dump(getClass(), Error, ex, null); | 227 repo.getLog().dump(getClass(), Error, ex, null); |
220 } | 228 } |
221 } | 229 } |
222 } | 230 } |
223 // bring dirstate up to commit state | 231 // bring dirstate up to commit state, TODO share this code with HgAddRemoveCommand |
224 final DirstateBuilder dirstateBuilder = new DirstateBuilder(repo); | 232 final DirstateBuilder dirstateBuilder = new DirstateBuilder(repo); |
225 dirstateBuilder.fillFrom(new DirstateReader(repo, new Path.SimpleSource())); | 233 dirstateBuilder.fillFrom(new DirstateReader(repo, new Path.SimpleSource())); |
226 for (Path p : removals) { | 234 for (Path p : removals) { |
227 dirstateBuilder.recordRemoved(p); | 235 dirstateBuilder.recordRemoved(p); |
228 } | 236 } |
229 for (Path p : touchInDirstate) { | 237 for (Path p : touchInDirstate) { |
230 dirstateBuilder.recordUncertain(p); | 238 dirstateBuilder.recordUncertain(p); |
231 } | 239 } |
232 dirstateBuilder.parents(changesetRev, Nodeid.NULL); | 240 dirstateBuilder.parents(changesetRev, Nodeid.NULL); |
233 dirstateBuilder.serialize(); | 241 dirstateBuilder.serialize(transaction); |
234 // update bookmarks | 242 // update bookmarks |
235 if (p1Commit != NO_REVISION || p2Commit != NO_REVISION) { | 243 if (p1Commit != NO_REVISION || p2Commit != NO_REVISION) { |
236 repo.getRepo().getBookmarks().updateActive(p1Cset, p2Cset, changesetRev); | 244 repo.getRepo().getBookmarks().updateActive(p1Cset, p2Cset, changesetRev); |
237 } | 245 } |
238 // TODO undo.dirstate and undo.branch as described in http://mercurial.selenic.com/wiki/FileFormats#undo..2A | |
239 // TODO Revisit: might be reasonable to send out a "Repo changed" notification, to clear | 246 // TODO Revisit: might be reasonable to send out a "Repo changed" notification, to clear |
240 // e.g. cached branch, tags and so on, not to rely on file change detection methods? | 247 // e.g. cached branch, tags and so on, not to rely on file change detection methods? |
241 // The same notification might come useful once Pull is implemented | 248 // The same notification might come useful once Pull is implemented |
242 return changesetRev; | 249 return changesetRev; |
243 } | 250 } |