Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/HgRemoteRepository.java @ 649:e79cf9a8130b
Push: phase4 - update local and remote phase information
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Wed, 26 Jun 2013 20:52:38 +0200 |
| parents | 3b7d51ed4c65 |
| children | 3b275cc2d2aa |
comparison
equal
deleted
inserted
replaced
| 648:690e71d29bf6 | 649:e79cf9a8130b |
|---|---|
| 64 import org.tmatesoft.hg.internal.DataSerializer; | 64 import org.tmatesoft.hg.internal.DataSerializer; |
| 65 import org.tmatesoft.hg.internal.EncodingHelper; | 65 import org.tmatesoft.hg.internal.EncodingHelper; |
| 66 import org.tmatesoft.hg.internal.Internals; | 66 import org.tmatesoft.hg.internal.Internals; |
| 67 import org.tmatesoft.hg.internal.DataSerializer.OutputStreamSerializer; | 67 import org.tmatesoft.hg.internal.DataSerializer.OutputStreamSerializer; |
| 68 import org.tmatesoft.hg.internal.PropertyMarshal; | 68 import org.tmatesoft.hg.internal.PropertyMarshal; |
| 69 import org.tmatesoft.hg.util.Outcome; | |
| 69 import org.tmatesoft.hg.util.Pair; | 70 import org.tmatesoft.hg.util.Pair; |
| 70 import org.tmatesoft.hg.util.LogFacility.Severity; | 71 import org.tmatesoft.hg.util.LogFacility.Severity; |
| 72 import org.tmatesoft.hg.util.Outcome.Kind; | |
| 71 | 73 |
| 72 /** | 74 /** |
| 73 * WORK IN PROGRESS, DO NOT USE | 75 * WORK IN PROGRESS, DO NOT USE |
| 74 * | 76 * |
| 75 * @see http://mercurial.selenic.com/wiki/WireProtocol | 77 * @see http://mercurial.selenic.com/wiki/WireProtocol |
| 448 c.disconnect(); | 450 c.disconnect(); |
| 449 } | 451 } |
| 450 } | 452 } |
| 451 } | 453 } |
| 452 | 454 |
| 453 public List<Pair<String,Nodeid>> bookmarks() throws HgRemoteConnectionException, HgRuntimeException { | 455 public Bookmarks getBookmarks() throws HgRemoteConnectionException, HgRuntimeException { |
| 454 final String actionName = "Get remote bookmarks"; | 456 final String actionName = "Get remote bookmarks"; |
| 455 final List<Pair<String, String>> values = listkeys("bookmarks", actionName); | 457 final List<Pair<String, String>> values = listkeys("bookmarks", actionName); |
| 456 ArrayList<Pair<String, Nodeid>> rv = new ArrayList<Pair<String, Nodeid>>(); | 458 ArrayList<Pair<String, Nodeid>> rv = new ArrayList<Pair<String, Nodeid>>(); |
| 457 for (Pair<String, String> l : values) { | 459 for (Pair<String, String> l : values) { |
| 458 if (l.second().length() != Nodeid.SIZE_ASCII) { | 460 if (l.second().length() != Nodeid.SIZE_ASCII) { |
| 461 } | 463 } |
| 462 Nodeid n = Nodeid.fromAscii(l.second()); | 464 Nodeid n = Nodeid.fromAscii(l.second()); |
| 463 String bm = new String(l.first()); | 465 String bm = new String(l.first()); |
| 464 rv.add(new Pair<String, Nodeid>(bm, n)); | 466 rv.add(new Pair<String, Nodeid>(bm, n)); |
| 465 } | 467 } |
| 466 return rv; | 468 return new Bookmarks(rv); |
| 467 } | 469 } |
| 468 | 470 |
| 469 public void updateBookmark(String name, Nodeid oldRev, Nodeid newRev) throws HgRemoteConnectionException, HgRuntimeException { | 471 public void updateBookmark(String name, Nodeid oldRev, Nodeid newRev) throws HgRemoteConnectionException, HgRuntimeException { |
| 470 final String namespace = "bookmarks"; | 472 final String namespace = "bookmarks"; |
| 471 HttpURLConnection c = null; | 473 HttpURLConnection c = null; |
| 486 c.disconnect(); | 488 c.disconnect(); |
| 487 } | 489 } |
| 488 } | 490 } |
| 489 } | 491 } |
| 490 | 492 |
| 491 private void phases() throws HgRemoteConnectionException, HgRuntimeException { | 493 public Phases getPhases() throws HgRemoteConnectionException, HgRuntimeException { |
| 494 initCapabilities(); | |
| 495 if (!remoteCapabilities.contains("pushkey")) { | |
| 496 // old server defaults to publishing | |
| 497 return new Phases(true, Collections.<Nodeid>emptyList()); | |
| 498 } | |
| 492 final List<Pair<String, String>> values = listkeys("phases", "Get remote phases"); | 499 final List<Pair<String, String>> values = listkeys("phases", "Get remote phases"); |
| 500 boolean publishing = true; | |
| 501 ArrayList<Nodeid> draftRoots = new ArrayList<Nodeid>(); | |
| 493 for (Pair<String, String> l : values) { | 502 for (Pair<String, String> l : values) { |
| 494 System.out.printf("%s : %s\n", l.first(), l.second()); | 503 if ("publishing".equalsIgnoreCase(l.first())) { |
| 495 } | 504 publishing = Boolean.parseBoolean(l.second()); |
| 496 } | 505 continue; |
| 506 } | |
| 507 Nodeid root = Nodeid.fromAscii(l.first()); | |
| 508 int ph = Integer.parseInt(l.second()); | |
| 509 if (ph == HgPhase.Draft.mercurialOrdinal()) { | |
| 510 draftRoots.add(root); | |
| 511 } else { | |
| 512 assert false; | |
| 513 sessionContext.getLog().dump(getClass(), Severity.Error, "Unexpected phase value %d for revision %s", ph, root); | |
| 514 } | |
| 515 } | |
| 516 return new Phases(publishing, draftRoots); | |
| 517 } | |
| 518 | |
| 519 public Outcome updatePhase(HgPhase from, HgPhase to, Nodeid n) throws HgRemoteConnectionException, HgRuntimeException { | |
| 520 if (pushkey("phases", n.toString(), String.valueOf(from.mercurialOrdinal()), String.valueOf(to.mercurialOrdinal()))) { | |
| 521 return new Outcome(Kind.Success, String.format("Phase of %s updated to %s", n.shortNotation(), to.name())); | |
| 522 } | |
| 523 return new Outcome(Kind.Failure, String.format("Phase update (%s: %s -> %s) failed", n.shortNotation(), from.name(), to.name())); | |
| 524 } | |
| 525 | |
| 497 | 526 |
| 498 public static void main(String[] args) throws Exception { | 527 public static void main(String[] args) throws Exception { |
| 499 final HgRemoteRepository r = new HgLookup().detectRemote("http://selenic.com/hg", null); | 528 final HgRemoteRepository r = new HgLookup().detectRemote("http://selenic.com/hg", null); |
| 500 if (r.isInvalid()) { | 529 if (r.isInvalid()) { |
| 501 return; | 530 return; |
| 502 } | 531 } |
| 503 System.out.println(r.remoteCapabilities); | 532 System.out.println(r.remoteCapabilities); |
| 504 r.phases(); | 533 r.getPhases(); |
| 505 final List<Pair<String, Nodeid>> bm = r.bookmarks(); | 534 final Iterable<Pair<String, Nodeid>> bm = r.getBookmarks(); |
| 506 for (Pair<String, Nodeid> pair : bm) { | 535 for (Pair<String, Nodeid> pair : bm) { |
| 507 System.out.println(pair); | 536 System.out.println(pair); |
| 508 } | 537 } |
| 509 } | 538 } |
| 510 | 539 |
| 598 c.disconnect(); | 627 c.disconnect(); |
| 599 } | 628 } |
| 600 } | 629 } |
| 601 } | 630 } |
| 602 | 631 |
| 632 private boolean pushkey(String namespace, String key, String oldValue, String newValue) throws HgRemoteConnectionException, HgRuntimeException { | |
| 633 HttpURLConnection c = null; | |
| 634 try { | |
| 635 final String p = String.format("%s?cmd=pushkey&namespace=%s&key=%s&old=%s&new=&s", url.getPath(), namespace, key, oldValue, newValue); | |
| 636 URL u = new URL(url, p); | |
| 637 c = setupConnection(u.openConnection()); | |
| 638 c.connect(); | |
| 639 if (debug) { | |
| 640 dumpResponseHeader(u, c); | |
| 641 } | |
| 642 checkResponseOk(c, key, "pushkey"); | |
| 643 final InputStream is = c.getInputStream(); | |
| 644 int rv = is.read(); | |
| 645 if (is.read() != -1) { | |
| 646 sessionContext.getLog().dump(getClass(), Severity.Error, "Unexpected data in response to pushkey"); | |
| 647 } | |
| 648 is.close(); | |
| 649 return rv == '1'; | |
| 650 } catch (MalformedURLException ex) { | |
| 651 throw new HgRemoteConnectionException("Bad URL", ex).setRemoteCommand("pushkey").setServerInfo(getLocation()); | |
| 652 } catch (IOException ex) { | |
| 653 throw new HgRemoteConnectionException("Communication failure", ex).setRemoteCommand("pushkey").setServerInfo(getLocation()); | |
| 654 } finally { | |
| 655 if (c != null) { | |
| 656 c.disconnect(); | |
| 657 } | |
| 658 } | |
| 659 } | |
| 660 | |
| 603 private void checkResponseOk(HttpURLConnection c, String opName, String remoteCmd) throws HgRemoteConnectionException, IOException { | 661 private void checkResponseOk(HttpURLConnection c, String opName, String remoteCmd) throws HgRemoteConnectionException, IOException { |
| 604 if (c.getResponseCode() != 200) { | 662 if (c.getResponseCode() != 200) { |
| 605 String m = c.getResponseMessage() == null ? "unknown reason" : c.getResponseMessage(); | 663 String m = c.getResponseMessage() == null ? "unknown reason" : c.getResponseMessage(); |
| 606 String em = String.format("%s failed: %s (HTTP error:%d)", opName, m, c.getResponseCode()); | 664 String em = String.format("%s failed: %s (HTTP error:%d)", opName, m, c.getResponseCode()); |
| 607 throw new HgRemoteConnectionException(em).setRemoteCommand(remoteCmd).setServerInfo(getLocation()); | 665 throw new HgRemoteConnectionException(em).setRemoteCommand(remoteCmd).setServerInfo(getLocation()); |
| 710 RemoteBranch o = (RemoteBranch) obj; | 768 RemoteBranch o = (RemoteBranch) obj; |
| 711 // in fact, p1 and p2 are not supposed to be null, ever (at least for RemoteBranch created from server output) | 769 // in fact, p1 and p2 are not supposed to be null, ever (at least for RemoteBranch created from server output) |
| 712 return head.equals(o.head) && root.equals(o.root) && (p1 == null && o.p1 == null || p1.equals(o.p1)) && (p2 == null && o.p2 == null || p2.equals(o.p2)); | 770 return head.equals(o.head) && root.equals(o.root) && (p1 == null && o.p1 == null || p1.equals(o.p1)) && (p2 == null && o.p2 == null || p2.equals(o.p2)); |
| 713 } | 771 } |
| 714 } | 772 } |
| 773 | |
| 774 public static final class Bookmarks implements Iterable<Pair<String, Nodeid>> { | |
| 775 private final List<Pair<String, Nodeid>> bm; | |
| 776 | |
| 777 private Bookmarks(List<Pair<String, Nodeid>> bookmarks) { | |
| 778 bm = bookmarks; | |
| 779 } | |
| 780 | |
| 781 public Iterator<Pair<String, Nodeid>> iterator() { | |
| 782 return bm.iterator(); | |
| 783 } | |
| 784 } | |
| 785 | |
| 786 public static final class Phases { | |
| 787 private final boolean pub; | |
| 788 private final List<Nodeid> droots; | |
| 789 | |
| 790 private Phases(boolean publishing, List<Nodeid> draftRoots) { | |
| 791 pub = publishing; | |
| 792 droots = draftRoots; | |
| 793 } | |
| 794 | |
| 795 /** | |
| 796 * Non-publishing servers may (shall?) respond with a list of draft roots. | |
| 797 * This method doesn't make sense when {@link #isPublishingServer()} is <code>true</code> | |
| 798 * | |
| 799 * @return list of draft roots on remote server | |
| 800 */ | |
| 801 public List<Nodeid> draftRoots() { | |
| 802 assert !pub; | |
| 803 return droots; | |
| 804 } | |
| 805 | |
| 806 /** | |
| 807 * @return <code>true</code> if revisions on remote server shall be deemed published (either | |
| 808 * old server w/o explicit setting, or a new one with <code>phases.publish == true</code>) | |
| 809 */ | |
| 810 public boolean isPublishingServer() { | |
| 811 return pub; | |
| 812 } | |
| 813 } | |
| 715 } | 814 } |
