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 } |