Mercurial > hg4j
comparison src/org/tmatesoft/hg/core/HgPushCommand.java @ 650:3b275cc2d2aa
Push: phase4 - settle local and remote phases, push updated phases regardless of server publishing state, do not push secret changesets
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Fri, 28 Jun 2013 19:27:26 +0200 |
| parents | e79cf9a8130b |
| children | cd77bf51b562 |
comparison
equal
deleted
inserted
replaced
| 649:e79cf9a8130b | 650:3b275cc2d2aa |
|---|---|
| 37 import org.tmatesoft.hg.repo.HgPhase; | 37 import org.tmatesoft.hg.repo.HgPhase; |
| 38 import org.tmatesoft.hg.repo.HgRemoteRepository; | 38 import org.tmatesoft.hg.repo.HgRemoteRepository; |
| 39 import org.tmatesoft.hg.repo.HgRepository; | 39 import org.tmatesoft.hg.repo.HgRepository; |
| 40 import org.tmatesoft.hg.repo.HgRuntimeException; | 40 import org.tmatesoft.hg.repo.HgRuntimeException; |
| 41 import org.tmatesoft.hg.util.CancelledException; | 41 import org.tmatesoft.hg.util.CancelledException; |
| 42 import org.tmatesoft.hg.util.Outcome; | |
| 42 import org.tmatesoft.hg.util.Pair; | 43 import org.tmatesoft.hg.util.Pair; |
| 43 import org.tmatesoft.hg.util.ProgressSupport; | 44 import org.tmatesoft.hg.util.ProgressSupport; |
| 44 import org.tmatesoft.hg.util.LogFacility.Severity; | 45 import org.tmatesoft.hg.util.LogFacility.Severity; |
| 45 | 46 |
| 46 /** | 47 /** |
| 70 // find out missing | 71 // find out missing |
| 71 // TODO refactor same code in HgOutgoingCommand #getComparator and #getParentHelper | 72 // TODO refactor same code in HgOutgoingCommand #getComparator and #getParentHelper |
| 72 final HgChangelog clog = repo.getChangelog(); | 73 final HgChangelog clog = repo.getChangelog(); |
| 73 final HgParentChildMap<HgChangelog> parentHelper = new HgParentChildMap<HgChangelog>(clog); | 74 final HgParentChildMap<HgChangelog> parentHelper = new HgParentChildMap<HgChangelog>(clog); |
| 74 parentHelper.init(); | 75 parentHelper.init(); |
| 76 final Internals implRepo = HgInternals.getImplementationRepo(repo); | |
| 77 final PhasesHelper phaseHelper = new PhasesHelper(implRepo, parentHelper); | |
| 75 final RepositoryComparator comparator = new RepositoryComparator(parentHelper, remoteRepo); | 78 final RepositoryComparator comparator = new RepositoryComparator(parentHelper, remoteRepo); |
| 76 comparator.compare(new ProgressSupport.Sub(progress, 50), getCancelSupport(null, true)); | 79 comparator.compare(new ProgressSupport.Sub(progress, 50), getCancelSupport(null, true)); |
| 77 List<Nodeid> l = comparator.getLocalOnlyRevisions(); | 80 List<Nodeid> l = comparator.getLocalOnlyRevisions(); |
| 81 final RevisionSet outgoing; | |
| 82 if (phaseHelper.isCapableOfPhases() && phaseHelper.withSecretRoots()) { | |
| 83 RevisionSet secret = phaseHelper.allSecret(); | |
| 84 outgoing = new RevisionSet(l).subtract(secret); | |
| 85 } else { | |
| 86 outgoing = new RevisionSet(l); | |
| 87 } | |
| 78 // | 88 // |
| 79 // prepare bundle | 89 // prepare bundle |
| 80 final Internals implRepo = HgInternals.getImplementationRepo(repo); | |
| 81 BundleGenerator bg = new BundleGenerator(implRepo); | 90 BundleGenerator bg = new BundleGenerator(implRepo); |
| 82 File bundleFile = bg.create(l); | 91 File bundleFile = bg.create(outgoing.asList()); |
| 83 progress.worked(20); | 92 progress.worked(20); |
| 84 HgBundle b = new HgLookup(repo.getSessionContext()).loadBundle(bundleFile); | 93 HgBundle b = new HgLookup(repo.getSessionContext()).loadBundle(bundleFile); |
| 85 // | 94 // |
| 86 // send changes | 95 // send changes |
| 87 remoteRepo.unbundle(b, comparator.getRemoteHeads()); | 96 remoteRepo.unbundle(b, comparator.getRemoteHeads()); |
| 88 progress.worked(20); | 97 progress.worked(20); |
| 89 // | 98 // |
| 90 // update phase information | 99 // update phase information |
| 91 PhasesHelper phaseHelper = new PhasesHelper(implRepo, parentHelper); | |
| 92 if (phaseHelper.isCapableOfPhases()) { | 100 if (phaseHelper.isCapableOfPhases()) { |
| 93 RevisionSet outgoing = new RevisionSet(l); | |
| 94 RevisionSet presentSecret = phaseHelper.allSecret(); | 101 RevisionSet presentSecret = phaseHelper.allSecret(); |
| 95 RevisionSet presentDraft = phaseHelper.allDraft(); | 102 RevisionSet presentDraft = phaseHelper.allDraft(); |
| 96 RevisionSet secretLeft, draftLeft; | 103 RevisionSet secretLeft, draftLeft; |
| 97 HgRemoteRepository.Phases remotePhases = remoteRepo.getPhases(); | 104 HgRemoteRepository.Phases remotePhases = remoteRepo.getPhases(); |
| 105 RevisionSet remoteDrafts = knownRemoteDrafts(remotePhases, parentHelper, outgoing); | |
| 98 if (remotePhases.isPublishingServer()) { | 106 if (remotePhases.isPublishingServer()) { |
| 99 // although it's unlikely outgoing would affect secret changesets, | 107 // although it's unlikely outgoing would affect secret changesets, |
| 100 // it doesn't hurt to check secret roots along with draft ones | 108 // it doesn't hurt to check secret roots along with draft ones |
| 101 secretLeft = presentSecret.subtract(outgoing); | 109 secretLeft = presentSecret.subtract(outgoing); |
| 102 draftLeft = presentDraft.subtract(outgoing); | 110 draftLeft = presentDraft.subtract(outgoing); |
| 103 } else { | 111 } else { |
| 104 // shall merge local and remote phase states | 112 // shall merge local and remote phase states |
| 105 ArrayList<Nodeid> knownRemoteDraftRoots = new ArrayList<Nodeid>(); | |
| 106 for (Nodeid rdr : remotePhases.draftRoots()) { | |
| 107 if (clog.isKnown(rdr)) { | |
| 108 knownRemoteDraftRoots.add(rdr); | |
| 109 } | |
| 110 } | |
| 111 // childrenOf(knownRemoteDraftRoots) is everything remote may treat as Draft | |
| 112 RevisionSet remoteDrafts = new RevisionSet(parentHelper.childrenOf(knownRemoteDraftRoots)); | |
| 113 List<Nodeid> localChildrenNotSent = parentHelper.childrenOf(outgoing.heads(parentHelper).asList()); | |
| 114 // remote shall know only what we've sent, subtract revisions we didn't actually sent | |
| 115 remoteDrafts = remoteDrafts.subtract(new RevisionSet(localChildrenNotSent)); | |
| 116 // if there's a remote draft root that points to revision we know is public | |
| 117 RevisionSet remoteDraftsLocallyPublic = remoteDrafts.subtract(presentSecret).subtract(presentDraft); | |
| 118 if (!remoteDraftsLocallyPublic.isEmpty()) { | |
| 119 // foreach remoteDraftsLocallyPublic.heads() do push Draft->Public | |
| 120 for (Nodeid n : remoteDraftsLocallyPublic.heads(parentHelper)) { | |
| 121 try { | |
| 122 remoteRepo.updatePhase(HgPhase.Draft, HgPhase.Public, n); | |
| 123 } catch (HgRemoteConnectionException ex) { | |
| 124 implRepo.getLog().dump(getClass(), Severity.Error, ex, String.format("Failed to update phase of %s", n.shortNotation())); | |
| 125 } | |
| 126 } | |
| 127 remoteDrafts = remoteDrafts.subtract(remoteDraftsLocallyPublic); | |
| 128 } | |
| 129 // revisions that cease to be secret (gonna become Public), e.g. someone else pushed them | 113 // revisions that cease to be secret (gonna become Public), e.g. someone else pushed them |
| 130 RevisionSet secretGone = presentSecret.intersect(remoteDrafts); | 114 RevisionSet secretGone = presentSecret.intersect(remoteDrafts); |
| 131 // trace parents of these published secret revisions | 115 // parents of those remote drafts are public, mark them as public locally, too |
| 132 RevisionSet secretMadePublic = presentSecret.parentsOf(secretGone, parentHelper); | 116 RevisionSet remotePublic = presentSecret.ancestors(secretGone, parentHelper); |
| 133 secretLeft = presentSecret.subtract(secretGone).subtract(secretMadePublic); | 117 secretLeft = presentSecret.subtract(secretGone).subtract(remotePublic); |
| 134 // same for drafts | 118 /* |
| 135 RevisionSet draftGone = presentDraft.intersect(remoteDrafts); | 119 * Revisions grow from left to right (parents to the left, children to the right) |
| 136 RevisionSet draftMadePublic = presentDraft.parentsOf(draftGone, parentHelper); | 120 * |
| 137 draftLeft = presentDraft.subtract(draftGone).subtract(draftMadePublic); | 121 * I: Set of local is subset of remote |
| 122 * | |
| 123 * local draft | |
| 124 * --o---r---o---l---o-- | |
| 125 * remote draft | |
| 126 * | |
| 127 * Remote draft roots shall be updated | |
| 128 * | |
| 129 * | |
| 130 * II: Set of local is superset of remote | |
| 131 * | |
| 132 * local draft | |
| 133 * --o---l---o---r---o-- | |
| 134 * remote draft | |
| 135 * | |
| 136 * Local draft roots shall be updated | |
| 137 */ | |
| 138 RevisionSet sharedDraft = presentDraft.intersect(remoteDrafts); // (I: ~presentDraft; II: ~remoteDraft | |
| 139 RevisionSet localDraftRemotePublic = presentDraft.ancestors(sharedDraft, parentHelper); // I: 0; II: those treated public on remote | |
| 140 // forget those deemed public by remote (drafts shared by both remote and local are ok to stay) | |
| 141 draftLeft = presentDraft.subtract(localDraftRemotePublic); | |
| 138 } | 142 } |
| 139 final RevisionSet newDraftRoots = draftLeft.roots(parentHelper); | 143 final RevisionSet newDraftRoots = draftLeft.roots(parentHelper); |
| 140 final RevisionSet newSecretRoots = secretLeft.roots(parentHelper); | 144 final RevisionSet newSecretRoots = secretLeft.roots(parentHelper); |
| 141 phaseHelper.updateRoots(newDraftRoots.asList(), newSecretRoots.asList()); | 145 phaseHelper.updateRoots(newDraftRoots.asList(), newSecretRoots.asList()); |
| 146 // | |
| 147 // if there's a remote draft root that points to revision we know is public | |
| 148 RevisionSet remoteDraftsLocalPublic = remoteDrafts.subtract(draftLeft).subtract(secretLeft); | |
| 149 if (!remoteDraftsLocalPublic.isEmpty()) { | |
| 150 // foreach remoteDraftsLocallyPublic.heads() do push Draft->Public | |
| 151 for (Nodeid n : remoteDraftsLocalPublic.heads(parentHelper)) { | |
| 152 try { | |
| 153 Outcome upo = remoteRepo.updatePhase(HgPhase.Draft, HgPhase.Public, n); | |
| 154 if (!upo.isOk()) { | |
| 155 implRepo.getLog().dump(getClass(), Severity.Info, "Failed to update remote phase, reason: %s", upo.getMessage()); | |
| 156 } | |
| 157 } catch (HgRemoteConnectionException ex) { | |
| 158 implRepo.getLog().dump(getClass(), Severity.Error, ex, String.format("Failed to update phase of %s", n.shortNotation())); | |
| 159 } | |
| 160 } | |
| 161 } | |
| 142 } | 162 } |
| 143 progress.worked(5); | 163 progress.worked(5); |
| 144 // | 164 // |
| 145 // update bookmark information | 165 // update bookmark information |
| 146 HgBookmarks localBookmarks = repo.getBookmarks(); | 166 HgBookmarks localBookmarks = repo.getBookmarks(); |
| 170 } finally { | 190 } finally { |
| 171 progress.done(); | 191 progress.done(); |
| 172 } | 192 } |
| 173 } | 193 } |
| 174 | 194 |
| 195 private RevisionSet knownRemoteDrafts(HgRemoteRepository.Phases remotePhases, HgParentChildMap<HgChangelog> parentHelper, RevisionSet outgoing) { | |
| 196 ArrayList<Nodeid> knownRemoteDraftRoots = new ArrayList<Nodeid>(); | |
| 197 for (Nodeid rdr : remotePhases.draftRoots()) { | |
| 198 if (parentHelper.knownNode(rdr)) { | |
| 199 knownRemoteDraftRoots.add(rdr); | |
| 200 } | |
| 201 } | |
| 202 // knownRemoteDraftRoots + childrenOf(knownRemoteDraftRoots) is everything remote may treat as Draft | |
| 203 RevisionSet remoteDrafts = new RevisionSet(knownRemoteDraftRoots); | |
| 204 remoteDrafts = remoteDrafts.union(remoteDrafts.children(parentHelper)); | |
| 205 // 1) outgoing.children gives all local revisions accessible from outgoing. | |
| 206 // 2) outgoing.roots.children is equivalent with smaller intermediate set, the way we build | |
| 207 // childrenOf doesn't really benefits from that. | |
| 208 RevisionSet localChildrenNotSent = outgoing.children(parentHelper).subtract(outgoing); | |
| 209 // remote shall know only what we've sent, subtract revisions we didn't actually sent | |
| 210 remoteDrafts = remoteDrafts.subtract(localChildrenNotSent); | |
| 211 return remoteDrafts; | |
| 212 } | |
| 213 | |
| 175 /* | 214 /* |
| 176 * To test, start a server: | 215 * To test, start a server: |
| 177 * $ hg --config web.allow_push=* --config web.push_ssl=False --config server.validate=True --debug serve | 216 * $ hg --config web.allow_push=* --config web.push_ssl=False --config server.validate=True --debug serve |
| 178 */ | 217 */ |
| 179 public static void main(String[] args) throws Exception { | 218 public static void main(String[] args) throws Exception { |
