# HG changeset patch # User Artem Tikhomirov # Date 1367864901 -7200 # Node ID c56edf42be645e6595d2c1ef0115d5c5c05f6260 # Parent c3505001a42ab4651c026f6ce01d0b533827b220 Commit: update active bookmark with new revision diff -r c3505001a42a -r c56edf42be64 src/org/tmatesoft/hg/internal/CommitFacility.java --- a/src/org/tmatesoft/hg/internal/CommitFacility.java Mon May 06 18:53:04 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/CommitFacility.java Mon May 06 20:28:21 2013 +0200 @@ -180,7 +180,7 @@ byte[] clogContent = changelogBuilder.build(manifestRev, message); RevlogStreamWriter changelogWriter = new RevlogStreamWriter(repo, repo.getImplAccess().getChangelogStream()); Nodeid changesetRev = changelogWriter.addRevision(clogContent, clogRevisionIndex, p1Commit, p2Commit); - // FIXME move fncache update to an external facility, along with dirstate update + // FIXME move fncache update to an external facility, along with dirstate and bookmark update if (!newlyAddedFiles.isEmpty() && repo.fncacheInUse()) { FNCacheFile fncache = new FNCacheFile(repo); for (Path p : newlyAddedFiles) { @@ -204,6 +204,12 @@ } dirstateBuilder.parents(changesetRev, Nodeid.NULL); dirstateBuilder.serialize(); + // update bookmarks + Nodeid p1Cset = p1Commit == NO_REVISION ? null : clog.getRevision(p1Commit); + Nodeid p2Cset = p2Commit == NO_REVISION ? null : clog.getRevision(p2Commit); + if (p1Commit != NO_REVISION || p2Commit != NO_REVISION) { + repo.getRepo().getBookmarks().updateActive(p1Cset, p2Cset, changesetRev); + } return changesetRev; } /* diff -r c3505001a42a -r c56edf42be64 src/org/tmatesoft/hg/repo/HgBookmarks.java --- a/src/org/tmatesoft/hg/repo/HgBookmarks.java Mon May 06 18:53:04 2013 +0200 +++ b/src/org/tmatesoft/hg/repo/HgBookmarks.java Mon May 06 20:28:21 2013 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 TMate Software Ltd + * Copyright (c) 2012-2013 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,20 +16,28 @@ */ package org.tmatesoft.hg.repo; +import static org.tmatesoft.hg.util.LogFacility.Severity.Error; + import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import org.tmatesoft.hg.core.HgIOException; +import org.tmatesoft.hg.core.HgRepositoryLockException; import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.internal.Internals; import org.tmatesoft.hg.internal.LineReader; import org.tmatesoft.hg.util.LogFacility; /** * + * @see http://mercurial.selenic.com/wiki/Bookmarks * @author Artem Tikhomirov * @author TMate Software Ltd. */ @@ -114,4 +122,61 @@ // hence can use view (not a synchronized copy) here return Collections.unmodifiableSet(bookmarks.keySet()); } + + /** + * Update currently bookmark with new commit. + * Note, child has to be descendant of a p1 or p2 + * + * @param p1 first parent, or null + * @param p2 second parent, or null + * @param child new commit, descendant of one of the parents, not null + * @throws HgIOException if failed to write updated bookmark information + * @throws HgRepositoryLockException if failed to lock repository for modifications + */ + @Experimental(reason="Provisional API") + public void updateActive(Nodeid p1, Nodeid p2, Nodeid child) throws HgIOException, HgRepositoryLockException { + if (activeBookmark == null) { + return; + } + Nodeid activeRev = getRevision(activeBookmark); + if (!activeRev.equals(p1) && !activeRev.equals(p2)) { + // from the wiki: + // "active bookmarks are automatically updated when committing to the changeset they are pointing to" + // FIXME: test ci 1, hg bookmark active, ci 2, hg bookmark -f -r 0 active, ci 3, check active still points to r0 + return; + } + if (child.equals(activeRev)) { + return; + } + LinkedHashMap copy = new LinkedHashMap(bookmarks); + copy.put(activeBookmark, child); + bookmarks = copy; + write(); + } + + private void write() throws HgIOException, HgRepositoryLockException { + File bookmarksFile = internalRepo.getRepositoryFile(HgRepositoryFiles.Bookmarks); + HgRepositoryLock workingDirLock = internalRepo.getRepo().getWorkingDirLock(); + FileWriter fileWriter = null; + workingDirLock.acquire(); + try { + fileWriter = new FileWriter(bookmarksFile); + for (String bm : bookmarks.keySet()) { + Nodeid nid = bookmarks.get(bm); + fileWriter.write(String.format("%s %s\n", nid.toString(), bm)); + } + fileWriter.flush(); + } catch (IOException ex) { + throw new HgIOException("Failed to serialize bookmarks", ex, bookmarksFile); + } finally { + try { + if (fileWriter != null) { + fileWriter.close(); + } + } catch (IOException ex) { + internalRepo.getSessionContext().getLog().dump(getClass(), Error, ex, null); + } + workingDirLock.release(); + } + } } diff -r c3505001a42a -r c56edf42be64 src/org/tmatesoft/hg/repo/HgRepositoryLock.java --- a/src/org/tmatesoft/hg/repo/HgRepositoryLock.java Mon May 06 18:53:04 2013 +0200 +++ b/src/org/tmatesoft/hg/repo/HgRepositoryLock.java Mon May 06 20:28:21 2013 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 TMate Software Ltd + * Copyright (c) 2012-2013 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -99,7 +99,7 @@ /** * Perform actual locking. Waits for timeout (if specified at construction time) - * before throwing {@link HgInvalidStateException} in case lock is not available + * before throwing {@link HgRepositoryLockException} in case lock is not available * immediately. * *

