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 }