Mercurial > jhg
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 { |