Multiple calls are possible, but corresponding number of {@link #release()} diff -r c3505001a42a -r c56edf42be64 test/org/tmatesoft/hg/test/TestCommit.java --- a/test/org/tmatesoft/hg/test/TestCommit.java Mon May 06 18:53:04 2013 +0200 +++ b/test/org/tmatesoft/hg/test/TestCommit.java Mon May 06 20:28:21 2013 +0200 @@ -277,6 +277,37 @@ assertHgVerifyOk(repoLoc); } + @Test + public void testUpdateActiveBookmark() throws Exception { + File repoLoc = RepoUtils.cloneRepoToTempLocation("log-1", "test-commit-cmd", false); + ExecHelper eh = new ExecHelper(new OutputParser.Stub(), repoLoc); + String activeBookmark = "bm1"; + eh.run("hg", "bookmarks", activeBookmark); + + HgRepository hgRepo = new HgLookup().detect(repoLoc); + assertEquals("[sanity]", activeBookmark, hgRepo.getBookmarks().getActiveBookmarkName()); + Nodeid activeBookmarkRevision = hgRepo.getBookmarks().getRevision(activeBookmark); + assertEquals("[sanity]", activeBookmarkRevision, hgRepo.getWorkingCopyParents().first()); + + HgDataFile dfD = hgRepo.getFileNode("d"); + File fileD = new File(repoLoc, "d"); + assertTrue("[sanity]", dfD.exists()); + assertTrue("[sanity]", fileD.canRead()); + + RepoUtils.modifyFileAppend(fileD, " 1 \n"); + HgCommitCommand cmd = new HgCommitCommand(hgRepo).message("FIRST"); + Outcome r = cmd.execute(); + errorCollector.assertTrue(r.isOk()); + Nodeid c = cmd.getCommittedRevision(); + + errorCollector.assertEquals(activeBookmark, hgRepo.getBookmarks().getActiveBookmarkName()); + errorCollector.assertEquals(c, hgRepo.getBookmarks().getRevision(activeBookmark)); + // reload repo, and repeat the check + hgRepo = new HgLookup().detect(repoLoc); + errorCollector.assertEquals(activeBookmark, hgRepo.getBookmarks().getActiveBookmarkName()); + errorCollector.assertEquals(c, hgRepo.getBookmarks().getRevision(activeBookmark)); + } + private void assertHgVerifyOk(File repoLoc) throws InterruptedException, IOException { ExecHelper verifyRun = new ExecHelper(new OutputParser.Stub(), repoLoc); verifyRun.run("hg", "verify");