{"thread":{"id":"15059","subject":"[EGIT PATCH 01/31] Fix Repository.mapObject() for missing objects","startedAt":"2008-08-17T20:43:41Z","lastAt":"2008-08-25T14:24:17Z","messageCount":62,"participants":["Marek Zawirski","Shawn O. Pearce","Robin Rosenberg","Petr Baudis","Jakub Narebski","Mike Dalessio","Pierre Habouzit","Martin Langhoff","Imran M Yousuf"],"isPatch":true,"patchVersion":1,"patchTotal":31},"messages":[{"id":"87464","messageId":"1219005852-21496-1-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":null,"subject":"[EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:41Z","receivedAt":"2008-08-17T20:43:41Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"That's the final series for one of my core tasks for GSoC: GUI related\nstuff. Little late, but it required lot of polishing.\n\nTraditionally, it's also available at\nhttp://repo.or.cz/w/egit/zawir.git?a=shortlog;h=refs/heads/push\nBut wait... this time it was pushed from Eclipse! ;)\n\nThe series consists of few parts:\n1) Various small fixes and improvements to jgit.\n2) Lot of refactor, fixes, improvements in Git Clone Wizard.\n3) Push GUI with some general purpose components.\n(more in commit messages)\n\nFetch GUI is not implemented yet, although with this\nEclipse-experience and new stuff available in .components, I suspect\nit we'll be *much* much easier to do it than this push GUI, so I may\ntry to do it as well, especially if you enjoy this one. I'm just\nstarting working on that.\n\nChanges in plugin were tested on Eclipse 3.4 at Linux, Windows XP and\npartially at MacOS 10.4 (there are some issues that makes it looking\nworse there, one very annoying bug is reported to Eclipse' bugzilla).\nI'm now using mostly Eclipse 3.4, but I've tried to use up to 3.3 API.\nShawn also tried this code at Eclipse 3.3 on OS X 10.5, at least up to\nsome stage.\n\nComments regarding this GUI are welcome, I hope that yet another mind\ncan bring some ideas. Recently (few days ago), Shawn brought some\ngreat idea of making Eclipse view for results of operations on remote\n(fetch, push) instead of dialog. That would be cool, although\nunfortunatelly it was too late for me to do it - to change project at\nthis stage.\n\nI've got some problem with icons right now, as I'm pretty far from\nstating that I'm good at doing that sort of art.\nRobin, Tor, I know that you were already contributing some graphics to\negit. If someone of you would like to do some icon for push/fetch with\npleasure, you are welcome. Otherwise I'll have to do some crappy icon\ninstead ;) Another matter are checkboxes screenshots. I'm not sure\nabout legality status of including them. Any ideas if/how we co use\nthem or some another set that we can for sure?\n\nAlthough that's last big portion of code during this GSoC from me,\nI may send some smaller things in next 2 days or fix this stuff if\nneeded.\n\nHave fun with push;>\nMarek / zawir\n\nMarek Zawirski (31):\n  Fix Repository.mapObject() for missing objects\n  Fix Repository isValidRefName() for empty names\n  Fix Repository.resolve() to not throw runtime exceptions\n  Document/fix Transport open method for specific case\n  Fix RefSpec javadoc regarding spec expanding\n  Make wildcard checking public in RefSpec\n  Add openAll() and applyConfig() methods to Transport\n  Add dryRun option to Transport and console push\n  Extract Transport findRemoteRefUpdatesFor() as static method\n  Improve javadoc of Transport push()\n  Clean up exception issues in RemoteRefUpdate\n  Add another RemoteRefUpdate constructor, useful for 2-stage push\n  Add getAllRemoteConfigs() to RemoteConfig\n  Add setFetchRefSpecs and setPushRefSpecs to RemoteConfig\n  Add simple abbreviate() method to ObjectId\n  Remove debug/test console output from GitIndex\n  Fix typo in uitext.properties message\n  Refactor/rewrite CloneSourcePage to universal RepositorySelectionPage\n  Clone wizard and related: refactor, clean-up, fixes or improvements\n  Move clone logic away from GitCloneWizard to CloneOperation\n  Add canCreateSubdir() heuristic in CloneDestinationPage\n  Set FileDialog selection appropriately in clone wizard\n  Allow selecting empty dir in clone wizard\n  Clone wizard: force dir to suggested path only if repo selection\n    change\n  Create ListRemoteOperation for listing remote repo branches\n  Make Clone's SourceBranchPage more user-friendly\n  Add few EPL Eclipse icons\n  Checkbox images/screenshots\n  Universal GUI for specifications edition: RefSpecPanel and related\n  Add PushOperation to plugin\n  Push GUI\n\n .../src/org/spearce/egit/core/CoreText.java        |   18 +\n .../src/org/spearce/egit/core/coretext.properties  |    8 +\n .../org/spearce/egit/core/op/CloneOperation.java   |  129 ++-\n .../spearce/egit/core/op/ListRemoteOperation.java  |  104 ++\n .../org/spearce/egit/core/op/PushOperation.java    |  148 ++\n .../spearce/egit/core/op/PushOperationResult.java  |  273 +++\n .../egit/core/op/PushOperationSpecification.java   |   82 +\n .../icons/checkboxes/disabled_checked.gif          |  Bin 0 -> 166 bytes\n .../icons/checkboxes/disabled_unchecked.gif        |  Bin 0 -> 125 bytes\n .../icons/checkboxes/enabled_checked.gif           |  Bin 0 -> 166 bytes\n .../icons/checkboxes/enabled_unchecked.gif         |  Bin 0 -> 157 bytes\n org.spearce.egit.ui/icons/elcl16/add.gif           |  Bin 0 -> 318 bytes\n org.spearce.egit.ui/icons/elcl16/clear.gif         |  Bin 0 -> 595 bytes\n org.spearce.egit.ui/icons/elcl16/delete.gif        |  Bin 0 -> 351 bytes\n org.spearce.egit.ui/icons/elcl16/trash.gif         |  Bin 0 -> 590 bytes\n org.spearce.egit.ui/plugin.properties              |    3 +\n org.spearce.egit.ui/plugin.xml                     |   15 +\n .../src/org/spearce/egit/ui/UIIcons.java           |   25 +\n .../src/org/spearce/egit/ui/UIText.java            |  398 ++++-\n .../egit/ui/internal/actions/PushAction.java       |   47 +\n .../ui/internal/clone/BranchChangeListener.java    |   13 -\n .../ui/internal/clone/CloneDestinationPage.java    |  165 ++-\n .../egit/ui/internal/clone/CloneSourcePage.java    |  460 -----\n .../egit/ui/internal/clone/GitCloneWizard.java     |   92 +-\n .../egit/ui/internal/clone/SourceBranchPage.java   |  327 ++--\n .../ui/internal/components/BaseWizardPage.java     |   58 +\n .../components/CenteredImageLabelProvider.java     |   52 +\n .../internal/components/CheckboxLabelProvider.java |  138 ++\n .../internal/components/ClickableCellEditor.java   |   68 +\n .../internal/components/ComboLabelingSupport.java  |   77 +\n .../ui/internal/components/RefContentProposal.java |  140 ++\n .../egit/ui/internal/components/RefSpecPage.java   |  237 +++\n .../egit/ui/internal/components/RefSpecPanel.java  | 1823 ++++++++++++++++++++\n .../internal/components/RepositorySelection.java   |  133 ++\n .../components/RepositorySelectionPage.java        |  663 +++++++\n .../SelectionChangeListener.java}                  |   19 +-\n .../egit/ui/internal/push/ConfirmationPage.java    |  211 +++\n .../egit/ui/internal/push/PushResultTable.java     |  327 ++++\n .../spearce/egit/ui/internal/push/PushWizard.java  |  250 +++\n .../ui/internal/push/RefUpdateContentProvider.java |   62 +\n .../egit/ui/internal/push/RefUpdateElement.java    |   67 +\n .../egit/ui/internal/push/ResultDialog.java        |   65 +\n .../src/org/spearce/egit/ui/uitext.properties      |  163 ++-\n .../src/org/spearce/jgit/pgm/Fetch.java            |   10 +-\n .../src/org/spearce/jgit/pgm/Push.java             |   55 +-\n .../src/org/spearce/jgit/pgm/TextBuiltin.java      |    4 -\n .../src/org/spearce/jgit/lib/AnyObjectId.java      |   15 +\n .../src/org/spearce/jgit/lib/GitIndex.java         |    3 -\n .../src/org/spearce/jgit/lib/Repository.java       |   64 +-\n .../src/org/spearce/jgit/lib/RepositoryConfig.java |   21 +\n .../org/spearce/jgit/transport/PushProcess.java    |   18 +-\n .../src/org/spearce/jgit/transport/RefSpec.java    |   29 +-\n .../org/spearce/jgit/transport/RemoteConfig.java   |   50 +\n .../spearce/jgit/transport/RemoteRefUpdate.java    |   40 +-\n .../src/org/spearce/jgit/transport/Transport.java  |  283 +++-\n 55 files changed, 6471 insertions(+), 981 deletions(-)\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/op/ListRemoteOperation.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperation.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperationResult.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperationSpecification.java\n create mode 100644 org.spearce.egit.ui/icons/checkboxes/disabled_checked.gif\n create mode 100644 org.spearce.egit.ui/icons/checkboxes/disabled_unchecked.gif\n create mode 100644 org.spearce.egit.ui/icons/checkboxes/enabled_checked.gif\n create mode 100644 org.spearce.egit.ui/icons/checkboxes/enabled_unchecked.gif\n create mode 100644 org.spearce.egit.ui/icons/elcl16/add.gif\n create mode 100644 org.spearce.egit.ui/icons/elcl16/clear.gif\n create mode 100644 org.spearce.egit.ui/icons/elcl16/delete.gif\n create mode 100644 org.spearce.egit.ui/icons/elcl16/trash.gif\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/PushAction.java\n delete mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/BranchChangeListener.java\n delete mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneSourcePage.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/BaseWizardPage.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/CenteredImageLabelProvider.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/CheckboxLabelProvider.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/ClickableCellEditor.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/ComboLabelingSupport.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefContentProposal.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPanel.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelection.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionPage.java\n rename org.spearce.egit.ui/src/org/spearce/egit/ui/internal/{clone/URIishChangeListener.java => components/SelectionChangeListener.java} (51%)\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/ConfirmationPage.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/PushResultTable.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/PushWizard.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/RefUpdateContentProvider.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/RefUpdateElement.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/ResultDialog.java\n"},{"id":"87463","messageId":"1219005852-21496-2-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-1-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 01/31] Fix Repository.mapObject() for missing objects","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:42Z","receivedAt":"2008-08-17T20:43:42Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"When object doesn't exist, instead of returning null as stated in javadoc\nthis method was throwing NullPointerException. Now it returns null.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/lib/Repository.java       |    5 ++++-\n 1 files changed, 4 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex 7679e53..a8591cc 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -385,6 +385,8 @@ public class Repository {\n \t */\n \tpublic Object mapObject(final ObjectId id, final String refName) throws IOException {\n \t\tfinal ObjectLoader or = openObject(id);\n+\t\tif (or == null)\n+\t\t\treturn null;\n \t\tfinal byte[] raw = or.getBytes();\n \t\tif (or.getType() == Constants.OBJ_TREE)\n \t\t\treturn makeTree(id, raw);\n@@ -394,7 +396,8 @@ public class Repository {\n \t\t\treturn makeTag(id, refName, raw);\n \t\tif (or.getType() == Constants.OBJ_BLOB)\n \t\t\treturn raw;\n-\t\treturn null;\n+\t\tthrow new IncorrectObjectTypeException(id,\n+\t\t\t\t\"COMMIT nor TREE nor BLOB nor TAG\");\n \t}\n \n \t/**\n-- \n1.5.6.3\n"},{"id":"87465","messageId":"1219005852-21496-3-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-2-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 02/31] Fix Repository isValidRefName() for empty names","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:43Z","receivedAt":"2008-08-17T20:43:43Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Empty name is obviously invalid - method should return false.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/lib/Repository.java       |    3 +++\n 1 files changed, 3 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex a8591cc..17cdb40 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -986,6 +986,9 @@ public class Repository {\n \t */\n \tpublic static boolean isValidRefName(final String refName) {\n \t\tfinal int len = refName.length();\n+\t\tif (len == 0)\n+\t\t\treturn false;\n+\t\t\n \t\tchar p = '\\0';\n \t\tfor (int i=0; i<len; ++i) {\n \t\t\tchar c = refName.charAt(i);\n-- \n1.5.6.3\n"},{"id":"87467","messageId":"1219005852-21496-4-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-3-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 03/31] Fix Repository.resolve() to not throw runtime exceptions","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:44Z","receivedAt":"2008-08-17T20:43:44Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"resolve() was throwing undocumented runtime exceptions.\nArrayIndexOutOfBoundsException was thrown when it couldn't find commit's\nparents and NumberFormatException when it couldn't parse parents number.\n\nNow it returns null when it can't find appropriate commit's parents\nand (already checked) RevisionSyntaxException when it can't parse number.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/lib/Repository.java       |   56 ++++++++++++++++----\n 1 files changed, 45 insertions(+), 11 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex 17cdb40..756e3b9 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -569,9 +569,22 @@ public class Repository {\n \t\t\t\t\t\t\t\tbreak;\n \t\t\t\t\t\t}\n \t\t\t\t\t\tString parentnum = new String(rev, i+1, j-i-1);\n-\t\t\t\t\t\tint pnum = Integer.parseInt(parentnum);\n-\t\t\t\t\t\tif (pnum != 0)\n-\t\t\t\t\t\t\trefId = ((Commit)ref).getParentIds()[pnum - 1];\n+\t\t\t\t\t\tint pnum;\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tpnum = Integer.parseInt(parentnum);\n+\t\t\t\t\t\t} catch (NumberFormatException e) {\n+\t\t\t\t\t\t\tthrow new RevisionSyntaxException(\n+\t\t\t\t\t\t\t\t\t\"Invalid commit parent number\",\n+\t\t\t\t\t\t\t\t\trevstr);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tif (pnum != 0) {\n+\t\t\t\t\t\t\tfinal ObjectId parents[] = ((Commit) ref)\n+\t\t\t\t\t\t\t\t\t.getParentIds();\n+\t\t\t\t\t\t\tif (pnum > parents.length)\n+\t\t\t\t\t\t\t\trefId = null;\n+\t\t\t\t\t\t\telse\n+\t\t\t\t\t\t\t\trefId = parents[pnum - 1];\n+\t\t\t\t\t\t}\n \t\t\t\t\t\ti = j - 1;\n \t\t\t\t\t\tbreak;\n \t\t\t\t\tcase '{':\n@@ -632,17 +645,27 @@ public class Repository {\n \t\t\t\t\t\tbreak;\n \t\t\t\t\tdefault:\n \t\t\t\t\t\tref = mapObject(refId, null);\n-\t\t\t\t\t\tif (ref instanceof Commit)\n-\t\t\t\t\t\t\trefId = ((Commit)ref).getParentIds()[0];\n-\t\t\t\t\t\telse\n+\t\t\t\t\t\tif (ref instanceof Commit) {\n+\t\t\t\t\t\t\tfinal ObjectId parents[] = ((Commit) ref)\n+\t\t\t\t\t\t\t\t\t.getParentIds();\n+\t\t\t\t\t\t\tif (parents.length == 0)\n+\t\t\t\t\t\t\t\trefId = null;\n+\t\t\t\t\t\t\telse\n+\t\t\t\t\t\t\t\trefId = parents[0];\n+\t\t\t\t\t\t} else\n \t\t\t\t\t\t\tthrow new IncorrectObjectTypeException(refId,  Constants.TYPE_COMMIT);\n \t\t\t\t\t\t\n \t\t\t\t\t}\n \t\t\t\t} else {\n \t\t\t\t\tref = mapObject(refId, null);\n-\t\t\t\t\tif (ref instanceof Commit)\n-\t\t\t\t\t\trefId = ((Commit)ref).getParentIds()[0];\n-\t\t\t\t\telse\n+\t\t\t\t\tif (ref instanceof Commit) {\n+\t\t\t\t\t\tfinal ObjectId parents[] = ((Commit) ref)\n+\t\t\t\t\t\t\t\t.getParentIds();\n+\t\t\t\t\t\tif (parents.length == 0)\n+\t\t\t\t\t\t\trefId = null;\n+\t\t\t\t\t\telse\n+\t\t\t\t\t\t\trefId = parents[0];\n+\t\t\t\t\t} else\n \t\t\t\t\t\tthrow new IncorrectObjectTypeException(refId,  Constants.TYPE_COMMIT);\n \t\t\t\t}\n \t\t\t\tbreak;\n@@ -658,9 +681,20 @@ public class Repository {\n \t\t\t\t\t\tbreak;\n \t\t\t\t}\n \t\t\t\tString distnum = new String(rev, i+1, l-i-1);\n-\t\t\t\tint dist = Integer.parseInt(distnum);\n+\t\t\t\tint dist;\n+\t\t\t\ttry {\n+\t\t\t\t\tdist = Integer.parseInt(distnum);\n+\t\t\t\t} catch (NumberFormatException e) {\n+\t\t\t\t\tthrow new RevisionSyntaxException(\n+\t\t\t\t\t\t\t\"Invalid ancestry length\", revstr);\n+\t\t\t\t}\n \t\t\t\twhile (dist >= 0) {\n-\t\t\t\t\trefId = ((Commit)ref).getParentIds()[0];\n+\t\t\t\t\tfinal ObjectId[] parents = ((Commit) ref).getParentIds();\n+\t\t\t\t\tif (parents.length == 0) {\n+\t\t\t\t\t\trefId = null;\n+\t\t\t\t\t\tbreak;\n+\t\t\t\t\t}\n+\t\t\t\t\trefId = parents[0];\n \t\t\t\t\tref = mapCommit(refId);\n \t\t\t\t\t--dist;\n \t\t\t\t}\n-- \n1.5.6.3\n"},{"id":"87466","messageId":"1219005852-21496-5-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-4-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 04/31] Document/fix Transport open method for specific case","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:45Z","receivedAt":"2008-08-17T20:43:45Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"RemoteConfig may have empty URIs list.\nIt would be nicer to throw documented exception than\nArrayIndexOutOfBoundsException in this case.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/transport/Transport.java  |    7 +++++++\n 1 files changed, 7 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\nindex 5bec4d2..30175e3 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n@@ -103,9 +103,16 @@ public abstract class Transport {\n \t * @return the new transport instance. Never null.\n \t * @throws NotSupportedException\n \t *             the protocol specified is not supported.\n+\t * @throws IllegalArgumentException\n+\t *             if provided remote configuration doesn't have any URI\n+\t *             associated.\n \t */\n \tpublic static Transport open(final Repository local, final RemoteConfig cfg)\n \t\t\tthrows NotSupportedException {\n+\t\tif (cfg.getURIs().isEmpty())\n+\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\"Remote config \\\"\"\n+\t\t\t\t\t+ cfg.getName() + \"\\\" has no URIs associated\");\n \t\tfinal Transport tn = open(local, cfg.getURIs().get(0));\n \t\ttn.setOptionUploadPack(cfg.getUploadPack());\n \t\ttn.fetch = cfg.getFetchRefSpecs();\n-- \n1.5.6.3\n"},{"id":"87468","messageId":"1219005852-21496-6-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-5-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 05/31] Fix RefSpec javadoc regarding spec expanding","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:46Z","receivedAt":"2008-08-17T20:43:46Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"We can give expandSource() a wildcard as argument, so it will produce\nnew wildcard spec, which is still correct.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/transport/RefSpec.java    |   14 ++++++++++----\n 1 files changed, 10 insertions(+), 4 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RefSpec.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RefSpec.java\nindex 25d5977..e489233 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RefSpec.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RefSpec.java\n@@ -331,8 +331,11 @@ public class RefSpec {\n \t * otherwise expansion results may be unpredictable.\n \t *\n \t * @param r\n-\t *            a ref name that matched our source specification.\n-\t * @return a new specification that is not a wildcard.\n+\t *            a ref name that matched our source specification. Could be a\n+\t *            wildcard also.\n+\t * @return a new specification expanded from provided ref name. Result\n+\t *         specification is wildcard if and only if provided ref name is\n+\t *         wildcard.\n \t */\n \tpublic RefSpec expandFromSource(final String r) {\n \t\treturn isWildcard() ? new RefSpec(this, r) : this;\n@@ -345,8 +348,11 @@ public class RefSpec {\n \t * otherwise expansion results may be unpredictable.\n \t * \n \t * @param r\n-\t *            a ref that matched our source specification.\n-\t * @return a new specification that is not a wildcard.\n+\t *            a ref that matched our source specification. Could be a\n+\t *            wildcard also.\n+\t * @return a new specification expanded from provided ref name. Result\n+\t *         specification is wildcard if and only if provided ref name is\n+\t *         wildcard.\n \t */\n \tpublic RefSpec expandFromSource(final Ref r) {\n \t\treturn isWildcard() ? new RefSpec(this, r.getName()) : this;\n-- \n1.5.6.3\n"},{"id":"87471","messageId":"1219005852-21496-7-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-6-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 06/31] Make wildcard checking public in RefSpec","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:47Z","receivedAt":"2008-08-17T20:43:47Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"This constant and method can/should be reused in GUI.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/transport/RefSpec.java    |   15 +++++++++++++--\n 1 files changed, 13 insertions(+), 2 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RefSpec.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RefSpec.java\nindex e489233..9ec5847 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RefSpec.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RefSpec.java\n@@ -47,9 +47,20 @@ import org.spearce.jgit.lib.Ref;\n  * reference in one repository to another reference in another repository.\n  */\n public class RefSpec {\n-\tprivate static final String WILDCARD_SUFFIX = \"/*\";\n+\t/**\n+\t * Suffix for wildcard ref spec component, that indicate matching all refs\n+\t * with specified prefix.\n+\t */\n+\tpublic static final String WILDCARD_SUFFIX = \"/*\";\n \n-\tprivate static boolean isWildcard(final String s) {\n+\t/**\n+\t * Check whether provided string is a wildcard ref spec component.\n+\t * \n+\t * @param s\n+\t *            ref spec component - string to test. Can be null.\n+\t * @return true if provided string is a wildcard ref spec component.\n+\t */\n+\tpublic static boolean isWildcard(final String s) {\n \t\treturn s != null && s.endsWith(WILDCARD_SUFFIX);\n \t}\n \n-- \n1.5.6.3\n"},{"id":"87469","messageId":"1219005852-21496-8-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-7-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 07/31] Add openAll() and applyConfig() methods to Transport","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:48Z","receivedAt":"2008-08-17T20:43:48Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"openAll() method honours many URIs in remote configuration when opening\ntransports. Old open() calls remained, they still open only 1 transport\nat time.\n\nopenAll() is used during push operation - pgm.Push implementation was\nfixed to use it.\n\napplyConfig() is used internally here, but could be interesting for\nclients and it's safe to call it.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/pgm/Push.java             |   44 ++++++-----\n .../src/org/spearce/jgit/transport/Transport.java  |   83 ++++++++++++++++++--\n 2 files changed, 99 insertions(+), 28 deletions(-)\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\nindex 6b35ab8..a952309 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n@@ -86,7 +86,7 @@ class Push extends TextBuiltin {\n \t@Option(name = \"--receive-pack\", metaVar = \"path\")\n \tprivate String receivePack;\n \n-\tprivate boolean first = true;\n+\tprivate boolean shownURI;\n \n \t@Override\n \tprotected void run() throws Exception {\n@@ -97,27 +97,31 @@ class Push extends TextBuiltin {\n \t\t\t\trefSpecs.add(spec.setForceUpdate(true));\n \t\t}\n \n-\t\tfinal Transport transport = Transport.open(db, remote);\n-\t\ttransport.setPushThin(thin);\n-\t\tif (receivePack != null)\n-\t\t\ttransport.setOptionReceivePack(receivePack);\n-\t\tfinal Collection<RemoteRefUpdate> toPush = transport\n-\t\t\t\t.findRemoteRefUpdatesFor(refSpecs);\n+\t\tfinal List<Transport> transports = Transport.openAll(db, remote);\n+\t\tfor (final Transport transport : transports) {\n+\t\t\ttransport.setPushThin(thin);\n+\t\t\tif (receivePack != null)\n+\t\t\t\ttransport.setOptionReceivePack(receivePack);\n \n-\t\tfinal PushResult result = transport.push(new TextProgressMonitor(),\n-\t\t\t\ttoPush);\n-\t\ttransport.close();\n+\t\t\tfinal Collection<RemoteRefUpdate> toPush = transport\n+\t\t\t\t\t.findRemoteRefUpdatesFor(refSpecs);\n \n-\t\tprintPushResult(result);\n+\t\t\tfinal PushResult result = transport.push(new TextProgressMonitor(),\n+\t\t\t\t\ttoPush);\n+\t\t\tprintPushResult(transport, result);\n+\t\t}\n \t}\n \n-\tprivate void printPushResult(final PushResult result) {\n+\tprivate void printPushResult(final Transport transport,\n+\t\t\tfinal PushResult result) {\n+\t\tshownURI = false;\n \t\tboolean everythingUpToDate = true;\n+\n \t\t// at first, print up-to-date ones...\n \t\tfor (final RemoteRefUpdate rru : result.getRemoteUpdates()) {\n \t\t\tif (rru.getStatus() == Status.UP_TO_DATE) {\n \t\t\t\tif (verbose)\n-\t\t\t\t\tprintRefUpdateResult(result, rru);\n+\t\t\t\t\tprintRefUpdateResult(transport, result, rru);\n \t\t\t} else\n \t\t\t\teverythingUpToDate = false;\n \t\t}\n@@ -125,25 +129,25 @@ class Push extends TextBuiltin {\n \t\tfor (final RemoteRefUpdate rru : result.getRemoteUpdates()) {\n \t\t\t// ...then successful updates...\n \t\t\tif (rru.getStatus() == Status.OK)\n-\t\t\t\tprintRefUpdateResult(result, rru);\n+\t\t\t\tprintRefUpdateResult(transport, result, rru);\n \t\t}\n \n \t\tfor (final RemoteRefUpdate rru : result.getRemoteUpdates()) {\n \t\t\t// ...finally, others (problematic)\n \t\t\tif (rru.getStatus() != Status.OK\n \t\t\t\t\t&& rru.getStatus() != Status.UP_TO_DATE)\n-\t\t\t\tprintRefUpdateResult(result, rru);\n+\t\t\t\tprintRefUpdateResult(transport, result, rru);\n \t\t}\n \n \t\tif (everythingUpToDate)\n \t\t\tout.println(\"Everything up-to-date\");\n \t}\n \n-\tprivate void printRefUpdateResult(final PushResult result,\n-\t\t\tfinal RemoteRefUpdate rru) {\n-\t\tif (first) {\n-\t\t\tfirst = false;\n-\t\t\tout.format(\"To %s\\n\", result.getURI());\n+\tprivate void printRefUpdateResult(final Transport transport,\n+\t\t\tfinal PushResult result, final RemoteRefUpdate rru) {\n+\t\tif (!shownURI) {\n+\t\t\tshownURI = true;\n+\t\t\tout.format(\"To %s\\n\", transport.getURI());\n \t\t}\n \n \t\tfinal String remoteName = rru.getRemoteName();\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\nindex 30175e3..73aa771 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n@@ -75,8 +75,10 @@ public abstract class Transport {\n \t * @param local\n \t *            existing local repository.\n \t * @param remote\n-\t *            location of the remote repository.\n-\t * @return the new transport instance. Never null.\n+\t *            location of the remote repository - may be URI or remote\n+\t *            configuration name.\n+\t * @return the new transport instance. Never null. In case of multiple URIs\n+\t *         in remote configuration, only the first is chosen.\n \t * @throws URISyntaxException\n \t *             the location is not a remote defined in the configuration\n \t *             file and is not a well-formed URL.\n@@ -93,6 +95,35 @@ public abstract class Transport {\n \t}\n \n \t/**\n+\t * Open new transport instances to connect two repositories.\n+\t * \n+\t * @param local\n+\t *            existing local repository.\n+\t * @param remote\n+\t *            location of the remote repository - may be URI or remote\n+\t *            configuration name.\n+\t * @return the list of new transport instances for every URI in remote\n+\t *         configuration.\n+\t * @throws URISyntaxException\n+\t *             the location is not a remote defined in the configuration\n+\t *             file and is not a well-formed URL.\n+\t * @throws NotSupportedException\n+\t *             the protocol specified is not supported.\n+\t */\n+\tpublic static List<Transport> openAll(final Repository local,\n+\t\t\tfinal String remote) throws NotSupportedException,\n+\t\t\tURISyntaxException {\n+\t\tfinal RemoteConfig cfg = new RemoteConfig(local.getConfig(), remote);\n+\t\tfinal List<URIish> uris = cfg.getURIs();\n+\t\tif (uris.size() == 0) {\n+\t\t\tfinal ArrayList<Transport> transports = new ArrayList<Transport>(1);\n+\t\t\ttransports.add(open(local, new URIish(remote)));\n+\t\t\treturn transports;\n+\t\t}\n+\t\treturn openAll(local, cfg);\n+\t}\n+\n+\t/**\n \t * Open a new transport instance to connect two repositories.\n \t * \n \t * @param local\n@@ -100,7 +131,8 @@ public abstract class Transport {\n \t * @param cfg\n \t *            configuration describing how to connect to the remote\n \t *            repository.\n-\t * @return the new transport instance. Never null.\n+\t * @return the new transport instance. Never null. In case of multiple URIs\n+\t *         in remote configuration, only the first is chosen.\n \t * @throws NotSupportedException\n \t *             the protocol specified is not supported.\n \t * @throws IllegalArgumentException\n@@ -114,15 +146,36 @@ public abstract class Transport {\n \t\t\t\t\t\"Remote config \\\"\"\n \t\t\t\t\t+ cfg.getName() + \"\\\" has no URIs associated\");\n \t\tfinal Transport tn = open(local, cfg.getURIs().get(0));\n-\t\ttn.setOptionUploadPack(cfg.getUploadPack());\n-\t\ttn.fetch = cfg.getFetchRefSpecs();\n-\t\ttn.tagopt = cfg.getTagOpt();\n-\t\ttn.setOptionReceivePack(cfg.getReceivePack());\n-\t\ttn.push = cfg.getPushRefSpecs();\n+\t\ttn.applyConfig(cfg);\n \t\treturn tn;\n \t}\n \n \t/**\n+\t * Open new transport instances to connect two repositories.\n+\t * \n+\t * @param local\n+\t *            existing local repository.\n+\t * @param cfg\n+\t *            configuration describing how to connect to the remote\n+\t *            repository.\n+\t * @return the list of new transport instances for every URI in remote\n+\t *         configuration.\n+\t * @throws NotSupportedException\n+\t *             the protocol specified is not supported.\n+\t */\n+\tpublic static List<Transport> openAll(final Repository local,\n+\t\t\tfinal RemoteConfig cfg) throws NotSupportedException {\n+\t\tfinal List<URIish> uris = cfg.getURIs();\n+\t\tfinal List<Transport> transports = new ArrayList<Transport>(uris.size());\n+\t\tfor (final URIish uri : uris) {\n+\t\t\tfinal Transport tn = open(local, uri);\n+\t\t\ttn.applyConfig(cfg);\n+\t\t\ttransports.add(tn);\n+\t\t}\n+\t\treturn transports;\n+\t}\n+\n+\t/**\n \t * Open a new transport instance to connect two repositories.\n \t * \n \t * @param local\n@@ -357,6 +410,20 @@ public abstract class Transport {\n \tpublic void setPushThin(final boolean pushThin) {\n \t\tthis.pushThin = pushThin;\n \t}\n+\t\n+\t/**\n+\t * Apply provided remote configuration on this transport.\n+\t * \n+\t * @param cfg\n+\t *            configuration to apply on this transport.\n+\t */\n+\tpublic void applyConfig(final RemoteConfig cfg) {\n+\t\tsetOptionUploadPack(cfg.getUploadPack());\n+\t\tfetch = cfg.getFetchRefSpecs();\n+\t\tsetTagOpt(cfg.getTagOpt());\n+\t\toptionReceivePack = cfg.getReceivePack();\n+\t\tpush = cfg.getPushRefSpecs();\n+\t}\n \n \t/**\n \t * Fetch objects and refs from the remote repository to the local one.\n-- \n1.5.6.3\n"},{"id":"87472","messageId":"1219005852-21496-9-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-8-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 08/31] Add dryRun option to Transport and console push","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:49Z","receivedAt":"2008-08-17T20:43:49Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Implementation of C Git --dry-run behavior for push operation.\nIt allows investigating possible push result, while not performing real\npush operation - not updating remote refs.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/pgm/Push.java             |    4 ++\n .../org/spearce/jgit/transport/PushProcess.java    |   18 +++++++-\n .../src/org/spearce/jgit/transport/Transport.java  |   40 ++++++++++++++++++--\n 3 files changed, 55 insertions(+), 7 deletions(-)\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\nindex a952309..f5b24c6 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n@@ -85,6 +85,9 @@ class Push extends TextBuiltin {\n \n \t@Option(name = \"--receive-pack\", metaVar = \"path\")\n \tprivate String receivePack;\n+\t\n+\t@Option(name = \"--dry-run\")\n+\tprivate boolean dryRun = Transport.DEFAULT_DRY_RUN;\n \n \tprivate boolean shownURI;\n \n@@ -102,6 +105,7 @@ class Push extends TextBuiltin {\n \t\t\ttransport.setPushThin(thin);\n \t\t\tif (receivePack != null)\n \t\t\t\ttransport.setOptionReceivePack(receivePack);\n+\t\t\ttransport.setDryRun(dryRun);\n \n \t\t\tfinal Collection<RemoteRefUpdate> toPush = transport\n \t\t\t\t\t.findRemoteRefUpdatesFor(refSpecs);\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java\nindex 6a2176f..cafec05 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java\n@@ -100,7 +100,10 @@ class PushProcess {\n \t/**\n \t * Perform push operation between local and remote repository - set remote\n \t * refs appropriately, send needed objects and update local tracking refs.\n-\t *\n+\t * <p>\n+\t * When {@link Transport#isDryRun()} is true, result of this operation is\n+\t * just estimation of real operation result, no real action is performed.\n+\t * \n \t * @param monitor\n \t *            progress monitor used for feedback about operation.\n \t * @return result of push operation with complete status description.\n@@ -118,12 +121,15 @@ class PushProcess {\n \t\t\tmonitor.endTask();\n \n \t\t\tfinal Map<String, RemoteRefUpdate> preprocessed = prepareRemoteUpdates();\n-\t\t\tif (!preprocessed.isEmpty())\n+\t\t\tif (transport.isDryRun())\n+\t\t\t\tmodifyUpdatesForDryRun();\n+\t\t\telse if (!preprocessed.isEmpty())\n \t\t\t\tconnection.push(monitor, preprocessed);\n \t\t} finally {\n \t\t\tconnection.close();\n \t\t}\n-\t\tupdateTrackingRefs();\n+\t\tif (!transport.isDryRun())\n+\t\t\tupdateTrackingRefs();\n \t\treturn prepareOperationResult();\n \t}\n \n@@ -191,6 +197,12 @@ class PushProcess {\n \t\treturn result;\n \t}\n \n+\tprivate void modifyUpdatesForDryRun() {\n+\t\tfor (final RemoteRefUpdate rru : toPush.values())\n+\t\t\tif (rru.getStatus() == Status.NOT_ATTEMPTED)\n+\t\t\t\trru.setStatus(Status.OK);\n+\t}\n+\n \tprivate void updateTrackingRefs() {\n \t\tfor (final RemoteRefUpdate rru : toPush.values()) {\n \t\t\tfinal Status status = rru.getStatus();\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\nindex 73aa771..98853e6 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n@@ -236,6 +236,11 @@ public abstract class Transport {\n \tpublic static final RefSpec REFSPEC_PUSH_ALL = new RefSpec(\n \t\t\t\"refs/heads/*:refs/heads/*\");\n \n+\t/**\n+\t * Default setting for {@link #dryRun} option.\n+\t */\n+\tpublic static final boolean DEFAULT_DRY_RUN = false;\n+\n \t/** The repository this transport fetches into, or pushes out of. */\n \tprotected final Repository local;\n \n@@ -271,6 +276,9 @@ public abstract class Transport {\n \t/** Should push produce thin-pack when sending objects to remote repository. */\n \tprivate boolean pushThin = DEFAULT_PUSH_THIN;\n \n+\t/** Should push just check for operation result, not really push. */\n+\tprivate boolean dryRun = DEFAULT_DRY_RUN;\n+\n \t/**\n \t * Create a new transport instance.\n \t * \n@@ -426,6 +434,27 @@ public abstract class Transport {\n \t}\n \n \t/**\n+\t * @return true if push operation should just check for possible result and\n+\t *         not really update remote refs, false otherwise - when push should\n+\t *         act normally.\n+\t */\n+\tpublic boolean isDryRun() {\n+\t\treturn dryRun;\n+\t}\n+\n+\t/**\n+\t * Set dry run option for push operation.\n+\t * \n+\t * @param dryRun\n+\t *            true if push operation should just check for possible result\n+\t *            and not really update remote refs, false otherwise - when push\n+\t *            should act normally.\n+\t */\n+\tpublic void setDryRun(final boolean dryRun) {\n+\t\tthis.dryRun = dryRun;\n+\t}\n+\n+\t/**\n \t * Fetch objects and refs from the remote repository to the local one.\n \t * <p>\n \t * This is a utility function providing standard fetch behavior. Local\n@@ -495,10 +524,13 @@ public abstract class Transport {\n \t * operation result is provided after execution.\n \t * <p>\n \t * For setting up remote ref update specification from ref spec, see helper\n-\t * method {@link #findRemoteRefUpdatesFor(Collection)}, predefined refspecs ({@link #REFSPEC_TAGS},\n-\t * {@link #REFSPEC_PUSH_ALL}) or consider using directly\n-\t * {@link RemoteRefUpdate} for more possibilities.\n-\t *\n+\t * method {@link #findRemoteRefUpdatesFor(Collection)}, predefined refspecs\n+\t * ({@link #REFSPEC_TAGS}, {@link #REFSPEC_PUSH_ALL}) or consider using\n+\t * directly {@link RemoteRefUpdate} for more possibilities.\n+\t * <p>\n+\t * When {@link #isDryRun()} is true, result of this operation is just\n+\t * estimation of real operation result, no real action is performed.\n+\t * \n \t * @see RemoteRefUpdate\n \t *\n \t * @param monitor\n-- \n1.5.6.3\n"},{"id":"87470","messageId":"1219005852-21496-10-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-9-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 09/31] Extract Transport findRemoteRefUpdatesFor() as static method","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:50Z","receivedAt":"2008-08-17T20:43:50Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"This method could be used outside of specific URI scope, so let it be\nstatic. Otherwise, if someone want to generate remote ref updates from\nrefspecs he/she may have to create some dummy transport just for that.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/transport/Transport.java  |  147 ++++++++++++--------\n 1 files changed, 90 insertions(+), 57 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\nindex 98853e6..e986e48 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n@@ -211,7 +211,92 @@ public abstract class Transport {\n \n \t\tthrow new NotSupportedException(\"URI not supported: \" + remote);\n \t}\n+\t\n+\t/**\n+\t * Convert push remote refs update specification from {@link RefSpec} form\n+\t * to {@link RemoteRefUpdate}. Conversion expands wildcards by matching\n+\t * source part to local refs. expectedOldObjectId in RemoteRefUpdate is\n+\t * always set as null. Tracking branch is configured if RefSpec destination\n+\t * matches source of any fetch ref spec for this transport remote\n+\t * configuration.\n+\t * \n+\t * @param db\n+\t *            local database.\n+\t * @param specs\n+\t *            collection of RefSpec to convert.\n+\t * @param fetchSpecs\n+\t *            fetch specifications used for finding localtracking refs. May\n+\t *            be null or empty collection.\n+\t * @return collection of set up {@link RemoteRefUpdate}.\n+\t * @throws TransportException\n+\t *             when problem occurred during conversion or specification set\n+\t *             up: most probably, missing objects or refs.\n+\t */\n+\tpublic static Collection<RemoteRefUpdate> findRemoteRefUpdatesFor(\n+\t\t\tfinal Repository db, final Collection<RefSpec> specs,\n+\t\t\tCollection<RefSpec> fetchSpecs) throws TransportException {\n+\t\tif (fetchSpecs == null)\n+\t\t\tfetchSpecs = Collections.emptyList();\n+\t\tfinal List<RemoteRefUpdate> result = new LinkedList<RemoteRefUpdate>();\n+\t\tfinal Collection<RefSpec> procRefs = expandPushWildcardsFor(db, specs);\n+\n+\t\tfor (final RefSpec spec : procRefs) {\n+\t\t\ttry {\n+\t\t\t\tfinal String srcRef = spec.getSource();\n+\t\t\t\t// null destination (no-colon in ref-spec) is a special case\n+\t\t\t\tfinal String remoteName = (spec.getDestination() == null ? spec\n+\t\t\t\t\t\t.getSource() : spec.getDestination());\n+\t\t\t\tfinal boolean forceUpdate = spec.isForceUpdate();\n+\t\t\t\tfinal String localName = findTrackingRefName(remoteName,\n+\t\t\t\t\t\tfetchSpecs);\n+\n+\t\t\t\tfinal RemoteRefUpdate rru = new RemoteRefUpdate(db, srcRef,\n+\t\t\t\t\t\tremoteName, forceUpdate, localName, null);\n+\t\t\t\tresult.add(rru);\n+\t\t\t} catch (TransportException x) {\n+\t\t\t\tthrow x;\n+\t\t\t} catch (Exception x) {\n+\t\t\t\tthrow new TransportException(\n+\t\t\t\t\t\t\"Problem with resolving push ref spec \\\"\" + spec\n+\t\t\t\t\t\t\t\t+ \"\\\" locally: \" + x.getMessage(), x);\n+\t\t\t}\n+\t\t}\n+\t\treturn result;\n+\t}\n+\n+\tprivate static Collection<RefSpec> expandPushWildcardsFor(\n+\t\t\tfinal Repository db, final Collection<RefSpec> specs) {\n+\t\tfinal Map<String, Ref> localRefs = db.getAllRefs();\n+\t\tfinal Collection<RefSpec> procRefs = new HashSet<RefSpec>();\n \n+\t\tfor (final RefSpec spec : specs) {\n+\t\t\tif (spec.isWildcard()) {\n+\t\t\t\tfor (final Ref localRef : localRefs.values()) {\n+\t\t\t\t\tif (spec.matchSource(localRef))\n+\t\t\t\t\t\tprocRefs.add(spec.expandFromSource(localRef));\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\tprocRefs.add(spec);\n+\t\t\t}\n+\t\t}\n+\t\treturn procRefs;\n+\t}\n+\n+\tprivate static String findTrackingRefName(final String remoteName,\n+\t\t\tfinal Collection<RefSpec> fetchSpecs) {\n+\t\t// try to find matching tracking refs\n+\t\tfor (final RefSpec fetchSpec : fetchSpecs) {\n+\t\t\tif (fetchSpec.matchSource(remoteName)) {\n+\t\t\t\tif (fetchSpec.isWildcard())\n+\t\t\t\t\treturn fetchSpec.expandFromSource(remoteName)\n+\t\t\t\t\t\t\t.getDestination();\n+\t\t\t\telse\n+\t\t\t\t\treturn fetchSpec.getDestination();\n+\t\t\t}\n+\t\t}\n+\t\treturn null;\n+\t}\n+\t\n \t/**\n \t * Default setting for {@link #fetchThin} option.\n \t */\n@@ -573,7 +658,10 @@ public abstract class Transport {\n \t * always set as null. Tracking branch is configured if RefSpec destination\n \t * matches source of any fetch ref spec for this transport remote\n \t * configuration.\n-\t *\n+\t * <p>\n+\t * Conversion is performed for context of this transport (database, fetch\n+\t * specifications).\n+\t * \n \t * @param specs\n \t *            collection of RefSpec to convert.\n \t * @return collection of set up {@link RemoteRefUpdate}.\n@@ -583,30 +671,7 @@ public abstract class Transport {\n \t */\n \tpublic Collection<RemoteRefUpdate> findRemoteRefUpdatesFor(\n \t\t\tfinal Collection<RefSpec> specs) throws TransportException {\n-\t\tfinal List<RemoteRefUpdate> result = new LinkedList<RemoteRefUpdate>();\n-\t\tfinal Collection<RefSpec> procRefs = expandPushWildcardsFor(specs);\n-\n-\t\tfor (final RefSpec spec : procRefs) {\n-\t\t\ttry {\n-\t\t\t\tfinal String srcRef = spec.getSource();\n-\t\t\t\t// null destination (no-colon in ref-spec) is a special case\n-\t\t\t\tfinal String remoteName = (spec.getDestination() == null ? spec\n-\t\t\t\t\t\t.getSource() : spec.getDestination());\n-\t\t\t\tfinal boolean forceUpdate = spec.isForceUpdate();\n-\t\t\t\tfinal String localName = findTrackingRefName(remoteName);\n-\n-\t\t\t\tfinal RemoteRefUpdate rru = new RemoteRefUpdate(local, srcRef,\n-\t\t\t\t\t\tremoteName, forceUpdate, localName, null);\n-\t\t\t\tresult.add(rru);\n-\t\t\t} catch (TransportException x) {\n-\t\t\t\tthrow x;\n-\t\t\t} catch (Exception x) {\n-\t\t\t\tthrow new TransportException(\n-\t\t\t\t\t\t\"Problem with resolving push ref spec \\\"\" + spec\n-\t\t\t\t\t\t\t\t+ \"\\\" locally: \" + x.getMessage(), x);\n-\t\t\t}\n-\t\t}\n-\t\treturn result;\n+\t\treturn findRemoteRefUpdatesFor(local, specs, fetch);\n \t}\n \n \t/**\n@@ -642,36 +707,4 @@ public abstract class Transport {\n \t * any open file handles used to read the \"remote\" repository.\n \t */\n \tpublic abstract void close();\n-\n-\tprivate Collection<RefSpec> expandPushWildcardsFor(\n-\t\t\tfinal Collection<RefSpec> specs) {\n-\t\tfinal Map<String, Ref> localRefs = local.getAllRefs();\n-\t\tfinal Collection<RefSpec> procRefs = new HashSet<RefSpec>();\n-\n-\t\tfor (final RefSpec spec : specs) {\n-\t\t\tif (spec.isWildcard()) {\n-\t\t\t\tfor (final Ref localRef : localRefs.values()) {\n-\t\t\t\t\tif (spec.matchSource(localRef))\n-\t\t\t\t\t\tprocRefs.add(spec.expandFromSource(localRef));\n-\t\t\t\t}\n-\t\t\t} else {\n-\t\t\t\tprocRefs.add(spec);\n-\t\t\t}\n-\t\t}\n-\t\treturn procRefs;\n-\t}\n-\n-\tprivate String findTrackingRefName(final String remoteName) {\n-\t\t// try to find matching tracking refs\n-\t\tfor (final RefSpec fetchSpec : fetch) {\n-\t\t\tif (fetchSpec.matchSource(remoteName)) {\n-\t\t\t\tif (fetchSpec.isWildcard())\n-\t\t\t\t\treturn fetchSpec.expandFromSource(remoteName)\n-\t\t\t\t\t\t\t.getDestination();\n-\t\t\t\telse\n-\t\t\t\t\treturn fetchSpec.getDestination();\n-\t\t\t}\n-\t\t}\n-\t\treturn null;\n-\t}\n }\n-- \n1.5.6.3\n"},{"id":"87473","messageId":"1219005852-21496-11-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-10-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 10/31] Improve javadoc of Transport push()","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:51Z","receivedAt":"2008-08-17T20:43:51Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Let's state explicitly that remote ref updates are modified during this\ncall.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/transport/Transport.java  |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\nindex e986e48..8e42516 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n@@ -627,7 +627,7 @@ public abstract class Transport {\n \t *            collection to use the specifications from the RemoteConfig\n \t *            converted by {@link #findRemoteRefUpdatesFor(Collection)}. No\n \t *            more than 1 RemoteRefUpdate with the same remoteName is\n-\t *            allowed.\n+\t *            allowed. These objects are modified during this call.\n \t * @return information about results of remote refs updates, tracking refs\n \t *         updates and refs advertised by remote repository.\n \t * @throws NotSupportedException\n-- \n1.5.6.3\n"},{"id":"87474","messageId":"1219005852-21496-12-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-11-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 11/31] Clean up exception issues in RemoteRefUpdate","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:52Z","receivedAt":"2008-08-17T20:43:52Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"IllegalArgumentException was probably a wrong choice for exception\nthrown in RemoteRefUpdate when src object can't be resolved - it should\nbe checked one.\n\nNow it's IOException, which makes call more safe for external clients.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../spearce/jgit/transport/RemoteRefUpdate.java    |   12 +++---\n .../src/org/spearce/jgit/transport/Transport.java  |   48 +++++++++----------\n 2 files changed, 29 insertions(+), 31 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java\nindex 7db6c55..5afb8a4 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java\n@@ -166,23 +166,23 @@ public class RemoteRefUpdate {\n \t *            remote ref with this name.\n \t * @throws IOException\n \t *             when I/O error occurred during creating\n-\t *             {@link TrackingRefUpdate} for local tracking branch.\n+\t *             {@link TrackingRefUpdate} for local tracking branch or srcRef\n+\t *             can't be resolved to any object.\n \t * @throws IllegalArgumentException\n-\t *             if some required parameter was null or srcRef can't be\n-\t *             resolved to any object.\n+\t *             if some required parameter was null\n \t */\n \tpublic RemoteRefUpdate(final Repository db, final String srcRef,\n \t\t\tfinal String remoteName, final boolean forceUpdate,\n \t\t\tfinal String localName, final ObjectId expectedOldObjectId)\n \t\t\tthrows IOException {\n \t\tif (remoteName == null)\n-\t\t\tthrow new IllegalArgumentException(\"remote name can't be null\");\n+\t\t\tthrow new IllegalArgumentException(\"Remote name can't be null.\");\n \t\tthis.srcRef = srcRef;\n \t\tthis.newObjectId = (srcRef == null ? ObjectId.zeroId() : db\n \t\t\t\t.resolve(srcRef));\n \t\tif (newObjectId == null)\n-\t\t\tthrow new IllegalArgumentException(\n-\t\t\t\t\t\"source ref doesn't resolve to any object\");\n+\t\t\tthrow new IOException(\"Source ref \" + srcRef\n+\t\t\t\t\t+ \" doesn't resolve to any object.\");\n \t\tthis.remoteName = remoteName;\n \t\tthis.forceUpdate = forceUpdate;\n \t\tif (localName != null && db != null)\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\nindex 8e42516..939347e 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n@@ -39,6 +39,7 @@\n \n package org.spearce.jgit.transport;\n \n+import java.io.IOException;\n import java.net.URISyntaxException;\n import java.util.ArrayList;\n import java.util.Collection;\n@@ -211,7 +212,7 @@ public abstract class Transport {\n \n \t\tthrow new NotSupportedException(\"URI not supported: \" + remote);\n \t}\n-\t\n+\n \t/**\n \t * Convert push remote refs update specification from {@link RefSpec} form\n \t * to {@link RemoteRefUpdate}. Conversion expands wildcards by matching\n@@ -228,38 +229,29 @@ public abstract class Transport {\n \t *            fetch specifications used for finding localtracking refs. May\n \t *            be null or empty collection.\n \t * @return collection of set up {@link RemoteRefUpdate}.\n-\t * @throws TransportException\n+\t * @throws IOException\n \t *             when problem occurred during conversion or specification set\n \t *             up: most probably, missing objects or refs.\n \t */\n \tpublic static Collection<RemoteRefUpdate> findRemoteRefUpdatesFor(\n \t\t\tfinal Repository db, final Collection<RefSpec> specs,\n-\t\t\tCollection<RefSpec> fetchSpecs) throws TransportException {\n+\t\t\tCollection<RefSpec> fetchSpecs) throws IOException {\n \t\tif (fetchSpecs == null)\n \t\t\tfetchSpecs = Collections.emptyList();\n \t\tfinal List<RemoteRefUpdate> result = new LinkedList<RemoteRefUpdate>();\n \t\tfinal Collection<RefSpec> procRefs = expandPushWildcardsFor(db, specs);\n \n \t\tfor (final RefSpec spec : procRefs) {\n-\t\t\ttry {\n-\t\t\t\tfinal String srcRef = spec.getSource();\n-\t\t\t\t// null destination (no-colon in ref-spec) is a special case\n-\t\t\t\tfinal String remoteName = (spec.getDestination() == null ? spec\n-\t\t\t\t\t\t.getSource() : spec.getDestination());\n-\t\t\t\tfinal boolean forceUpdate = spec.isForceUpdate();\n-\t\t\t\tfinal String localName = findTrackingRefName(remoteName,\n-\t\t\t\t\t\tfetchSpecs);\n-\n-\t\t\t\tfinal RemoteRefUpdate rru = new RemoteRefUpdate(db, srcRef,\n-\t\t\t\t\t\tremoteName, forceUpdate, localName, null);\n-\t\t\t\tresult.add(rru);\n-\t\t\t} catch (TransportException x) {\n-\t\t\t\tthrow x;\n-\t\t\t} catch (Exception x) {\n-\t\t\t\tthrow new TransportException(\n-\t\t\t\t\t\t\"Problem with resolving push ref spec \\\"\" + spec\n-\t\t\t\t\t\t\t\t+ \"\\\" locally: \" + x.getMessage(), x);\n-\t\t\t}\n+\t\t\tfinal String srcRef = spec.getSource();\n+\t\t\t// null destination (no-colon in ref-spec) is a special case\n+\t\t\tfinal String remoteName = (spec.getDestination() == null ? spec\n+\t\t\t\t\t.getSource() : spec.getDestination());\n+\t\t\tfinal boolean forceUpdate = spec.isForceUpdate();\n+\t\t\tfinal String localName = findTrackingRefName(remoteName, fetchSpecs);\n+\n+\t\t\tfinal RemoteRefUpdate rru = new RemoteRefUpdate(db, srcRef,\n+\t\t\t\t\tremoteName, forceUpdate, localName, null);\n+\t\t\tresult.add(rru);\n \t\t}\n \t\treturn result;\n \t}\n@@ -643,7 +635,13 @@ public abstract class Transport {\n \t\t\tTransportException {\n \t\tif (toPush == null || toPush.isEmpty()) {\n \t\t\t// If the caller did not ask for anything use the defaults.\n-\t\t\ttoPush = findRemoteRefUpdatesFor(push);\n+\t\t\ttry {\n+\t\t\t\ttoPush = findRemoteRefUpdatesFor(push);\n+\t\t\t} catch (final IOException e) {\n+\t\t\t\tthrow new TransportException(\n+\t\t\t\t\t\t\"Problem with resolving push ref specs locally: \"\n+\t\t\t\t\t\t\t\t+ e.getMessage(), e);\n+\t\t\t}\n \t\t\tif (toPush.isEmpty())\n \t\t\t\tthrow new TransportException(\"Nothing to push.\");\n \t\t}\n@@ -665,12 +663,12 @@ public abstract class Transport {\n \t * @param specs\n \t *            collection of RefSpec to convert.\n \t * @return collection of set up {@link RemoteRefUpdate}.\n-\t * @throws TransportException\n+\t * @throws IOException\n \t *             when problem occurred during conversion or specification set\n \t *             up: most probably, missing objects or refs.\n \t */\n \tpublic Collection<RemoteRefUpdate> findRemoteRefUpdatesFor(\n-\t\t\tfinal Collection<RefSpec> specs) throws TransportException {\n+\t\t\tfinal Collection<RefSpec> specs) throws IOException {\n \t\treturn findRemoteRefUpdatesFor(local, specs, fetch);\n \t}\n \n-- \n1.5.6.3\n"},{"id":"87479","messageId":"1219005852-21496-13-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-12-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 12/31] Add another RemoteRefUpdate constructor, useful for 2-stage push","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:53Z","receivedAt":"2008-08-17T20:43:53Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"New constructor base on existing RemoteRefUpdate instance, providing\ndeep copy of object, but allowing change of expectedOldObjectId.\n\nIt may be useful for copying ref updates during one-to-many push or\n2-stage push, with first 1st step being dry run, 2nd being actual push.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../spearce/jgit/transport/RemoteRefUpdate.java    |   28 +++++++++++++++++++-\n 1 files changed, 27 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java\nindex 5afb8a4..42588c1 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java\n@@ -123,7 +123,7 @@ public class RemoteRefUpdate {\n \n \tprivate final TrackingRefUpdate trackingRefUpdate;\n \n-\tprivate String srcRef;\n+\tprivate final String srcRef;\n \n \tprivate final boolean forceUpdate;\n \n@@ -133,6 +133,8 @@ public class RemoteRefUpdate {\n \n \tprivate String message;\n \n+\tprivate final Repository db;\n+\n \t/**\n \t * Construct remote ref update request by providing an update specification.\n \t * Object is created with default {@link Status#NOT_ATTEMPTED} status and no\n@@ -190,11 +192,35 @@ public class RemoteRefUpdate {\n \t\t\t\t\tremoteName, forceUpdate, newObjectId, \"push\");\n \t\telse\n \t\t\ttrackingRefUpdate = null;\n+\t\tthis.db = db;\n \t\tthis.expectedOldObjectId = expectedOldObjectId;\n \t\tthis.status = Status.NOT_ATTEMPTED;\n \t}\n \n \t/**\n+\t * Create a new instance of this object basing on existing instance for\n+\t * configuration. State (like {@link #getMessage()}, {@link #getStatus()})\n+\t * of base object is not shared. Expected old object id is set up from\n+\t * scratch, as this constructor may be used for 2-stage push: first one\n+\t * being dry run, second one being actual push.\n+\t * \n+\t * @param base\n+\t *            configuration base.\n+\t * @param newExpectedOldObjectId\n+\t *            new expected object id value.\n+\t * @throws IOException\n+\t *             when I/O error occurred during creating\n+\t *             {@link TrackingRefUpdate} for local tracking branch or srcRef\n+\t *             of base object no longer can be resolved to any object.\n+\t */\n+\tpublic RemoteRefUpdate(final RemoteRefUpdate base,\n+\t\t\tfinal ObjectId newExpectedOldObjectId) throws IOException {\n+\t\tthis(base.db, base.srcRef, base.remoteName, base.forceUpdate,\n+\t\t\t\t(base.trackingRefUpdate == null ? null : base.trackingRefUpdate\n+\t\t\t\t\t\t.getLocalName()), newExpectedOldObjectId);\n+\t}\n+\n+\t/**\n \t * @return expectedOldObjectId required to be advertised by remote side, as\n \t *         set in constructor; may be null.\n \t */\n-- \n1.5.6.3\n"},{"id":"87477","messageId":"1219005852-21496-14-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-13-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 13/31] Add getAllRemoteConfigs() to RemoteConfig","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:54Z","receivedAt":"2008-08-17T20:43:54Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Introduce the method allowing us to parse all configured remotes.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/lib/RepositoryConfig.java |   21 ++++++++++++++++\n .../org/spearce/jgit/transport/RemoteConfig.java   |   26 ++++++++++++++++++++\n 2 files changed, 47 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\nindex 048940d..397c294 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n@@ -53,9 +53,11 @@ import java.io.PrintWriter;\n import java.util.ArrayList;\n import java.util.Collections;\n import java.util.HashMap;\n+import java.util.HashSet;\n import java.util.Iterator;\n import java.util.List;\n import java.util.Map;\n+import java.util.Set;\n \n import org.spearce.jgit.util.FS;\n \n@@ -277,6 +279,25 @@ public class RepositoryConfig {\n \t\t\treturn baseConfig.getStringList(section, subsection, name);\n \t\treturn new String[0];\n \t}\n+\t\n+\t/**\n+\t * @param section\n+\t *            section to search for.\n+\t * @return set of all subsections of specified section within this\n+\t *         configuration and its base configuration; may be empty if no\n+\t *         subsection exists.\n+\t */\n+\tpublic Set<String> getSubsections(final String section) {\n+\t\tfinal Set<String> result = new HashSet<String>();\n+\n+\t\tfor (final Entry e : entries) {\n+\t\t\tif (section.equals(e.base) && e.extendedBase != null)\n+\t\t\t\tresult.add(e.extendedBase);\n+\t\t}\n+\t\tif (baseConfig != null)\n+\t\t\tresult.addAll(baseConfig.getSubsections(section));\n+\t\treturn result;\n+\t}\n \n \tprivate String getRawString(final String section, final String subsection,\n \t\t\tfinal String name) {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java\nindex bb21511..cde5d43 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java\n@@ -73,6 +73,32 @@ public class RemoteConfig {\n \n \t/** Default value for {@link #getReceivePack()} if not specified. */\n \tpublic static final String DEFAULT_RECEIVE_PACK = \"git-receive-pack\";\n+\t\n+\t/**\n+\t * Parse all remote blocks in an existing configuration file, looking for\n+\t * remotes configuration.\n+\t * \n+\t * @param rc\n+\t *            the existing configuration to get the remote settings from.\n+\t *            The configuration must already be loaded into memory.\n+\t * @return all remotes configurations existing in provided repository\n+\t *         configuration. Returned configurations are ordered\n+\t *         lexicographically by names.\n+\t * @throws URISyntaxException\n+\t *             one of the URIs within the remote's configuration is invalid.\n+\t */\n+\tpublic static List<RemoteConfig> getAllRemoteConfigs(\n+\t\t\tfinal RepositoryConfig rc) throws URISyntaxException {\n+\t\tfinal List<String> names = new ArrayList<String>(rc\n+\t\t\t\t.getSubsections(SECTION));\n+\t\tCollections.sort(names);\n+\n+\t\tfinal List<RemoteConfig> result = new ArrayList<RemoteConfig>(names\n+\t\t\t\t.size());\n+\t\tfor (final String name : names)\n+\t\t\tresult.add(new RemoteConfig(rc, name));\n+\t\treturn result;\n+\t}\n \n \tprivate String name;\n \n-- \n1.5.6.3\n"},{"id":"87475","messageId":"1219005852-21496-15-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-14-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 14/31] Add setFetchRefSpecs and setPushRefSpecs to RemoteConfig","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:55Z","receivedAt":"2008-08-17T20:43:55Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"These methods allows us overriding whole collections of specifications,\nsaving unnecessary external code.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../org/spearce/jgit/transport/RemoteConfig.java   |   24 ++++++++++++++++++++\n 1 files changed, 24 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java\nindex cde5d43..5b5e4d1 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java\n@@ -267,6 +267,30 @@ public class RemoteConfig {\n \t}\n \n \t/**\n+\t * Override existing fetch specifications with new ones.\n+\t * \n+\t * @param specs\n+\t *            list of fetch specifications to set. List is copied, it can be\n+\t *            modified after this call.\n+\t */\n+\tpublic void setFetchRefSpecs(final List<RefSpec> specs) {\n+\t\tfetch.clear();\n+\t\tfetch.addAll(specs);\n+\t}\n+\n+\t/**\n+\t * Override existing push specifications with new ones.\n+\t * \n+\t * @param specs\n+\t *            list of push specifications to set. List is copied, it can be\n+\t *            modified after this call.\n+\t */\n+\tpublic void setPushRefSpecs(final List<RefSpec> specs) {\n+\t\tpush.clear();\n+\t\tpush.addAll(specs);\n+\t}\n+\n+\t/**\n \t * Remove a fetch RefSpec from this remote.\n \t * \n \t * @param s\n-- \n1.5.6.3\n"},{"id":"87482","messageId":"1219005852-21496-16-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-15-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 15/31] Add simple abbreviate() method to ObjectId","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:56Z","receivedAt":"2008-08-17T20:43:56Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"It's common usage to search for short, but unique abbreviation of\nobject's SHA-1, so let's have a method for that.\n\nThis is a really workaround implementation, but it has been already used\nin other places. Let's have other usages pointing here, so we can easily\nchange only this implementation later.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/pgm/Fetch.java            |   10 +++++-----\n .../src/org/spearce/jgit/pgm/Push.java             |    7 +++----\n .../src/org/spearce/jgit/pgm/TextBuiltin.java      |    4 ----\n .../src/org/spearce/jgit/lib/AnyObjectId.java      |   15 +++++++++++++++\n 4 files changed, 23 insertions(+), 13 deletions(-)\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java\nindex 4eff32b..99ed101 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java\n@@ -93,7 +93,7 @@ class Fetch extends TextBuiltin {\n \t\t}\n \t}\n \n-\tprivate static String longTypeOf(final TrackingRefUpdate u) {\n+\tprivate String longTypeOf(final TrackingRefUpdate u) {\n \t\tfinal RefUpdate.Result r = u.getResult();\n \t\tif (r == RefUpdate.Result.LOCK_FAILURE)\n \t\t\treturn \"[lock fail]\";\n@@ -110,14 +110,14 @@ class Fetch extends TextBuiltin {\n \t\t}\n \n \t\tif (r == RefUpdate.Result.FORCED) {\n-\t\t\tfinal String aOld = abbreviateObject(u.getOldObjectId());\n-\t\t\tfinal String aNew = abbreviateObject(u.getNewObjectId());\n+\t\t\tfinal String aOld = u.getOldObjectId().abbreviate(db);\n+\t\t\tfinal String aNew = u.getNewObjectId().abbreviate(db);\n \t\t\treturn aOld + \"...\" + aNew;\n \t\t}\n \n \t\tif (r == RefUpdate.Result.FAST_FORWARD) {\n-\t\t\tfinal String aOld = abbreviateObject(u.getOldObjectId());\n-\t\t\tfinal String aNew = abbreviateObject(u.getNewObjectId());\n+\t\t\tfinal String aOld = u.getOldObjectId().abbreviate(db);\n+\t\t\tfinal String aNew = u.getNewObjectId().abbreviate(db);\n \t\t\treturn aOld + \"..\" + aNew;\n \t\t}\n \ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\nindex f5b24c6..b61071c 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n@@ -173,10 +173,9 @@ class Push extends TextBuiltin {\n \t\t\t\t} else {\n \t\t\t\t\tboolean fastForward = rru.isFastForward();\n \t\t\t\t\tfinal char flag = fastForward ? ' ' : '+';\n-\t\t\t\t\tfinal String summary = abbreviateObject(oldRef\n-\t\t\t\t\t\t\t.getObjectId())\n+\t\t\t\t\tfinal String summary = oldRef.getObjectId().abbreviate(db)\n \t\t\t\t\t\t\t+ (fastForward ? \"..\" : \"...\")\n-\t\t\t\t\t\t\t+ abbreviateObject(rru.getNewObjectId());\n+\t\t\t\t\t\t\t+ rru.getNewObjectId().abbreviate(db);\n \t\t\t\t\tfinal String message = fastForward ? null : \"forced update\";\n \t\t\t\t\tprintUpdateLine(flag, summary, srcRef, remoteName, message);\n \t\t\t\t}\n@@ -199,7 +198,7 @@ class Push extends TextBuiltin {\n \n \t\tcase REJECTED_REMOTE_CHANGED:\n \t\t\tfinal String message = \"remote ref object changed - is not expected one \"\n-\t\t\t\t\t+ abbreviateObject(rru.getExpectedOldObjectId());\n+\t\t\t\t\t+ rru.getExpectedOldObjectId().abbreviate(db);\n \t\t\tprintUpdateLine('!', \"[rejected]\", srcRef, remoteName, message);\n \t\t\tbreak;\n \ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java\nindex 5c066cb..23ab92b 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java\n@@ -179,10 +179,6 @@ public abstract class TextBuiltin {\n \t\treturn new Die(why);\n \t}\n \n-\tprotected static String abbreviateObject(final ObjectId id) {\n-\t\treturn id.toString().substring(0, 7);\n-\t}\n-\n \tprotected String abbreviateRef(String dst, boolean abbreviateRemote) {\n \t\tif (dst.startsWith(REFS_HEADS))\n \t\t\tdst = dst.substring(REFS_HEADS.length());\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/AnyObjectId.java b/org.spearce.jgit/src/org/spearce/jgit/lib/AnyObjectId.java\nindex 7357e57..2c5518a 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/AnyObjectId.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/AnyObjectId.java\n@@ -411,6 +411,21 @@ public abstract class AnyObjectId implements Comparable {\n \t}\n \n \t/**\n+\t * Return unique abbreviation (prefix) of this object SHA-1.\n+\t * <p>\n+\t * Current implementation is not guaranteeing uniqueness, it just returns\n+\t * fixed-length prefix of SHA-1 string.\n+\t * \n+\t * @param repo\n+\t *            repository for checking uniqueness within.\n+\t * @return SHA-1 abbreviation.\n+\t */\n+\tpublic String abbreviate(final Repository repo) {\n+\t\t// TODO implement checking for uniqueness\n+\t\treturn toString().substring(0, 7);\n+\t}\n+\n+\t/**\n \t * Obtain an immutable copy of this current object name value.\n \t * <p>\n \t * Only returns <code>this</code> if this instance is an unsubclassed\n-- \n1.5.6.3\n"},{"id":"87483","messageId":"1219005852-21496-17-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-16-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 16/31] Remove debug/test console output from GitIndex","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:57Z","receivedAt":"2008-08-17T20:43:57Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Typical Eclipse users (read: me) usually doesn't like unnecessary\noutput on Eclipse stdout.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/lib/GitIndex.java         |    3 ---\n 1 files changed, 0 insertions(+), 3 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java b/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java\nindex c7a4402..2255c1a 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/GitIndex.java\n@@ -190,7 +190,6 @@ public class GitIndex {\n \t * @throws IOException\n \t */\n \tpublic void read() throws IOException {\n-\t\tlong t0 = System.currentTimeMillis();\n \t\tchanged = false;\n \t\tstatDirty = false;\n \t\tif (!cacheFile.exists()) {\n@@ -214,9 +213,7 @@ public class GitIndex {\n \t\t\t\tEntry entry = new Entry(buffer);\n \t\t\t\tentries.put(entry.name, entry);\n \t\t\t}\n-\t\t\tlong t1 = System.currentTimeMillis();\n \t\t\tlastCacheTime = cacheFile.lastModified();\n-\t\t\tSystem.out.println(\"Read index \"+cacheFile+\" in \"+((t1-t0)/1000.0)+\"s\");\n \t\t} finally {\n \t\t\tcache.close();\n \t\t}\n-- \n1.5.6.3\n"},{"id":"87485","messageId":"1219005852-21496-18-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-17-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 17/31] Fix typo in uitext.properties message","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:58Z","receivedAt":"2008-08-17T20:43:58Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/egit/ui/uitext.properties      |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex 9516aa0..55b1348 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -16,7 +16,7 @@\n ##\n \n SharingWizard_windowTitle=Configure Git Repository\n-SharingWizard_failed=Failed to initalize Git team provider.\n+SharingWizard_failed=Failed to initialize Git team provider.\n \n GenericOperationFailed={0} Failed\n \n-- \n1.5.6.3\n"},{"id":"87484","messageId":"1219005852-21496-19-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-18-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 18/31] Refactor/rewrite CloneSourcePage to universal RepositorySelectionPage","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:43:59Z","receivedAt":"2008-08-17T20:43:59Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"CloneSourcePage is refactored to serve as generic wizard page for\nrepository selection. Selection can be performed now as custom URI or\n(optionally - configurable) from preconfigured remote repositories. That\nallows us to this wizard page for selection of both source and destination\nrepository in wizards.\n\nAs there was need for code rewrite in few places, some things get fixed or\nimproved by the way:\n- Controls are disabled/enabled recursively, so user get better feedback\n  what he/she can type.\n- URI panel behaved strange when bad URI was typed, now it's probably more\n  obvious what's going on.\n- Distinction is introduced between internal URI/RemoteConfig selection\n  (possibly invalid) and exposed one - for other pages. Hence, clients of\n  this class don't have to handle internal validation issues.\n- isPageComplete() logic is moved to checkPage(), as it seems to be\n  strange (and hard to follow?) pattern to mix setPageComplete() and\n  custom isPageComplete().\n- possibly minor forgotten issues.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |   47 +-\n .../ui/internal/clone/CloneDestinationPage.java    |   24 +-\n .../egit/ui/internal/clone/CloneSourcePage.java    |  460 -------------\n .../egit/ui/internal/clone/GitCloneWizard.java     |   13 +-\n .../egit/ui/internal/clone/SourceBranchPage.java   |   37 +-\n .../ui/internal/clone/URIishChangeListener.java    |   20 -\n .../components/RepositorySelectionListener.java    |   32 +\n .../components/RepositorySelectionPage.java        |  680 ++++++++++++++++++++\n .../src/org/spearce/egit/ui/uitext.properties      |   37 +-\n 9 files changed, 798 insertions(+), 552 deletions(-)\n delete mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneSourcePage.java\n delete mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/URIishChangeListener.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionListener.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionPage.java\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex 4adb99c..0d39440 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -53,52 +53,67 @@ public class UIText extends NLS {\n \tpublic static String GitCloneWizard_failed;\n \n \t/** */\n-\tpublic static String CloneSourcePage_title;\n+\tpublic static String RepositorySelectionPage_sourceSelectionTitle;\n \n \t/** */\n-\tpublic static String CloneSourcePage_description;\n+\tpublic static String RepositorySelectionPage_sourceSelectionDescription;\n \n \t/** */\n-\tpublic static String CloneSourcePage_groupLocation;\n+\tpublic static String RepositorySelectionPage_destinationSelectionTitle;\n \n \t/** */\n-\tpublic static String CloneSourcePage_groupAuthentication;\n+\tpublic static String RepositorySelectionPage_destinationSelectionDescription;\n \n \t/** */\n-\tpublic static String CloneSourcePage_groupConnection;\n+\tpublic static String RepositorySelectionPage_groupLocation;\n \n \t/** */\n-\tpublic static String CloneSourcePage_promptURI;\n+\tpublic static String RepositorySelectionPage_groupAuthentication;\n \n \t/** */\n-\tpublic static String CloneSourcePage_promptHost;\n+\tpublic static String RepositorySelectionPage_groupConnection;\n \n \t/** */\n-\tpublic static String CloneSourcePage_promptPath;\n+\tpublic static String RepositorySelectionPage_promptURI;\n \n \t/** */\n-\tpublic static String CloneSourcePage_promptUser;\n+\tpublic static String RepositorySelectionPage_promptHost;\n \n \t/** */\n-\tpublic static String CloneSourcePage_promptPassword;\n+\tpublic static String RepositorySelectionPage_promptPath;\n \n \t/** */\n-\tpublic static String CloneSourcePage_promptScheme;\n+\tpublic static String RepositorySelectionPage_promptUser;\n \n \t/** */\n-\tpublic static String CloneSourcePage_promptPort;\n+\tpublic static String RepositorySelectionPage_promptPassword;\n \n \t/** */\n-\tpublic static String CloneSourcePage_fieldRequired;\n+\tpublic static String RepositorySelectionPage_promptScheme;\n \n \t/** */\n-\tpublic static String CloneSourcePage_fieldNotSupported;\n+\tpublic static String RepositorySelectionPage_promptPort;\n \n \t/** */\n-\tpublic static String CloneSourcePage_fileNotFound;\n+\tpublic static String RepositorySelectionPage_fieldRequired;\n \n \t/** */\n-\tpublic static String CloneSourcePage_internalError;\n+\tpublic static String RepositorySelectionPage_fieldNotSupported;\n+\n+\t/** */\n+\tpublic static String RepositorySelectionPage_fileNotFound;\n+\n+\t/** */\n+\tpublic static String RepositorySelectionPage_internalError;\n+\n+\t/** */\n+\tpublic static String RepositorySelectionPage_configuredRemoteChoice;\n+\n+\t/** */\n+\tpublic static String RepositorySelectionPage_uriChoice;\n+\n+\t/** */\n+\tpublic static String SourceBranchPage_title;\n \n \t/** */\n \tpublic static String SourceBranchPage_branchList;\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\nindex 5d5a8ee..249bfd3 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\n@@ -9,7 +9,6 @@\n package org.spearce.egit.ui.internal.clone;\n \n import java.io.File;\n-import java.net.URISyntaxException;\n \n import org.eclipse.core.resources.ResourcesPlugin;\n import org.eclipse.jface.wizard.WizardPage;\n@@ -28,10 +27,12 @@ import org.eclipse.swt.widgets.FileDialog;\n import org.eclipse.swt.widgets.Group;\n import org.eclipse.swt.widgets.Label;\n import org.eclipse.swt.widgets.Text;\n-import org.spearce.egit.ui.UIIcons;\n import org.spearce.egit.ui.UIText;\n+import org.spearce.egit.ui.internal.components.RepositorySelectionListener;\n+import org.spearce.egit.ui.internal.components.RepositorySelectionPage;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.transport.RemoteConfig;\n import org.spearce.jgit.transport.URIish;\n \n /**\n@@ -39,7 +40,7 @@ import org.spearce.jgit.transport.URIish;\n  * cloned.\n  */\n class CloneDestinationPage extends WizardPage {\n-\tprivate final CloneSourcePage sourcePage;\n+\tprivate final RepositorySelectionPage sourcePage;\n \n \tprivate final SourceBranchPage branchPage;\n \n@@ -51,16 +52,17 @@ class CloneDestinationPage extends WizardPage {\n \n \tprivate Text remoteText;\n \n-\tCloneDestinationPage(final CloneSourcePage sp, final SourceBranchPage bp) {\n+\tCloneDestinationPage(final RepositorySelectionPage sp,\n+\t\t\tfinal SourceBranchPage bp) {\n \t\tsuper(CloneDestinationPage.class.getName());\n \t\tsourcePage = sp;\n \t\tbranchPage = bp;\n \n \t\tsetTitle(UIText.CloneDestinationPage_title);\n-\t\tsetImageDescriptor(UIIcons.WIZBAN_IMPORT_REPO);\n \n-\t\tsourcePage.addURIishChangeListener(new URIishChangeListener() {\n-\t\t\tpublic void uriishChanged(final URIish newURI) {\n+\t\tsourcePage.addRepositorySelectionListener(new RepositorySelectionListener() {\n+\t\t\tpublic void selectionChanged(final URIish newURI,\n+\t\t\t\t\tfinal RemoteConfig newConfig) {\n \t\t\t\tif (newURI == null || !newURI.equals(validated))\n \t\t\t\t\tsetPageComplete(false);\n \t\t\t}\n@@ -221,12 +223,8 @@ class CloneDestinationPage extends WizardPage {\n \n \tprivate void revalidate() {\n \t\tURIish newURI = null;\n-\t\ttry {\n-\t\t\tnewURI = sourcePage.getURI();\n-\t\t\tvalidated = newURI;\n-\t\t} catch (URISyntaxException e) {\n-\t\t\tvalidated = null;\n-\t\t}\n+\t\tnewURI = sourcePage.getURI();\n+\t\tvalidated = newURI;\n \n \t\tif (newURI == null || !newURI.equals(validated)) {\n \t\t\tfinal String n = getSuggestedName();\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneSourcePage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneSourcePage.java\ndeleted file mode 100644\nindex 4bdd6af..0000000\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneSourcePage.java\n+++ /dev/null\n@@ -1,460 +0,0 @@\n-/*******************************************************************************\n- * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>\n- * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>\n- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n- *\n- * All rights reserved. This program and the accompanying materials\n- * are made available under the terms of the Eclipse Public License v1.0\n- * See LICENSE for the full license text, also available.\n- *******************************************************************************/\n-package org.spearce.egit.ui.internal.clone;\n-\n-import java.io.File;\n-import java.net.URISyntaxException;\n-import java.util.ArrayList;\n-import java.util.List;\n-import java.util.regex.Pattern;\n-\n-import org.eclipse.jface.wizard.WizardPage;\n-import org.eclipse.osgi.util.NLS;\n-import org.eclipse.swt.SWT;\n-import org.eclipse.swt.events.ModifyEvent;\n-import org.eclipse.swt.events.ModifyListener;\n-import org.eclipse.swt.events.SelectionEvent;\n-import org.eclipse.swt.events.SelectionListener;\n-import org.eclipse.swt.events.VerifyEvent;\n-import org.eclipse.swt.events.VerifyListener;\n-import org.eclipse.swt.layout.GridData;\n-import org.eclipse.swt.layout.GridLayout;\n-import org.eclipse.swt.widgets.Combo;\n-import org.eclipse.swt.widgets.Composite;\n-import org.eclipse.swt.widgets.Group;\n-import org.eclipse.swt.widgets.Label;\n-import org.eclipse.swt.widgets.Text;\n-import org.spearce.egit.ui.Activator;\n-import org.spearce.egit.ui.UIIcons;\n-import org.spearce.egit.ui.UIText;\n-import org.spearce.jgit.transport.URIish;\n-import org.spearce.jgit.util.FS;\n-\n-/**\n- * Wizard page that allows the user entering the location of a repository to be\n- * cloned.\n- */\n-class CloneSourcePage extends WizardPage {\n-\tprivate static final int S_GIT = 0;\n-\n-\tprivate static final int S_SSH = 1;\n-\n-\tprivate static final int S_SFTP = 2;\n-\n-\tprivate static final int S_HTTP = 3;\n-\n-\tprivate static final int S_HTTPS = 4;\n-\n-\tprivate static final int S_FTP = 5;\n-\n-\tprivate static final int S_FILE = 6;\n-\n-\tprivate static final String[] DEFAULT_SCHEMES;\n-\tstatic {\n-\t\tDEFAULT_SCHEMES = new String[7];\n-\t\tDEFAULT_SCHEMES[S_GIT] = \"git\";\n-\t\tDEFAULT_SCHEMES[S_SSH] = \"git+ssh\";\n-\t\tDEFAULT_SCHEMES[S_SFTP] = \"sftp\";\n-\t\tDEFAULT_SCHEMES[S_HTTP] = \"http\";\n-\t\tDEFAULT_SCHEMES[S_HTTPS] = \"https\";\n-\t\tDEFAULT_SCHEMES[S_FTP] = \"ftp\";\n-\t\tDEFAULT_SCHEMES[S_FILE] = \"file\";\n-\t}\n-\n-\tprivate final List<URIishChangeListener> uriishChangeListeners;\n-\n-\tprivate Group authGroup;\n-\n-\tprivate Text uriText;\n-\n-\tprivate Text hostText;\n-\n-\tprivate Text pathText;\n-\n-\tprivate Text userText;\n-\n-\tprivate Text passText;\n-\n-\tprivate Combo scheme;\n-\n-\tprivate Text portText;\n-\n-\tprivate int eventDepth;\n-\n-\tprivate URIish uri = new URIish();\n-\n-\tCloneSourcePage() {\n-\t\tsuper(CloneSourcePage.class.getName());\n-\t\tsetTitle(UIText.CloneSourcePage_title);\n-\t\tsetDescription(UIText.CloneSourcePage_description);\n-\t\tsetImageDescriptor(UIIcons.WIZBAN_IMPORT_REPO);\n-\t\turiishChangeListeners = new ArrayList<URIishChangeListener>(4);\n-\t}\n-\n-\tvoid addURIishChangeListener(final URIishChangeListener l) {\n-\t\turiishChangeListeners.add(l);\n-\t}\n-\n-\tpublic void createControl(final Composite parent) {\n-\t\tfinal Composite panel = new Composite(parent, SWT.NULL);\n-\t\tfinal GridLayout layout = new GridLayout();\n-\t\tlayout.numColumns = 1;\n-\t\tpanel.setLayout(layout);\n-\n-\t\tcreateLocationGroup(panel);\n-\t\tcreateConnectionGroup(panel);\n-\t\tauthGroup = createAuthenticationGroup(panel);\n-\n-\t\tupdateAuthGroup();\n-\t\tsetControl(panel);\n-\t\tsetPageComplete(false);\n-\t}\n-\n-\tprivate void createLocationGroup(final Composite parent) {\n-\t\tfinal Group g = createGroup(parent,\n-\t\t\t\tUIText.CloneSourcePage_groupLocation);\n-\n-\t\tnewLabel(g, UIText.CloneSourcePage_promptURI + \":\");\n-\t\turiText = new Text(g, SWT.BORDER);\n-\t\turiText.setLayoutData(createFieldGridData());\n-\t\turiText.addModifyListener(new ModifyListener() {\n-\t\t\tpublic void modifyText(final ModifyEvent e) {\n-\t\t\t\ttry {\n-\t\t\t\t\teventDepth++;\n-\t\t\t\t\tif (eventDepth != 1)\n-\t\t\t\t\t\treturn;\n-\n-\t\t\t\t\tfinal URIish u = new URIish(uriText.getText());\n-\t\t\t\t\tsafeSet(hostText, u.getHost());\n-\t\t\t\t\tsafeSet(pathText, u.getPath());\n-\t\t\t\t\tsafeSet(userText, u.getUser());\n-\t\t\t\t\tsafeSet(passText, u.getPass());\n-\n-\t\t\t\t\tif (u.getPort() > 0)\n-\t\t\t\t\t\tportText.setText(Integer.toString(u.getPort()));\n-\t\t\t\t\telse\n-\t\t\t\t\t\tportText.setText(\"\");\n-\n-\t\t\t\t\tif (isFile(u))\n-\t\t\t\t\t\tscheme.select(S_FILE);\n-\t\t\t\t\telse if (isSSH(u))\n-\t\t\t\t\t\tscheme.select(S_SSH);\n-\t\t\t\t\telse {\n-\t\t\t\t\t\tfor (int i = 0; i < DEFAULT_SCHEMES.length; i++) {\n-\t\t\t\t\t\t\tif (DEFAULT_SCHEMES[i].equals(u.getScheme())) {\n-\t\t\t\t\t\t\t\tscheme.select(i);\n-\t\t\t\t\t\t\t\tbreak;\n-\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t}\n-\t\t\t\t\t}\n-\n-\t\t\t\t\tupdateAuthGroup();\n-\t\t\t\t\turi = u;\n-\t\t\t\t\tfor (final URIishChangeListener l : uriishChangeListeners)\n-\t\t\t\t\t\tl.uriishChanged(u);\n-\t\t\t\t\tsetPageComplete(isPageComplete());\n-\t\t\t\t} catch (URISyntaxException err) {\n-\t\t\t\t\turiInvalid();\n-\t\t\t\t\tsetErrorMessage(err.getMessage());\n-\t\t\t\t\tsetPageComplete(false);\n-\t\t\t\t} finally {\n-\t\t\t\t\teventDepth--;\n-\t\t\t\t}\n-\t\t\t}\n-\t\t});\n-\n-\t\tnewLabel(g, UIText.CloneSourcePage_promptHost + \":\");\n-\t\thostText = new Text(g, SWT.BORDER);\n-\t\thostText.setLayoutData(createFieldGridData());\n-\t\thostText.addModifyListener(new ModifyListener() {\n-\t\t\tpublic void modifyText(final ModifyEvent e) {\n-\t\t\t\tsetURI(uri.setHost(nullString(hostText.getText())));\n-\t\t\t}\n-\t\t});\n-\n-\t\tnewLabel(g, UIText.CloneSourcePage_promptPath + \":\");\n-\t\tpathText = new Text(g, SWT.BORDER);\n-\t\tpathText.setLayoutData(createFieldGridData());\n-\t\tpathText.addModifyListener(new ModifyListener() {\n-\t\t\tpublic void modifyText(final ModifyEvent e) {\n-\t\t\t\tsetURI(uri.setPath(nullString(pathText.getText())));\n-\t\t\t}\n-\t\t});\n-\t}\n-\n-\tprivate Group createAuthenticationGroup(final Composite parent) {\n-\t\tfinal Group g = createGroup(parent,\n-\t\t\t\tUIText.CloneSourcePage_groupAuthentication);\n-\n-\t\tnewLabel(g, UIText.CloneSourcePage_promptUser + \":\");\n-\t\tuserText = new Text(g, SWT.BORDER);\n-\t\tuserText.setLayoutData(createFieldGridData());\n-\t\tuserText.addModifyListener(new ModifyListener() {\n-\t\t\tpublic void modifyText(final ModifyEvent e) {\n-\t\t\t\tsetURI(uri.setUser(nullString(userText.getText())));\n-\t\t\t}\n-\t\t});\n-\n-\t\tnewLabel(g, UIText.CloneSourcePage_promptPassword + \":\");\n-\t\tpassText = new Text(g, SWT.BORDER | SWT.PASSWORD);\n-\t\tpassText.setLayoutData(createFieldGridData());\n-\t\treturn g;\n-\t}\n-\n-\tprivate void createConnectionGroup(final Composite parent) {\n-\t\tfinal Group g = createGroup(parent,\n-\t\t\t\tUIText.CloneSourcePage_groupConnection);\n-\n-\t\tnewLabel(g, UIText.CloneSourcePage_promptScheme + \":\");\n-\t\tscheme = new Combo(g, SWT.DROP_DOWN | SWT.READ_ONLY);\n-\t\tscheme.setItems(DEFAULT_SCHEMES);\n-\t\tscheme.addSelectionListener(new SelectionListener() {\n-\t\t\tpublic void widgetDefaultSelected(final SelectionEvent e) {\n-\t\t\t\t// Nothing\n-\t\t\t}\n-\n-\t\t\tpublic void widgetSelected(final SelectionEvent e) {\n-\t\t\t\tfinal int idx = scheme.getSelectionIndex();\n-\t\t\t\tif (idx < 0)\n-\t\t\t\t\tsetURI(uri.setScheme(null));\n-\t\t\t\telse\n-\t\t\t\t\tsetURI(uri.setScheme(nullString(scheme.getItem(idx))));\n-\t\t\t\tupdateAuthGroup();\n-\t\t\t}\n-\t\t});\n-\n-\t\tnewLabel(g, UIText.CloneSourcePage_promptPort + \":\");\n-\t\tportText = new Text(g, SWT.BORDER);\n-\t\tportText.addVerifyListener(new VerifyListener() {\n-\t\t\tfinal Pattern p = Pattern.compile(\"^(?:[1-9][0-9]*)?$\");\n-\n-\t\t\tpublic void verifyText(final VerifyEvent e) {\n-\t\t\t\tfinal String v = portText.getText();\n-\t\t\t\te.doit = p.matcher(\n-\t\t\t\t\t\tv.substring(0, e.start) + e.text + v.substring(e.end))\n-\t\t\t\t\t\t.matches();\n-\t\t\t}\n-\t\t});\n-\t\tportText.addModifyListener(new ModifyListener() {\n-\t\t\tpublic void modifyText(final ModifyEvent e) {\n-\t\t\t\tfinal String val = nullString(portText.getText());\n-\t\t\t\tif (val == null)\n-\t\t\t\t\tsetURI(uri.setPort(-1));\n-\t\t\t\telse {\n-\t\t\t\t\ttry {\n-\t\t\t\t\t\tsetURI(uri.setPort(Integer.parseInt(val)));\n-\t\t\t\t\t} catch (NumberFormatException err) {\n-\t\t\t\t\t\t// Ignore it for now.\n-\t\t\t\t\t\turiInvalid();\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t}\n-\t\t});\n-\t}\n-\n-\tprivate static Group createGroup(final Composite parent, final String text) {\n-\t\tfinal Group g = new Group(parent, SWT.NONE);\n-\t\tfinal GridLayout layout = new GridLayout();\n-\t\tlayout.numColumns = 2;\n-\t\tg.setLayout(layout);\n-\t\tg.setText(text);\n-\t\tfinal GridData gd = new GridData();\n-\t\tgd.grabExcessHorizontalSpace = true;\n-\t\tgd.horizontalAlignment = SWT.FILL;\n-\t\tg.setLayoutData(gd);\n-\t\treturn g;\n-\t}\n-\n-\tprivate static void newLabel(final Group g, final String text) {\n-\t\tnew Label(g, SWT.NULL).setText(text);\n-\t}\n-\n-\tprivate static GridData createFieldGridData() {\n-\t\treturn new GridData(SWT.FILL, SWT.DEFAULT, true, false);\n-\t}\n-\n-\t/**\n-\t * Returns the URI entered in the Wizard page.\n-\t * \n-\t * @return the URI entered in the Wizard page.\n-\t * @throws URISyntaxException\n-\t */\n-\tpublic URIish getURI() throws URISyntaxException {\n-\t\treturn new URIish(uriText.getText());\n-\t}\n-\n-\t@Override\n-\tpublic boolean isPageComplete() {\n-\t\tif (uriText.getText().length() == 0) {\n-\t\t\tsetErrorMessage(null);\n-\t\t\treturn false;\n-\t\t}\n-\n-\t\ttry {\n-\t\t\tfinal URIish finalURI = getURI();\n-\t\t\tString proto = finalURI.getScheme();\n-\t\t\tif (proto == null && scheme.getSelectionIndex() >= 0)\n-\t\t\t\tproto = scheme.getItem(scheme.getSelectionIndex());\n-\n-\t\t\tif (uri.getPath() == null) {\n-\t\t\t\turiInvalid();\n-\t\t\t\tsetErrorMessage(NLS.bind(UIText.CloneSourcePage_fieldRequired,\n-\t\t\t\t\t\tUIText.CloneSourcePage_promptPath, proto));\n-\t\t\t\treturn false;\n-\t\t\t}\n-\n-\t\t\tif (isFile(finalURI)) {\n-\t\t\t\tString badField = null;\n-\t\t\t\tif (uri.getHost() != null)\n-\t\t\t\t\tbadField = UIText.CloneSourcePage_promptHost;\n-\t\t\t\telse if (uri.getUser() != null)\n-\t\t\t\t\tbadField = UIText.CloneSourcePage_promptUser;\n-\t\t\t\telse if (uri.getPass() != null)\n-\t\t\t\t\tbadField = UIText.CloneSourcePage_promptPassword;\n-\t\t\t\tif (badField != null) {\n-\t\t\t\t\turiInvalid();\n-\t\t\t\t\tsetErrorMessage(NLS.bind(\n-\t\t\t\t\t\t\tUIText.CloneSourcePage_fieldNotSupported, badField,\n-\t\t\t\t\t\t\tproto));\n-\t\t\t\t\treturn false;\n-\t\t\t\t}\n-\n-\t\t\t\tfinal File d = FS.resolve(new File(\".\"), uri.getPath());\n-\t\t\t\tif (!d.exists()) {\n-\t\t\t\t\tsetErrorMessage(NLS.bind(\n-\t\t\t\t\t\t\tUIText.CloneSourcePage_fileNotFound, d\n-\t\t\t\t\t\t\t\t\t.getAbsolutePath()));\n-\t\t\t\t\treturn false;\n-\t\t\t\t}\n-\t\t\t\tsetErrorMessage(null);\n-\t\t\t\treturn true;\n-\t\t\t}\n-\n-\t\t\tif (uri.getHost() == null) {\n-\t\t\t\turiInvalid();\n-\t\t\t\tsetErrorMessage(NLS.bind(UIText.CloneSourcePage_fieldRequired,\n-\t\t\t\t\t\tUIText.CloneSourcePage_promptHost, proto));\n-\t\t\t\treturn false;\n-\t\t\t}\n-\n-\t\t\tif (isGIT(finalURI)) {\n-\t\t\t\tString badField = null;\n-\t\t\t\tif (uri.getUser() != null)\n-\t\t\t\t\tbadField = UIText.CloneSourcePage_promptUser;\n-\t\t\t\telse if (uri.getPass() != null)\n-\t\t\t\t\tbadField = UIText.CloneSourcePage_promptPassword;\n-\t\t\t\tif (badField != null) {\n-\t\t\t\t\turiInvalid();\n-\t\t\t\t\tsetErrorMessage(NLS.bind(\n-\t\t\t\t\t\t\tUIText.CloneSourcePage_fieldNotSupported, badField,\n-\t\t\t\t\t\t\tproto));\n-\t\t\t\t\treturn false;\n-\t\t\t\t}\n-\t\t\t}\n-\n-\t\t\tsetErrorMessage(null);\n-\t\t\treturn true;\n-\t\t} catch (URISyntaxException e) {\n-\t\t\turiInvalid();\n-\t\t\tsetErrorMessage(e.getReason());\n-\t\t\treturn false;\n-\t\t} catch (Exception e) {\n-\t\t\turiInvalid();\n-\t\t\tActivator.logError(\"Error validating \" + getClass().getName(), e);\n-\t\t\tsetErrorMessage(UIText.CloneSourcePage_internalError);\n-\t\t\treturn false;\n-\t\t}\n-\t}\n-\n-\tprivate static boolean isGIT(final URIish uri) {\n-\t\treturn \"git\".equals(uri.getScheme());\n-\t}\n-\n-\tprivate static boolean isFile(final URIish uri) {\n-\t\tif (\"file\".equals(uri.getScheme()))\n-\t\t\treturn true;\n-\t\tif (uri.getHost() != null || uri.getPort() > 0 || uri.getUser() != null\n-\t\t\t\t|| uri.getPass() != null || uri.getPath() == null)\n-\t\t\treturn false;\n-\t\tif (uri.getScheme() == null)\n-\t\t\treturn FS.resolve(new File(\".\"), uri.getPath()).isDirectory();\n-\t\treturn false;\n-\t}\n-\n-\tprivate static boolean isSSH(final URIish uri) {\n-\t\tif (!uri.isRemote())\n-\t\t\treturn false;\n-\t\tfinal String scheme = uri.getScheme();\n-\t\tif (\"ssh\".equals(scheme))\n-\t\t\treturn true;\n-\t\tif (\"ssh+git\".equals(scheme))\n-\t\t\treturn true;\n-\t\tif (\"git+ssh\".equals(scheme))\n-\t\t\treturn true;\n-\t\tif (scheme == null && uri.getHost() != null && uri.getPath() != null)\n-\t\t\treturn true;\n-\t\treturn false;\n-\t}\n-\n-\tprivate static String nullString(final String value) {\n-\t\tif (value == null)\n-\t\t\treturn null;\n-\t\tfinal String v = value.trim();\n-\t\treturn v.length() == 0 ? null : v;\n-\t}\n-\n-\tprivate static void safeSet(final Text text, final String value) {\n-\t\ttext.setText(value != null ? value : \"\");\n-\t}\n-\n-\tprivate void setURI(final URIish u) {\n-\t\ttry {\n-\t\t\teventDepth++;\n-\t\t\tif (eventDepth == 1) {\n-\t\t\t\tfor (final URIishChangeListener l : uriishChangeListeners)\n-\t\t\t\t\tl.uriishChanged(u);\n-\t\t\t\turi = u;\n-\t\t\t\turiText.setText(uri.toString());\n-\t\t\t\tsetPageComplete(isPageComplete());\n-\t\t\t}\n-\t\t} finally {\n-\t\t\teventDepth--;\n-\t\t}\n-\t}\n-\n-\tprivate void updateAuthGroup() {\n-\t\tswitch (scheme.getSelectionIndex()) {\n-\t\tcase S_GIT:\n-\t\t\thostText.setEnabled(true);\n-\t\t\tportText.setEnabled(true);\n-\t\t\tauthGroup.setEnabled(false);\n-\t\t\tbreak;\n-\t\tcase S_SSH:\n-\t\tcase S_SFTP:\n-\t\tcase S_HTTP:\n-\t\tcase S_HTTPS:\n-\t\tcase S_FTP:\n-\t\t\thostText.setEnabled(true);\n-\t\t\tportText.setEnabled(true);\n-\t\t\tauthGroup.setEnabled(true);\n-\t\t\tbreak;\n-\t\tcase S_FILE:\n-\t\t\thostText.setEnabled(false);\n-\t\t\tportText.setEnabled(false);\n-\t\t\tauthGroup.setEnabled(false);\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\n-\tprivate void uriInvalid() {\n-\t\tfor (final URIishChangeListener l : uriishChangeListeners)\n-\t\t\tl.uriishChanged(null);\n-\t}\n-}\n\\ No newline at end of file\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\nindex 50c2ef9..58c169a 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\n@@ -25,7 +25,9 @@ import org.eclipse.ui.IImportWizard;\n import org.eclipse.ui.IWorkbench;\n import org.spearce.egit.core.op.CloneOperation;\n import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIIcons;\n import org.spearce.egit.ui.UIText;\n+import org.spearce.egit.ui.internal.components.RepositorySelectionPage;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.Ref;\n import org.spearce.jgit.lib.Repository;\n@@ -42,7 +44,7 @@ public class GitCloneWizard extends Wizard implements IImportWizard {\n \tprivate static final String REMOTES_PREFIX_S = Constants.REMOTES_PREFIX\n \t\t\t+ \"/\";\n \n-\tprivate CloneSourcePage cloneSource;\n+\tprivate RepositorySelectionPage cloneSource;\n \n \tprivate SourceBranchPage validSource;\n \n@@ -50,7 +52,8 @@ public class GitCloneWizard extends Wizard implements IImportWizard {\n \n \tpublic void init(IWorkbench arg0, IStructuredSelection arg1) {\n \t\tsetWindowTitle(UIText.GitCloneWizard_title);\n-\t\tcloneSource = new CloneSourcePage();\n+\t\tsetDefaultPageImageDescriptor(UIIcons.WIZBAN_IMPORT_REPO);\n+\t\tcloneSource = new RepositorySelectionPage(true);\n \t\tvalidSource = new SourceBranchPage(cloneSource);\n \t\tcloneDestination = new CloneDestinationPage(cloneSource, validSource);\n \t}\n@@ -68,11 +71,7 @@ public class GitCloneWizard extends Wizard implements IImportWizard {\n \t\tfinal Repository db;\n \t\tfinal RemoteConfig origin;\n \n-\t\ttry {\n-\t\t\turi = cloneSource.getURI();\n-\t\t} catch (URISyntaxException e) {\n-\t\t\treturn false;\n-\t\t}\n+\t\turi = cloneSource.getURI();\n \n \t\tfinal File workdir = cloneDestination.getDestinationFile();\n \t\tfinal String branch = cloneDestination.getInitialBranch();\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java\nindex b0aba1e..1768dbd 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java\n@@ -10,7 +10,6 @@ package org.spearce.egit.ui.internal.clone;\n \n import java.io.File;\n import java.lang.reflect.InvocationTargetException;\n-import java.net.URISyntaxException;\n import java.util.ArrayList;\n import java.util.Collection;\n import java.util.Collections;\n@@ -40,19 +39,21 @@ import org.eclipse.swt.widgets.Listener;\n import org.eclipse.swt.widgets.Table;\n import org.eclipse.swt.widgets.TableItem;\n import org.spearce.egit.ui.Activator;\n-import org.spearce.egit.ui.UIIcons;\n import org.spearce.egit.ui.UIText;\n+import org.spearce.egit.ui.internal.components.RepositorySelectionListener;\n+import org.spearce.egit.ui.internal.components.RepositorySelectionPage;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.Ref;\n import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.transport.FetchConnection;\n+import org.spearce.jgit.transport.RemoteConfig;\n import org.spearce.jgit.transport.Transport;\n import org.spearce.jgit.transport.URIish;\n \n class SourceBranchPage extends WizardPage {\n \tprivate final List<BranchChangeListener> branchChangeListeners;\n \n-\tprivate final CloneSourcePage sourcePage;\n+\tprivate final RepositorySelectionPage sourcePage;\n \n \tprivate URIish validated;\n \n@@ -68,19 +69,20 @@ class SourceBranchPage extends WizardPage {\n \n \tprivate String transportError;\n \n-\tSourceBranchPage(final CloneSourcePage sp) {\n+\tSourceBranchPage(final RepositorySelectionPage sp) {\n \t\tsuper(SourceBranchPage.class.getName());\n \t\tsourcePage = sp;\n-\t\tsetTitle(UIText.CloneSourcePage_title);\n-\t\tsetImageDescriptor(UIIcons.WIZBAN_IMPORT_REPO);\n-\t\tsourcePage.addURIishChangeListener(new URIishChangeListener() {\n-\t\t\tpublic void uriishChanged(final URIish newURI) {\n-\t\t\t\tif (newURI == null || !newURI.equals(validated)) {\n-\t\t\t\t\tvalidated = null;\n-\t\t\t\t\tsetPageComplete(false);\n-\t\t\t\t}\n-\t\t\t}\n-\t\t});\n+\t\tsetTitle(UIText.SourceBranchPage_title);\n+\t\tsourcePage\n+\t\t\t\t.addRepositorySelectionListener(new RepositorySelectionListener() {\n+\t\t\t\t\tpublic void selectionChanged(final URIish newURI,\n+\t\t\t\t\t\t\tfinal RemoteConfig newConfig) {\n+\t\t\t\t\t\tif (newURI == null || !newURI.equals(validated)) {\n+\t\t\t\t\t\t\tvalidated = null;\n+\t\t\t\t\t\t\tsetPageComplete(false);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t});\n \t\tbranchChangeListeners = new ArrayList<BranchChangeListener>(3);\n \t}\n \n@@ -190,12 +192,7 @@ class SourceBranchPage extends WizardPage {\n \n \tprivate void revalidate() {\n \t\tfinal URIish newURI;\n-\t\ttry {\n-\t\t\tnewURI = sourcePage.getURI();\n-\t\t} catch (URISyntaxException e) {\n-\t\t\ttransportError(e.getReason());\n-\t\t\treturn;\n-\t\t}\n+\t\tnewURI = sourcePage.getURI();\n \n \t\tlabel.setText(NLS.bind(UIText.SourceBranchPage_branchList, newURI\n \t\t\t\t.toString()));\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/URIishChangeListener.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/URIishChangeListener.java\ndeleted file mode 100644\nindex e956e95..0000000\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/URIishChangeListener.java\n+++ /dev/null\n@@ -1,20 +0,0 @@\n-/*******************************************************************************\n- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n- *\n- * All rights reserved. This program and the accompanying materials\n- * are made available under the terms of the Eclipse Public License v1.0\n- * See LICENSE for the full license text, also available.\n- *******************************************************************************/\n-package org.spearce.egit.ui.internal.clone;\n-\n-import org.spearce.jgit.transport.URIish;\n-\n-interface URIishChangeListener {\n-\t/**\n-\t * Notify the receiver that the URI has changed.\n-\t * \n-\t * @param newURI\n-\t *            the new URI. Null if the new URI is invalid.\n-\t */\n-\tvoid uriishChanged(URIish newURI);\n-}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionListener.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionListener.java\nnew file mode 100644\nindex 0000000..ef5c33b\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionListener.java\n@@ -0,0 +1,32 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.components;\n+\n+import org.spearce.jgit.transport.RemoteConfig;\n+import org.spearce.jgit.transport.URIish;\n+\n+/**\n+ * Interface for listeners of repository selection events from repository\n+ * selection dialogs.\n+ */\n+public interface RepositorySelectionListener {\n+\t/**\n+\t * Notify the receiver that the repository selection has changed. Each time\n+\t * at least one argument of this call is null, which indicates that it has\n+\t * illegal value or this form of repository selection is not selected.\n+\t * \n+\t * @param newURI\n+\t *            the new specified URI. null if the new URI is invalid or user\n+\t *            chosen to specify repository as remote config instead of URI.\n+\t * @param newConfig\n+\t *            the new remote config. null if user chosen to specify\n+\t *            repository as URI.\n+\t */\n+\tpublic void selectionChanged(URIish newURI, RemoteConfig newConfig);\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionPage.java\nnew file mode 100644\nindex 0000000..a487cb1\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionPage.java\n@@ -0,0 +1,680 @@\n+/*******************************************************************************\n+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>\n+ * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.components;\n+\n+import java.io.File;\n+import java.net.URISyntaxException;\n+import java.util.Iterator;\n+import java.util.LinkedList;\n+import java.util.List;\n+import java.util.regex.Pattern;\n+\n+import org.eclipse.jface.wizard.WizardPage;\n+import org.eclipse.osgi.util.NLS;\n+import org.eclipse.swt.SWT;\n+import org.eclipse.swt.events.ModifyEvent;\n+import org.eclipse.swt.events.ModifyListener;\n+import org.eclipse.swt.events.SelectionAdapter;\n+import org.eclipse.swt.events.SelectionEvent;\n+import org.eclipse.swt.events.VerifyEvent;\n+import org.eclipse.swt.events.VerifyListener;\n+import org.eclipse.swt.layout.GridData;\n+import org.eclipse.swt.layout.GridLayout;\n+import org.eclipse.swt.widgets.Button;\n+import org.eclipse.swt.widgets.Combo;\n+import org.eclipse.swt.widgets.Composite;\n+import org.eclipse.swt.widgets.Control;\n+import org.eclipse.swt.widgets.Group;\n+import org.eclipse.swt.widgets.Label;\n+import org.eclipse.swt.widgets.Text;\n+import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIText;\n+import org.spearce.jgit.transport.RemoteConfig;\n+import org.spearce.jgit.transport.URIish;\n+import org.spearce.jgit.util.FS;\n+\n+/**\n+ * Wizard page that allows the user entering the location of a remote repository\n+ * by specifying URL manually or selecting a preconfigured remote repository.\n+ */\n+public class RepositorySelectionPage extends WizardPage {\n+\tprivate static final int REMOTE_CONFIG_TEXT_MAX_LENGTH = 80;\n+\n+\tprivate static final String DEFAULT_REMOTE_NAME = \"origin\";\n+\n+\tprivate static final int S_GIT = 0;\n+\n+\tprivate static final int S_SSH = 1;\n+\n+\tprivate static final int S_SFTP = 2;\n+\n+\tprivate static final int S_HTTP = 3;\n+\n+\tprivate static final int S_HTTPS = 4;\n+\n+\tprivate static final int S_FTP = 5;\n+\n+\tprivate static final int S_FILE = 6;\n+\n+\tprivate static final String[] DEFAULT_SCHEMES;\n+\tstatic {\n+\t\tDEFAULT_SCHEMES = new String[7];\n+\t\tDEFAULT_SCHEMES[S_GIT] = \"git\";\n+\t\tDEFAULT_SCHEMES[S_SSH] = \"git+ssh\";\n+\t\tDEFAULT_SCHEMES[S_SFTP] = \"sftp\";\n+\t\tDEFAULT_SCHEMES[S_HTTP] = \"http\";\n+\t\tDEFAULT_SCHEMES[S_HTTPS] = \"https\";\n+\t\tDEFAULT_SCHEMES[S_FTP] = \"ftp\";\n+\t\tDEFAULT_SCHEMES[S_FILE] = \"file\";\n+\t}\n+\t\n+\tprivate static void setEnabledRecursively(final Control control,\n+\t\t\tfinal boolean enable) {\n+\t\tcontrol.setEnabled(enable);\n+\t\tif (control instanceof Composite)\n+\t\t\tfor (final Control child : ((Composite) control).getChildren())\n+\t\t\t\tsetEnabledRecursively(child, enable);\n+\t}\n+\n+\tprivate final List<RepositorySelectionListener> selectionListeners;\n+\n+\tprivate final List<RemoteConfig> configuredRemotes;\n+\n+\tprivate Group authGroup;\n+\n+\tprivate Text uriText;\n+\n+\tprivate Text hostText;\n+\n+\tprivate Text pathText;\n+\n+\tprivate Text userText;\n+\n+\tprivate Text passText;\n+\n+\tprivate Combo scheme;\n+\n+\tprivate Text portText;\n+\n+\tprivate int eventDepth;\n+\n+\tprivate URIish uri;\n+\n+\tprivate RemoteConfig remoteConfig;\n+\n+\tprivate RemoteConfig exposedRemoteConfig;\n+\n+\tprivate URIish exposedURI;\n+\n+\tprivate Composite remotePanel;\n+\n+\tprivate Button remoteButton;\n+\n+\tprivate Combo remoteCombo;\n+\n+\tprivate Composite uriPanel;\n+\n+\tprivate Button uriButton;\n+\n+\t/**\n+\t * Create repository selection page, allowing user specifying URI or\n+\t * (optionally) choosing from preconfigured remotes list.\n+\t * <p>\n+\t * Wizard page is created without image, just with text description.\n+\t * \n+\t * @param sourceSelection\n+\t *            true if dialog is used for source selection; false otherwise\n+\t *            (destination selection). This indicates appropriate text\n+\t *            messages.\n+\t * @param configuredRemotes\n+\t *            list of configured remotes that user may select as an\n+\t *            alternative to manual URI specification. Remotes appear in\n+\t *            given order in GUI, with {@value #DEFAULT_REMOTE_NAME} as the\n+\t *            default choice. List may be null or empty - no remotes\n+\t *            configurations appear in this case. Note that the provided\n+\t *            list may be changed by this constructor.\n+\t */\n+\tpublic RepositorySelectionPage(final boolean sourceSelection,\n+\t\t\tfinal List<RemoteConfig> configuredRemotes) {\n+\t\tsuper(RepositorySelectionPage.class.getName());\n+\t\tthis.uri = new URIish();\n+\n+\t\tif (configuredRemotes != null)\n+\t\t\tremoveUnusableRemoteConfigs(configuredRemotes);\n+\t\tif (configuredRemotes == null || configuredRemotes.isEmpty())\n+\t\t\tthis.configuredRemotes = null;\n+\t\telse {\n+\t\t\tthis.configuredRemotes = configuredRemotes;\n+\t\t\tthis.remoteConfig = selectDefaultRemoteConfig();\n+\t\t}\n+\n+\t\tif (sourceSelection) {\n+\t\t\tsetTitle(UIText.RepositorySelectionPage_sourceSelectionTitle);\n+\t\t\tsetDescription(UIText.RepositorySelectionPage_sourceSelectionDescription);\n+\t\t} else {\n+\t\t\tsetTitle(UIText.RepositorySelectionPage_destinationSelectionTitle);\n+\t\t\tsetDescription(UIText.RepositorySelectionPage_destinationSelectionDescription);\n+\t\t}\n+\t\tselectionListeners = new LinkedList<RepositorySelectionListener>();\n+\t}\n+\n+\t/**\n+\t * Create repository selection page, allowing user specifying URI, with no\n+\t * preconfigured remotes selection.\n+\t * \n+\t * @param sourceSelection\n+\t *            true if dialog is used for source selection; false otherwise\n+\t *            (destination selection). This indicates appropriate text\n+\t *            messages.\n+\t */\n+\tpublic RepositorySelectionPage(final boolean sourceSelection) {\n+\t\tthis(sourceSelection, null);\n+\t}\n+\n+\t/**\n+\t * Add {@link RepositorySelectionListener} to list of listeners notified on\n+\t * repository selection change.\n+\t * \n+\t * @param l\n+\t *            listener that will be notified about changes\n+\t */\n+\tpublic void addRepositorySelectionListener(\n+\t\t\tfinal RepositorySelectionListener l) {\n+\t\tselectionListeners.add(l);\n+\t}\n+\n+\tpublic void createControl(final Composite parent) {\n+\t\tfinal Composite panel = new Composite(parent, SWT.NULL);\n+\t\tpanel.setLayout(new GridLayout());\n+\n+\t\tif (configuredRemotes != null)\n+\t\t\tcreateRemotePanel(panel);\n+\t\tcreateUriPanel(panel);\n+\n+\t\tupdateRemoteAndURIPanels();\n+\t\tsetControl(panel);\n+\t\tcheckPage();\n+\t}\n+\n+\tprivate void createRemotePanel(final Composite parent) {\n+\t\tremoteButton = new Button(parent, SWT.RADIO);\n+\t\tremoteButton\n+\t\t\t\t.setText(UIText.RepositorySelectionPage_configuredRemoteChoice\n+\t\t\t\t\t\t+ \":\");\n+\t\tremoteButton.setSelection(true);\n+\n+\t\tremotePanel = new Composite(parent, SWT.NULL);\n+\t\tremotePanel.setLayout(new GridLayout());\n+\t\tfinal GridData gd = new GridData();\n+\t\tgd.grabExcessHorizontalSpace = true;\n+\t\tgd.horizontalAlignment = SWT.FILL;\n+\t\tremotePanel.setLayoutData(gd);\n+\n+\t\tremoteCombo = new Combo(remotePanel, SWT.READ_ONLY | SWT.DROP_DOWN);\n+\t\tfinal String items[] = new String[configuredRemotes.size()];\n+\t\tint i = 0;\n+\t\tfor (final RemoteConfig rc : configuredRemotes)\n+\t\t\titems[i++] = getTextForRemoteConfig(rc);\n+\t\tfinal int defaultIndex = configuredRemotes.indexOf(remoteConfig);\n+\t\tremoteCombo.setItems(items);\n+\t\tremoteCombo.select(defaultIndex);\n+\t\tremoteCombo.addSelectionListener(new SelectionAdapter() {\n+\t\t\t@Override\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tfinal int idx = remoteCombo.getSelectionIndex();\n+\t\t\t\tremoteConfig = configuredRemotes.get(idx);\n+\t\t\t\tcheckPage();\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate void createUriPanel(final Composite parent) {\n+\t\tif (configuredRemotes != null) {\n+\t\t\turiButton = new Button(parent, SWT.RADIO);\n+\t\t\turiButton.setText(UIText.RepositorySelectionPage_uriChoice + \":\");\n+\t\t\turiButton.addSelectionListener(new SelectionAdapter() {\n+\t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\t\t// occurs either on selection or unselection event\n+\t\t\t\t\tupdateRemoteAndURIPanels();\n+\t\t\t\t\tcheckPage();\n+\t\t\t\t}\n+\t\t\t});\n+\t\t}\n+\n+\t\turiPanel = new Composite(parent, SWT.NULL);\n+\t\turiPanel.setLayout(new GridLayout());\n+\t\tfinal GridData gd = new GridData();\n+\t\tgd.grabExcessHorizontalSpace = true;\n+\t\tgd.horizontalAlignment = SWT.FILL;\n+\t\turiPanel.setLayoutData(gd);\n+\n+\t\tcreateLocationGroup(uriPanel);\n+\t\tcreateConnectionGroup(uriPanel);\n+\t\tauthGroup = createAuthenticationGroup(uriPanel);\n+\t}\n+\n+\tprivate void createLocationGroup(final Composite parent) {\n+\t\tfinal Group g = createGroup(parent,\n+\t\t\t\tUIText.RepositorySelectionPage_groupLocation);\n+\n+\t\tnewLabel(g, UIText.RepositorySelectionPage_promptURI + \":\");\n+\t\turiText = new Text(g, SWT.BORDER);\n+\t\turiText.setLayoutData(createFieldGridData());\n+\t\turiText.addModifyListener(new ModifyListener() {\n+\t\t\tpublic void modifyText(final ModifyEvent e) {\n+\t\t\t\ttry {\n+\t\t\t\t\teventDepth++;\n+\t\t\t\t\tif (eventDepth != 1)\n+\t\t\t\t\t\treturn;\n+\n+\t\t\t\t\tfinal URIish u = new URIish(uriText.getText());\n+\t\t\t\t\tsafeSet(hostText, u.getHost());\n+\t\t\t\t\tsafeSet(pathText, u.getPath());\n+\t\t\t\t\tsafeSet(userText, u.getUser());\n+\t\t\t\t\tsafeSet(passText, u.getPass());\n+\n+\t\t\t\t\tif (u.getPort() > 0)\n+\t\t\t\t\t\tportText.setText(Integer.toString(u.getPort()));\n+\t\t\t\t\telse\n+\t\t\t\t\t\tportText.setText(\"\");\n+\n+\t\t\t\t\tif (isFile(u))\n+\t\t\t\t\t\tscheme.select(S_FILE);\n+\t\t\t\t\telse if (isSSH(u))\n+\t\t\t\t\t\tscheme.select(S_SSH);\n+\t\t\t\t\telse {\n+\t\t\t\t\t\tfor (int i = 0; i < DEFAULT_SCHEMES.length; i++) {\n+\t\t\t\t\t\t\tif (DEFAULT_SCHEMES[i].equals(u.getScheme())) {\n+\t\t\t\t\t\t\t\tscheme.select(i);\n+\t\t\t\t\t\t\t\tbreak;\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\n+\t\t\t\t\tupdateAuthGroup();\n+\t\t\t\t\turi = u;\n+\t\t\t\t} catch (URISyntaxException err) {\n+\t\t\t\t\t// leave uriText as it is, but clean up underlying uri and\n+\t\t\t\t\t// decomposed fields\n+\t\t\t\t\turi = new URIish();\n+\t\t\t\t\thostText.setText(\"\");\n+\t\t\t\t\tpathText.setText(\"\");\n+\t\t\t\t\tuserText.setText(\"\");\n+\t\t\t\t\tpassText.setText(\"\");\n+\t\t\t\t\tportText.setText(\"\");\n+\t\t\t\t\tscheme.select(0);\n+\t\t\t\t} finally {\n+\t\t\t\t\teventDepth--;\n+\t\t\t\t}\n+\t\t\t\tcheckPage();\n+\t\t\t}\n+\t\t});\n+\n+\t\tnewLabel(g, UIText.RepositorySelectionPage_promptHost + \":\");\n+\t\thostText = new Text(g, SWT.BORDER);\n+\t\thostText.setLayoutData(createFieldGridData());\n+\t\thostText.addModifyListener(new ModifyListener() {\n+\t\t\tpublic void modifyText(final ModifyEvent e) {\n+\t\t\t\tsetURI(uri.setHost(nullString(hostText.getText())));\n+\t\t\t}\n+\t\t});\n+\n+\t\tnewLabel(g, UIText.RepositorySelectionPage_promptPath + \":\");\n+\t\tpathText = new Text(g, SWT.BORDER);\n+\t\tpathText.setLayoutData(createFieldGridData());\n+\t\tpathText.addModifyListener(new ModifyListener() {\n+\t\t\tpublic void modifyText(final ModifyEvent e) {\n+\t\t\t\tsetURI(uri.setPath(nullString(pathText.getText())));\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate Group createAuthenticationGroup(final Composite parent) {\n+\t\tfinal Group g = createGroup(parent,\n+\t\t\t\tUIText.RepositorySelectionPage_groupAuthentication);\n+\n+\t\tnewLabel(g, UIText.RepositorySelectionPage_promptUser + \":\");\n+\t\tuserText = new Text(g, SWT.BORDER);\n+\t\tuserText.setLayoutData(createFieldGridData());\n+\t\tuserText.addModifyListener(new ModifyListener() {\n+\t\t\tpublic void modifyText(final ModifyEvent e) {\n+\t\t\t\tsetURI(uri.setUser(nullString(userText.getText())));\n+\t\t\t}\n+\t\t});\n+\n+\t\tnewLabel(g, UIText.RepositorySelectionPage_promptPassword + \":\");\n+\t\tpassText = new Text(g, SWT.BORDER | SWT.PASSWORD);\n+\t\tpassText.setLayoutData(createFieldGridData());\n+\t\treturn g;\n+\t}\n+\n+\tprivate void createConnectionGroup(final Composite parent) {\n+\t\tfinal Group g = createGroup(parent,\n+\t\t\t\tUIText.RepositorySelectionPage_groupConnection);\n+\n+\t\tnewLabel(g, UIText.RepositorySelectionPage_promptScheme + \":\");\n+\t\tscheme = new Combo(g, SWT.DROP_DOWN | SWT.READ_ONLY);\n+\t\tscheme.setItems(DEFAULT_SCHEMES);\n+\t\tscheme.addSelectionListener(new SelectionAdapter() {\n+\t\t\tpublic void widgetSelected(final SelectionEvent e) {\n+\t\t\t\tfinal int idx = scheme.getSelectionIndex();\n+\t\t\t\tif (idx < 0)\n+\t\t\t\t\tsetURI(uri.setScheme(null));\n+\t\t\t\telse\n+\t\t\t\t\tsetURI(uri.setScheme(nullString(scheme.getItem(idx))));\n+\t\t\t\tupdateAuthGroup();\n+\t\t\t}\n+\t\t});\n+\n+\t\tnewLabel(g, UIText.RepositorySelectionPage_promptPort + \":\");\n+\t\tportText = new Text(g, SWT.BORDER);\n+\t\tportText.addVerifyListener(new VerifyListener() {\n+\t\t\tfinal Pattern p = Pattern.compile(\"^(?:[1-9][0-9]*)?$\");\n+\n+\t\t\tpublic void verifyText(final VerifyEvent e) {\n+\t\t\t\tfinal String v = portText.getText();\n+\t\t\t\te.doit = p.matcher(\n+\t\t\t\t\t\tv.substring(0, e.start) + e.text + v.substring(e.end))\n+\t\t\t\t\t\t.matches();\n+\t\t\t}\n+\t\t});\n+\t\tportText.addModifyListener(new ModifyListener() {\n+\t\t\tpublic void modifyText(final ModifyEvent e) {\n+\t\t\t\tfinal String val = nullString(portText.getText());\n+\t\t\t\tif (val == null)\n+\t\t\t\t\tsetURI(uri.setPort(-1));\n+\t\t\t\telse {\n+\t\t\t\t\ttry {\n+\t\t\t\t\t\tsetURI(uri.setPort(Integer.parseInt(val)));\n+\t\t\t\t\t} catch (NumberFormatException err) {\n+\t\t\t\t\t\t// Ignore it for now.\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate static Group createGroup(final Composite parent, final String text) {\n+\t\tfinal Group g = new Group(parent, SWT.NONE);\n+\t\tfinal GridLayout layout = new GridLayout();\n+\t\tlayout.numColumns = 2;\n+\t\tg.setLayout(layout);\n+\t\tg.setText(text);\n+\t\tfinal GridData gd = new GridData();\n+\t\tgd.grabExcessHorizontalSpace = true;\n+\t\tgd.horizontalAlignment = SWT.FILL;\n+\t\tg.setLayoutData(gd);\n+\t\treturn g;\n+\t}\n+\n+\tprivate static void newLabel(final Group g, final String text) {\n+\t\tnew Label(g, SWT.NULL).setText(text);\n+\t}\n+\n+\tprivate static GridData createFieldGridData() {\n+\t\treturn new GridData(SWT.FILL, SWT.DEFAULT, true, false);\n+\t}\n+\n+\t/**\n+\t * @return the URI entered in the Wizard page. null if URI is invalid or\n+\t *         user chosen to select remote config instead of providing direct\n+\t *         URI.\n+\t */\n+\tpublic URIish getURI() {\n+\t\treturn exposedURI;\n+\t}\n+\n+\t/**\n+\t * @return the selected remote configuration in the Wizard page. null if\n+\t *         user chosen to select repository as URI.\n+\t */\n+\tpublic RemoteConfig getRemoteConfig() {\n+\t\treturn exposedRemoteConfig;\n+\t}\n+\n+\tprivate static boolean isGIT(final URIish uri) {\n+\t\treturn \"git\".equals(uri.getScheme());\n+\t}\n+\n+\tprivate static boolean isFile(final URIish uri) {\n+\t\tif (\"file\".equals(uri.getScheme()))\n+\t\t\treturn true;\n+\t\tif (uri.getHost() != null || uri.getPort() > 0 || uri.getUser() != null\n+\t\t\t\t|| uri.getPass() != null || uri.getPath() == null)\n+\t\t\treturn false;\n+\t\tif (uri.getScheme() == null)\n+\t\t\treturn FS.resolve(new File(\".\"), uri.getPath()).isDirectory();\n+\t\treturn false;\n+\t}\n+\n+\tprivate static boolean isSSH(final URIish uri) {\n+\t\tif (!uri.isRemote())\n+\t\t\treturn false;\n+\t\tfinal String scheme = uri.getScheme();\n+\t\tif (\"ssh\".equals(scheme))\n+\t\t\treturn true;\n+\t\tif (\"ssh+git\".equals(scheme))\n+\t\t\treturn true;\n+\t\tif (\"git+ssh\".equals(scheme))\n+\t\t\treturn true;\n+\t\tif (scheme == null && uri.getHost() != null && uri.getPath() != null)\n+\t\t\treturn true;\n+\t\treturn false;\n+\t}\n+\n+\tprivate static String nullString(final String value) {\n+\t\tif (value == null)\n+\t\t\treturn null;\n+\t\tfinal String v = value.trim();\n+\t\treturn v.length() == 0 ? null : v;\n+\t}\n+\n+\tprivate static void safeSet(final Text text, final String value) {\n+\t\ttext.setText(value != null ? value : \"\");\n+\t}\n+\n+\tprivate boolean isURISelected() {\n+\t\treturn configuredRemotes == null || uriButton.getSelection();\n+\t}\n+\n+\tprivate void setURI(final URIish u) {\n+\t\ttry {\n+\t\t\teventDepth++;\n+\t\t\tif (eventDepth == 1) {\n+\t\t\t\turi = u;\n+\t\t\t\turiText.setText(uri.toString());\n+\t\t\t\tcheckPage();\n+\t\t\t}\n+\t\t} finally {\n+\t\t\teventDepth--;\n+\t\t}\n+\t}\n+\n+\tprivate static void removeUnusableRemoteConfigs(\n+\t\t\tfinal List<RemoteConfig> remotes) {\n+\t\tfinal Iterator<RemoteConfig> iter = remotes.iterator();\n+\t\twhile (iter.hasNext()) {\n+\t\t\tfinal RemoteConfig rc = iter.next();\n+\t\t\tif (rc.getURIs().isEmpty())\n+\t\t\t\titer.remove();\n+\t\t}\n+\t}\n+\n+\tprivate RemoteConfig selectDefaultRemoteConfig() {\n+\t\tfor (final RemoteConfig rc : configuredRemotes)\n+\t\t\tif (getTextForRemoteConfig(rc) == DEFAULT_REMOTE_NAME)\n+\t\t\t\treturn rc;\n+\t\treturn configuredRemotes.get(0);\n+\t}\n+\n+\tprivate static String getTextForRemoteConfig(final RemoteConfig rc) {\n+\t\tfinal StringBuilder sb = new StringBuilder(rc.getName());\n+\t\tsb.append(\": \");\n+\t\tboolean first = true;\n+\t\tfor (final URIish u : rc.getURIs()) {\n+\t\t\tfinal String uString = u.toString();\n+\t\t\tif (first)\n+\t\t\t\tfirst = false;\n+\t\t\telse {\n+\t\t\t\tsb.append(\", \");\n+\t\t\t\tif (sb.length() + uString.length() > REMOTE_CONFIG_TEXT_MAX_LENGTH) {\n+\t\t\t\t\tsb.append(\"...\");\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tsb.append(uString);\n+\t\t}\n+\t\treturn sb.toString();\n+\t}\n+\n+\tprivate void checkPage() {\n+\t\tif (isURISelected()) {\n+\t\t\tassert uri != null;\n+\t\t\tif (uriText.getText().length() == 0) {\n+\t\t\t\tselectionIncomplete(null);\n+\t\t\t\treturn;\n+\t\t\t}\n+\n+\t\t\ttry {\n+\t\t\t\tfinal URIish finalURI = new URIish(uriText.getText());\n+\t\t\t\tString proto = finalURI.getScheme();\n+\t\t\t\tif (proto == null && scheme.getSelectionIndex() >= 0)\n+\t\t\t\t\tproto = scheme.getItem(scheme.getSelectionIndex());\n+\n+\t\t\t\tif (uri.getPath() == null) {\n+\t\t\t\t\tselectionIncomplete(NLS.bind(\n+\t\t\t\t\t\t\tUIText.RepositorySelectionPage_fieldRequired,\n+\t\t\t\t\t\t\tUIText.RepositorySelectionPage_promptPath, proto));\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\n+\t\t\t\tif (isFile(finalURI)) {\n+\t\t\t\t\tString badField = null;\n+\t\t\t\t\tif (uri.getHost() != null)\n+\t\t\t\t\t\tbadField = UIText.RepositorySelectionPage_promptHost;\n+\t\t\t\t\telse if (uri.getUser() != null)\n+\t\t\t\t\t\tbadField = UIText.RepositorySelectionPage_promptUser;\n+\t\t\t\t\telse if (uri.getPass() != null)\n+\t\t\t\t\t\tbadField = UIText.RepositorySelectionPage_promptPassword;\n+\t\t\t\t\tif (badField != null) {\n+\t\t\t\t\t\tselectionIncomplete(NLS\n+\t\t\t\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\t\t\t\tUIText.RepositorySelectionPage_fieldNotSupported,\n+\t\t\t\t\t\t\t\t\t\tbadField, proto));\n+\t\t\t\t\t\treturn;\n+\t\t\t\t\t}\n+\n+\t\t\t\t\tfinal File d = FS.resolve(new File(\".\"), uri.getPath());\n+\t\t\t\t\tif (!d.exists()) {\n+\t\t\t\t\t\tselectionIncomplete(NLS.bind(\n+\t\t\t\t\t\t\t\tUIText.RepositorySelectionPage_fileNotFound, d\n+\t\t\t\t\t\t\t\t\t\t.getAbsolutePath()));\n+\t\t\t\t\t\treturn;\n+\t\t\t\t\t}\n+\n+\t\t\t\t\tselectionComplete(finalURI, null);\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\n+\t\t\t\tif (uri.getHost() == null) {\n+\t\t\t\t\tselectionIncomplete(NLS.bind(\n+\t\t\t\t\t\t\tUIText.RepositorySelectionPage_fieldRequired,\n+\t\t\t\t\t\t\tUIText.RepositorySelectionPage_promptHost, proto));\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\n+\t\t\t\tif (isGIT(finalURI)) {\n+\t\t\t\t\tString badField = null;\n+\t\t\t\t\tif (uri.getUser() != null)\n+\t\t\t\t\t\tbadField = UIText.RepositorySelectionPage_promptUser;\n+\t\t\t\t\telse if (uri.getPass() != null)\n+\t\t\t\t\t\tbadField = UIText.RepositorySelectionPage_promptPassword;\n+\t\t\t\t\tif (badField != null) {\n+\t\t\t\t\t\tselectionIncomplete(NLS\n+\t\t\t\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\t\t\t\tUIText.RepositorySelectionPage_fieldNotSupported,\n+\t\t\t\t\t\t\t\t\t\tbadField, proto));\n+\t\t\t\t\t\treturn;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\n+\t\t\t\tselectionComplete(finalURI, null);\n+\t\t\t\treturn;\n+\t\t\t} catch (URISyntaxException e) {\n+\t\t\t\tselectionIncomplete(e.getReason());\n+\t\t\t\treturn;\n+\t\t\t} catch (Exception e) {\n+\t\t\t\tActivator.logError(\"Error validating \" + getClass().getName(),\n+\t\t\t\t\t\te);\n+\t\t\t\tselectionIncomplete(UIText.RepositorySelectionPage_internalError);\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tassert remoteButton.getSelection();\n+\t\t\tselectionComplete(null, remoteConfig);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\tprivate void selectionIncomplete(final String errorMessage) {\n+\t\tsetExposedSelection(null, null);\n+\t\tsetErrorMessage(errorMessage);\n+\t\tsetPageComplete(false);\n+\t}\n+\n+\tprivate void selectionComplete(final URIish u, final RemoteConfig rc) {\n+\t\tsetExposedSelection(u, rc);\n+\t\tsetErrorMessage(null);\n+\t\tsetPageComplete(true);\n+\t}\n+\n+\tprivate void setExposedSelection(final URIish u, final RemoteConfig rc) {\n+\t\tif (u == exposedURI && rc == exposedRemoteConfig) // nothing changed\n+\t\t\treturn;\n+\t\tthis.exposedURI = u;\n+\t\tthis.exposedRemoteConfig = rc;\n+\n+\t\tfor (final RepositorySelectionListener l : selectionListeners)\n+\t\t\tl.selectionChanged(u, rc);\n+\t}\n+\n+\tprivate void updateRemoteAndURIPanels() {\n+\t\tsetEnabledRecursively(uriPanel, isURISelected());\n+\t\tif (uriPanel.getEnabled())\n+\t\t\tupdateAuthGroup();\n+\t\tif (configuredRemotes != null)\n+\t\t\tsetEnabledRecursively(remotePanel, !isURISelected());\n+\t}\n+\n+\tprivate void updateAuthGroup() {\n+\t\tswitch (scheme.getSelectionIndex()) {\n+\t\tcase S_GIT:\n+\t\t\thostText.setEnabled(true);\n+\t\t\tportText.setEnabled(true);\n+\t\t\tsetEnabledRecursively(authGroup, false);\n+\t\t\tbreak;\n+\t\tcase S_SSH:\n+\t\tcase S_SFTP:\n+\t\tcase S_HTTP:\n+\t\tcase S_HTTPS:\n+\t\tcase S_FTP:\n+\t\t\thostText.setEnabled(true);\n+\t\t\tportText.setEnabled(true);\n+\t\t\tsetEnabledRecursively(authGroup, true);\n+\t\t\tbreak;\n+\t\tcase S_FILE:\n+\t\t\thostText.setEnabled(false);\n+\t\t\tportText.setEnabled(false);\n+\t\t\tsetEnabledRecursively(authGroup, false);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex 55b1348..857568a 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -31,23 +31,28 @@ GitCloneWizard_title=Import Git Repository\n GitCloneWizard_jobName=Cloning from {0}\n GitCloneWizard_failed=Git repository clone failed.\n \n-CloneSourcePage_title=Source Git Repository\n-CloneSourcePage_description=Enter the location of the repository to be cloned.\n-CloneSourcePage_groupLocation=Location\n-CloneSourcePage_groupAuthentication=Authentication\n-CloneSourcePage_groupConnection=Connection\n-CloneSourcePage_promptURI=URI\n-CloneSourcePage_promptHost=Host\n-CloneSourcePage_promptPath=Repository path\n-CloneSourcePage_promptUser=User\n-CloneSourcePage_promptPassword=Password\n-CloneSourcePage_promptScheme=Protocol\n-CloneSourcePage_promptPort=Port\n-CloneSourcePage_fieldRequired={0} required for {1} protocol.\n-CloneSourcePage_fieldNotSupported={0} not supported on {1} protocol.\n-CloneSourcePage_fileNotFound={0} does not exist.\n-CloneSourcePage_internalError=Internal error; consult Eclipse error log.\n+RepositorySelectionPage_sourceSelectionTitle=Source Git Repository\n+RepositorySelectionPage_sourceSelectionDescription=Enter the location of the source repository.\n+RepositorySelectionPage_destinationSelectionTitle=Destination Git Repository\n+RepositorySelectionPage_destinationSelectionDescription=Enter the location of the destination repository.\n+RepositorySelectionPage_configuredRemoteChoice=Configured remote repository\n+RepositorySelectionPage_uriChoice=Custom URI\n+RepositorySelectionPage_groupLocation=Location\n+RepositorySelectionPage_groupAuthentication=Authentication\n+RepositorySelectionPage_groupConnection=Connection\n+RepositorySelectionPage_promptURI=URI\n+RepositorySelectionPage_promptHost=Host\n+RepositorySelectionPage_promptPath=Repository path\n+RepositorySelectionPage_promptUser=User\n+RepositorySelectionPage_promptPassword=Password\n+RepositorySelectionPage_promptScheme=Protocol\n+RepositorySelectionPage_promptPort=Port\n+RepositorySelectionPage_fieldRequired={0} required for {1} protocol.\n+RepositorySelectionPage_fieldNotSupported={0} not supported on {1} protocol.\n+RepositorySelectionPage_fileNotFound={0} does not exist.\n+RepositorySelectionPage_internalError=Internal error; consult Eclipse error log.\n \n+SourceBranchPage_title=Source Git Repository\n SourceBranchPage_branchList=Branches of {0}:\n SourceBranchPage_selectAll=Select All\n SourceBranchPage_selectNone=Deselect All\n-- \n1.5.6.3\n"},{"id":"87481","messageId":"1219005852-21496-20-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-19-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 19/31] Clone wizard and related: refactor, clean-up, fixes or improvements","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:00Z","receivedAt":"2008-08-17T20:44:00Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"At some stage, it was hard to change or improve anything in Clone wizard\nor reuse some of its components without some global refactor or clean-up.\n\nThis changeset introduces some common patterns of handling internal pages\nvalidation and inter-pages dependencies, it tries to address existing\nissues (complicated internal dependencies, redundant asymmetrical code)\nwith clone code that IMHO were making it hard to maintain or modify.\n\nSome of these changes introduce new behavior (fixes or improvements),\nas it was hard for me to separate this in my iterative process...\n\n- Remove mess of using both setPageComplete() and custom isPageComplete().\n  Now, checkPage() is used (calling setPageComplete()) instead - for\n  internal validation, while direct setPageComplete() calls are used in\n  special situations like handling inter-pages dependencies.\n- RepositorySelection class introduces encapsulated result of\n  RepositorySelectionPage with helper methods working on that result.\n- SelectionChangeListener became new common interface for notifying other\n  (dependent) pages about selection changes in current page.\n  BaseWizardPage is helper abstract class handling these listeners.\n- User can move backward in wizard, change something, revert this change,\n  then subsequent pages doesn't set page complete to false. It's now\n  supported in common way in whole wizard by *selectionEquals() methods.\n- Real-time checking for some fields of destination page is introduced.\n- Minor changes like renames or some simplifications, forgotten changes.\n\nSome of this commit resulting components, may be reused in other places,\nthat's why they are actually in .components package.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../ui/internal/clone/CloneDestinationPage.java    |  115 ++++++-----\n .../egit/ui/internal/clone/GitCloneWizard.java     |    3 +-\n .../egit/ui/internal/clone/SourceBranchPage.java   |  212 ++++++++++----------\n .../ui/internal/components/BaseWizardPage.java     |   58 ++++++\n .../internal/components/RepositorySelection.java   |  133 ++++++++++++\n .../components/RepositorySelectionListener.java    |   32 ---\n .../components/RepositorySelectionPage.java        |   61 ++----\n .../SelectionChangeListener.java}                  |   16 +-\n 8 files changed, 397 insertions(+), 233 deletions(-)\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/BaseWizardPage.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelection.java\n delete mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionListener.java\n rename org.spearce.egit.ui/src/org/spearce/egit/ui/internal/{clone/BranchChangeListener.java => components/SelectionChangeListener.java} (50%)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\nindex 249bfd3..01bf54a 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\n@@ -1,6 +1,7 @@\n /*******************************************************************************\n  * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>\n  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n  *\n  * All rights reserved. This program and the accompanying materials\n  * are made available under the terms of the Eclipse Public License v1.0\n@@ -9,6 +10,7 @@\n package org.spearce.egit.ui.internal.clone;\n \n import java.io.File;\n+import java.util.List;\n \n import org.eclipse.core.resources.ResourcesPlugin;\n import org.eclipse.jface.wizard.WizardPage;\n@@ -16,8 +18,8 @@ import org.eclipse.osgi.util.NLS;\n import org.eclipse.swt.SWT;\n import org.eclipse.swt.events.ModifyEvent;\n import org.eclipse.swt.events.ModifyListener;\n+import org.eclipse.swt.events.SelectionAdapter;\n import org.eclipse.swt.events.SelectionEvent;\n-import org.eclipse.swt.events.SelectionListener;\n import org.eclipse.swt.layout.GridData;\n import org.eclipse.swt.layout.GridLayout;\n import org.eclipse.swt.widgets.Button;\n@@ -28,12 +30,11 @@ import org.eclipse.swt.widgets.Group;\n import org.eclipse.swt.widgets.Label;\n import org.eclipse.swt.widgets.Text;\n import org.spearce.egit.ui.UIText;\n-import org.spearce.egit.ui.internal.components.RepositorySelectionListener;\n+import org.spearce.egit.ui.internal.components.RepositorySelection;\n import org.spearce.egit.ui.internal.components.RepositorySelectionPage;\n+import org.spearce.egit.ui.internal.components.SelectionChangeListener;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.Ref;\n-import org.spearce.jgit.transport.RemoteConfig;\n-import org.spearce.jgit.transport.URIish;\n \n /**\n  * Wizard page that allows the user entering the location of a repository to be\n@@ -44,7 +45,11 @@ class CloneDestinationPage extends WizardPage {\n \n \tprivate final SourceBranchPage branchPage;\n \n-\tprivate URIish validated;\n+\tprivate RepositorySelection validatedRepoSelection;\n+\n+\tprivate List<Ref> validatedSelectedBranches;\n+\n+\tprivate Ref validatedHEAD;\n \n \tprivate Combo initialBranch;\n \n@@ -60,18 +65,13 @@ class CloneDestinationPage extends WizardPage {\n \n \t\tsetTitle(UIText.CloneDestinationPage_title);\n \n-\t\tsourcePage.addRepositorySelectionListener(new RepositorySelectionListener() {\n-\t\t\tpublic void selectionChanged(final URIish newURI,\n-\t\t\t\t\tfinal RemoteConfig newConfig) {\n-\t\t\t\tif (newURI == null || !newURI.equals(validated))\n-\t\t\t\t\tsetPageComplete(false);\n+\t\tfinal SelectionChangeListener listener = new SelectionChangeListener() {\n+\t\t\tpublic void selectionChanged() {\n+\t\t\t\tcheckPreviousPagesSelections();\n \t\t\t}\n-\t\t});\n-\t\tbranchPage.addBranchChangeListener(new BranchChangeListener() {\n-\t\t\tpublic void branchesChanged() {\n-\t\t\t\tsetPageComplete(false);\n-\t\t\t}\n-\t\t});\n+\t\t};\n+\t\tsourcePage.addSelectionListener(listener);\n+\t\tbranchPage.addSelectionListener(listener);\n \t}\n \n \tpublic void createControl(final Composite parent) {\n@@ -84,7 +84,7 @@ class CloneDestinationPage extends WizardPage {\n \t\tcreateConfigGroup(panel);\n \n \t\tsetControl(panel);\n-\t\tsetPageComplete(isPageComplete());\n+\t\tcheckPage();\n \t}\n \n \t@Override\n@@ -94,6 +94,15 @@ class CloneDestinationPage extends WizardPage {\n \t\tsuper.setVisible(visible);\n \t}\n \n+\tprivate void checkPreviousPagesSelections() {\n+\t\tif (!sourcePage.selectionEquals(validatedRepoSelection)\n+\t\t\t\t|| !branchPage.selectionEquals(validatedSelectedBranches,\n+\t\t\t\t\t\tvalidatedHEAD))\n+\t\t\tsetPageComplete(false);\n+\t\telse\n+\t\t\tcheckPage();\n+\t}\n+\n \tprivate void createDestinationGroup(final Composite parent) {\n \t\tfinal Group g = createGroup(parent,\n \t\t\t\tUIText.CloneDestinationPage_groupDestination);\n@@ -108,16 +117,12 @@ class CloneDestinationPage extends WizardPage {\n \t\tdirectoryText.setLayoutData(createFieldGridData());\n \t\tdirectoryText.addModifyListener(new ModifyListener() {\n \t\t\tpublic void modifyText(final ModifyEvent e) {\n-\t\t\t\tsetPageComplete(isPageComplete());\n+\t\t\t\tcheckPage();\n \t\t\t}\n \t\t});\n \t\tfinal Button b = new Button(p, SWT.PUSH);\n \t\tb.setText(UIText.CloneDestinationPage_browseButton);\n-\t\tb.addSelectionListener(new SelectionListener() {\n-\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n-\t\t\t\t// Do nothing.\n-\t\t\t}\n-\n+\t\tb.addSelectionListener(new SelectionAdapter() {\n \t\t\tpublic void widgetSelected(final SelectionEvent e) {\n \t\t\t\tfinal FileDialog d;\n \n@@ -136,6 +141,12 @@ class CloneDestinationPage extends WizardPage {\n \t\tnewLabel(g, UIText.CloneDestinationPage_promptInitialBranch + \":\");\n \t\tinitialBranch = new Combo(g, SWT.DROP_DOWN | SWT.READ_ONLY);\n \t\tinitialBranch.setLayoutData(createFieldGridData());\n+\t\tinitialBranch.addSelectionListener(new SelectionAdapter() {\n+\t\t\t@Override\n+\t\t\tpublic void widgetSelected(final SelectionEvent e) {\n+\t\t\t\tcheckPage();\n+\t\t\t}\n+\t\t});\n \t}\n \n \tprivate void createConfigGroup(final Composite parent) {\n@@ -146,6 +157,11 @@ class CloneDestinationPage extends WizardPage {\n \t\tremoteText = new Text(g, SWT.BORDER);\n \t\tremoteText.setText(\"origin\");\n \t\tremoteText.setLayoutData(createFieldGridData());\n+\t\tremoteText.addModifyListener(new ModifyListener() {\n+\t\t\tpublic void modifyText(ModifyEvent e) {\n+\t\t\t\tcheckPage();\n+\t\t\t}\n+\t\t});\n \t}\n \n \tprivate static Group createGroup(final Composite parent, final String text) {\n@@ -193,50 +209,55 @@ class CloneDestinationPage extends WizardPage {\n \t\treturn remoteText.getText();\n \t}\n \n-\t@Override\n-\tpublic boolean isPageComplete() {\n+\t/**\n+\t * Check internal state for page completion status.\n+\t */\n+\tprivate void checkPage() {\n \t\tfinal String dstpath = directoryText.getText();\n \t\tif (dstpath.length() == 0) {\n \t\t\tsetErrorMessage(NLS.bind(UIText.CloneDestinationPage_fieldRequired,\n \t\t\t\t\tUIText.CloneDestinationPage_promptDirectory));\n-\t\t\treturn false;\n+\t\t\tsetPageComplete(false);\n+\t\t\treturn;\n \t\t}\n \t\tif (new File(dstpath).exists()) {\n \t\t\tsetErrorMessage(NLS.bind(UIText.CloneDestinationPage_errorExists,\n \t\t\t\t\tnew File(dstpath).getName()));\n-\t\t\treturn false;\n+\t\t\tsetPageComplete(false);\n+\t\t\treturn;\n \t\t}\n \t\tif (initialBranch.getSelectionIndex() < 0) {\n \t\t\tsetErrorMessage(NLS.bind(UIText.CloneDestinationPage_fieldRequired,\n \t\t\t\t\tUIText.CloneDestinationPage_promptInitialBranch));\n-\t\t\treturn false;\n+\t\t\tsetPageComplete(false);\n+\t\t\treturn;\n \t\t}\n \t\tif (remoteText.getText().length() == 0) {\n \t\t\tsetErrorMessage(NLS.bind(UIText.CloneDestinationPage_fieldRequired,\n \t\t\t\t\tUIText.CloneDestinationPage_promptRemoteName));\n-\t\t\treturn false;\n+\t\t\tsetPageComplete(false);\n+\t\t\treturn;\n \t\t}\n \n \t\tsetErrorMessage(null);\n-\t\treturn true;\n+\t\tsetPageComplete(true);\n \t}\n \n \tprivate void revalidate() {\n-\t\tURIish newURI = null;\n-\t\tnewURI = sourcePage.getURI();\n-\t\tvalidated = newURI;\n-\n-\t\tif (newURI == null || !newURI.equals(validated)) {\n-\t\t\tfinal String n = getSuggestedName();\n-\t\t\tsetDescription(NLS.bind(UIText.CloneDestinationPage_description,\n-\t\t\t\t\tn != null ? n : \"<unknown>\"));\n-\n-\t\t\tif (n != null) {\n-\t\t\t\tdirectoryText.setText(new File(ResourcesPlugin.getWorkspace()\n-\t\t\t\t\t\t.getRoot().getRawLocation().toFile(), n)\n-\t\t\t\t\t\t.getAbsolutePath());\n-\t\t\t}\n+\t\tif (sourcePage.selectionEquals(validatedRepoSelection)\n+\t\t\t\t&& branchPage.selectionEquals(validatedSelectedBranches,\n+\t\t\t\t\t\tvalidatedHEAD)) {\n+\t\t\tcheckPage();\n+\t\t\treturn;\n \t\t}\n+\t\tvalidatedRepoSelection = sourcePage.getSelection();\n+\t\tvalidatedSelectedBranches = branchPage.getSelectedBranches();\n+\t\tvalidatedHEAD = branchPage.getHEAD();\n+\n+\t\tfinal String n = getSuggestedName();\n+\t\tsetDescription(NLS.bind(UIText.CloneDestinationPage_description, n));\n+\t\tdirectoryText.setText(new File(ResourcesPlugin.getWorkspace().getRoot()\n+\t\t\t\t.getRawLocation().toFile(), n).getAbsolutePath());\n \n \t\tinitialBranch.removeAll();\n \t\tfinal Ref head = branchPage.getHEAD();\n@@ -250,13 +271,11 @@ class CloneDestinationPage extends WizardPage {\n \t\t\tinitialBranch.add(name);\n \t\t}\n \t\tinitialBranch.select(newix);\n+\t\tcheckPage();\n \t}\n \n \tprivate String getSuggestedName() {\n-\t\tif (validated == null)\n-\t\t\treturn null;\n-\n-\t\tString path = validated.getPath();\n+\t\tString path = validatedRepoSelection.getURI().getPath();\n \t\tint s = path.lastIndexOf('/');\n \t\tif (s != -1)\n \t\t\tpath = path.substring(s + 1);\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\nindex 58c169a..9c1d691 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\n@@ -1,6 +1,7 @@\n /*******************************************************************************\n  * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>\n  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n  *\n  * All rights reserved. This program and the accompanying materials\n  * are made available under the terms of the Eclipse Public License v1.0\n@@ -71,7 +72,7 @@ public class GitCloneWizard extends Wizard implements IImportWizard {\n \t\tfinal Repository db;\n \t\tfinal RemoteConfig origin;\n \n-\t\turi = cloneSource.getURI();\n+\t\turi = cloneSource.getSelection().getURI();\n \n \t\tfinal File workdir = cloneDestination.getDestinationFile();\n \t\tfinal String branch = cloneDestination.getInitialBranch();\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java\nindex 1768dbd..7710612 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java\n@@ -1,6 +1,7 @@\n /*******************************************************************************\n  * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>\n  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n  *\n  * All rights reserved. This program and the accompanying materials\n  * are made available under the terms of the Eclipse Public License v1.0\n@@ -23,49 +24,44 @@ import org.eclipse.core.runtime.OperationCanceledException;\n import org.eclipse.core.runtime.Status;\n import org.eclipse.jface.dialogs.ErrorDialog;\n import org.eclipse.jface.operation.IRunnableWithProgress;\n-import org.eclipse.jface.wizard.WizardPage;\n import org.eclipse.osgi.util.NLS;\n import org.eclipse.swt.SWT;\n+import org.eclipse.swt.events.SelectionAdapter;\n import org.eclipse.swt.events.SelectionEvent;\n-import org.eclipse.swt.events.SelectionListener;\n import org.eclipse.swt.layout.GridData;\n import org.eclipse.swt.layout.GridLayout;\n import org.eclipse.swt.layout.RowLayout;\n import org.eclipse.swt.widgets.Button;\n import org.eclipse.swt.widgets.Composite;\n-import org.eclipse.swt.widgets.Event;\n import org.eclipse.swt.widgets.Label;\n-import org.eclipse.swt.widgets.Listener;\n import org.eclipse.swt.widgets.Table;\n import org.eclipse.swt.widgets.TableItem;\n import org.spearce.egit.ui.Activator;\n import org.spearce.egit.ui.UIText;\n-import org.spearce.egit.ui.internal.components.RepositorySelectionListener;\n+import org.spearce.egit.ui.internal.components.BaseWizardPage;\n+import org.spearce.egit.ui.internal.components.RepositorySelection;\n import org.spearce.egit.ui.internal.components.RepositorySelectionPage;\n+import org.spearce.egit.ui.internal.components.SelectionChangeListener;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.Ref;\n import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.transport.FetchConnection;\n-import org.spearce.jgit.transport.RemoteConfig;\n import org.spearce.jgit.transport.Transport;\n-import org.spearce.jgit.transport.URIish;\n-\n-class SourceBranchPage extends WizardPage {\n-\tprivate final List<BranchChangeListener> branchChangeListeners;\n \n+class SourceBranchPage extends BaseWizardPage {\n \tprivate final RepositorySelectionPage sourcePage;\n \n-\tprivate URIish validated;\n+\tprivate RepositorySelection validatedRepoSelection;\n \n \tprivate Ref head;\n \n-\tprivate List<Ref> available = Collections.<Ref> emptyList();\n+\tprivate List<Ref> availableRefs = new ArrayList<Ref>();\n \n-\tprivate Label label;\n+\tprivate List<Ref> selectedRefs = new ArrayList<Ref>();\n \n-\tprivate Table availTable;\n+\tprivate Label label;\n \n-\tprivate boolean allSelected;\n+\tprivate Table refsTable;\n \n \tprivate String transportError;\n \n@@ -73,33 +69,19 @@ class SourceBranchPage extends WizardPage {\n \t\tsuper(SourceBranchPage.class.getName());\n \t\tsourcePage = sp;\n \t\tsetTitle(UIText.SourceBranchPage_title);\n-\t\tsourcePage\n-\t\t\t\t.addRepositorySelectionListener(new RepositorySelectionListener() {\n-\t\t\t\t\tpublic void selectionChanged(final URIish newURI,\n-\t\t\t\t\t\t\tfinal RemoteConfig newConfig) {\n-\t\t\t\t\t\tif (newURI == null || !newURI.equals(validated)) {\n-\t\t\t\t\t\t\tvalidated = null;\n-\t\t\t\t\t\t\tsetPageComplete(false);\n-\t\t\t\t\t\t}\n-\t\t\t\t\t}\n-\t\t\t\t});\n-\t\tbranchChangeListeners = new ArrayList<BranchChangeListener>(3);\n-\t}\n \n-\tvoid addBranchChangeListener(final BranchChangeListener l) {\n-\t\tbranchChangeListeners.add(l);\n+\t\tsourcePage.addSelectionListener(new SelectionChangeListener() {\n+\t\t\tpublic void selectionChanged() {\n+\t\t\t\tif (!sourcePage.selectionEquals(validatedRepoSelection))\n+\t\t\t\t\tsetPageComplete(false);\n+\t\t\t\telse\n+\t\t\t\t\tcheckPage();\n+\t\t\t}\n+\t\t});\n \t}\n \n-\tCollection<Ref> getSelectedBranches() {\n-\t\tallSelected = true;\n-\t\tfinal ArrayList<Ref> r = new ArrayList<Ref>(available.size());\n-\t\tfor (int i = 0; i < available.size(); i++) {\n-\t\t\tif (availTable.getItem(i).getChecked())\n-\t\t\t\tr.add(available.get(i));\n-\t\t\telse\n-\t\t\t\tallSelected = false;\n-\t\t}\n-\t\treturn r;\n+\tList<Ref> getSelectedBranches() {\n+\t\treturn new ArrayList<Ref>(selectedRefs);\n \t}\n \n \tRef getHEAD() {\n@@ -107,7 +89,11 @@ class SourceBranchPage extends WizardPage {\n \t}\n \n \tboolean isAllSelected() {\n-\t\treturn allSelected;\n+\t\treturn availableRefs.size() == selectedRefs.size();\n+\t}\n+\n+\tboolean selectionEquals(final List<Ref> selectedRefs, final Ref head) {\n+\t\treturn this.selectedRefs.equals(selectedRefs) && this.head == head;\n \t}\n \n \tpublic void createControl(final Composite parent) {\n@@ -119,14 +105,30 @@ class SourceBranchPage extends WizardPage {\n \t\tlabel = new Label(panel, SWT.NONE);\n \t\tlabel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));\n \n-\t\tavailTable = new Table(panel, SWT.CHECK | SWT.V_SCROLL | SWT.BORDER);\n-\t\tavailTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));\n-\t\tavailTable.addListener(SWT.Selection, new Listener() {\n-\t\t\tpublic void handleEvent(final Event event) {\n-\t\t\t\tif (event.detail == SWT.CHECK) {\n-\t\t\t\t\tnotifyChanged();\n-\t\t\t\t\tsetPageComplete(isPageComplete());\n-\t\t\t\t}\n+\t\trefsTable = new Table(panel, SWT.CHECK | SWT.V_SCROLL | SWT.BORDER);\n+\t\trefsTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));\n+\t\trefsTable.addSelectionListener(new SelectionAdapter() {\n+\t\t\t@Override\n+\t\t\tpublic void widgetSelected(final SelectionEvent e) {\n+\t\t\t\tif (e.detail != SWT.CHECK)\n+\t\t\t\t\treturn;\n+\n+\t\t\t\tfinal TableItem tableItem = (TableItem) e.item;\n+\t\t\t\tfinal int i = refsTable.indexOf(tableItem);\n+\t\t\t\tfinal Ref ref = availableRefs.get(i);\n+\n+\t\t\t\tif (tableItem.getChecked()) {\n+\t\t\t\t\tint insertionPos = 0;\n+\t\t\t\t\tfor (int j = 0; j < i; j++) {\n+\t\t\t\t\t\tif (selectedRefs.contains(availableRefs.get(j)))\n+\t\t\t\t\t\t\tinsertionPos++;\n+\t\t\t\t\t}\n+\t\t\t\t\tselectedRefs.add(insertionPos, ref);\n+\t\t\t\t} else\n+\t\t\t\t\tselectedRefs.remove(ref);\n+\n+\t\t\t\tnotifySelectionChanged();\n+\t\t\t\tcheckPage();\n \t\t\t}\n \t\t});\n \n@@ -135,36 +137,31 @@ class SourceBranchPage extends WizardPage {\n \t\tButton b;\n \t\tb = new Button(bPanel, SWT.PUSH);\n \t\tb.setText(UIText.SourceBranchPage_selectAll);\n-\t\tb.addSelectionListener(new SelectionListener() {\n-\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n-\t\t\t\t// Do nothing.\n-\t\t\t}\n-\n-\t\t\tpublic void widgetSelected(SelectionEvent e) {\n-\t\t\t\tfor (int i = 0; i < availTable.getItemCount(); i++)\n-\t\t\t\t\tavailTable.getItem(i).setChecked(true);\n-\t\t\t\tnotifyChanged();\n-\t\t\t\tsetPageComplete(isPageComplete());\n+\t\tb.addSelectionListener(new SelectionAdapter() {\n+\t\t\tpublic void widgetSelected(final SelectionEvent e) {\n+\t\t\t\tfor (int i = 0; i < refsTable.getItemCount(); i++)\n+\t\t\t\t\trefsTable.getItem(i).setChecked(true);\n+\t\t\t\tselectedRefs.clear();\n+\t\t\t\tselectedRefs.addAll(availableRefs);\n+\t\t\t\tnotifySelectionChanged();\n+\t\t\t\tcheckPage();\n \t\t\t}\n \t\t});\n \t\tb = new Button(bPanel, SWT.PUSH);\n \t\tb.setText(UIText.SourceBranchPage_selectNone);\n-\t\tb.addSelectionListener(new SelectionListener() {\n-\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n-\t\t\t\t// Do nothing.\n-\t\t\t}\n-\n-\t\t\tpublic void widgetSelected(SelectionEvent e) {\n-\t\t\t\tfor (int i = 0; i < availTable.getItemCount(); i++)\n-\t\t\t\t\tavailTable.getItem(i).setChecked(false);\n-\t\t\t\tnotifyChanged();\n-\t\t\t\tsetPageComplete(isPageComplete());\n+\t\tb.addSelectionListener(new SelectionAdapter() {\n+\t\t\tpublic void widgetSelected(final SelectionEvent e) {\n+\t\t\t\tfor (int i = 0; i < refsTable.getItemCount(); i++)\n+\t\t\t\t\trefsTable.getItem(i).setChecked(false);\n+\t\t\t\tselectedRefs.clear();\n+\t\t\t\tnotifySelectionChanged();\n+\t\t\t\tcheckPage();\n \t\t\t}\n \t\t});\n \t\tbPanel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));\n \n \t\tsetControl(panel);\n-\t\tsetPageComplete(false);\n+\t\tcheckPage();\n \t}\n \n \t@Override\n@@ -174,52 +171,57 @@ class SourceBranchPage extends WizardPage {\n \t\tsuper.setVisible(visible);\n \t}\n \n-\t@Override\n-\tpublic boolean isPageComplete() {\n+\t/**\n+\t * Check internal state for page completion status. This method should be\n+\t * called only when all necessary data from previous form is available.\n+\t */\n+\tprivate void checkPage() {\n \t\tif (transportError != null) {\n \t\t\tsetErrorMessage(transportError);\n-\t\t\treturn false;\n+\t\t\tsetPageComplete(false);\n+\t\t\treturn;\n \t\t}\n \n \t\tif (getSelectedBranches().isEmpty()) {\n \t\t\tsetErrorMessage(UIText.SourceBranchPage_errorBranchRequired);\n-\t\t\treturn false;\n+\t\t\tsetPageComplete(false);\n+\t\t\treturn;\n \t\t}\n \n \t\tsetErrorMessage(null);\n-\t\treturn true;\n+\t\tsetPageComplete(true);\n \t}\n \n \tprivate void revalidate() {\n-\t\tfinal URIish newURI;\n-\t\tnewURI = sourcePage.getURI();\n-\n-\t\tlabel.setText(NLS.bind(UIText.SourceBranchPage_branchList, newURI\n-\t\t\t\t.toString()));\n-\t\tlabel.getParent().layout();\n-\n-\t\tif (newURI.equals(validated)) {\n-\t\t\tsetPageComplete(isPageComplete());\n+\t\tif (sourcePage.selectionEquals(validatedRepoSelection)) {\n+\t\t\t// URI hasn't changed, no need to refill the page with new data\n+\t\t\tcheckPage();\n \t\t\treturn;\n \t\t}\n \n-\t\tsetErrorMessage(null);\n-\t\tsetPageComplete(false);\n+\t\tfinal RepositorySelection newRepoSelection = sourcePage.getSelection();\n+\t\tlabel.setText(NLS.bind(UIText.SourceBranchPage_branchList,\n+\t\t\t\tnewRepoSelection.getURI().toString()));\n+\t\tlabel.getParent().layout();\n+\n+\t\tvalidatedRepoSelection = null;\n \t\ttransportError = null;\n \t\thead = null;\n-\t\tavailable = new ArrayList<Ref>();\n-\t\tavailTable.removeAll();\n-\t\tallSelected = false;\n+\t\tavailableRefs.clear();\n+\t\tselectedRefs.clear();\n+\t\trefsTable.removeAll();\n+\t\tcheckPage();\n \t\tlabel.getDisplay().asyncExec(new Runnable() {\n \t\t\tpublic void run() {\n-\t\t\t\trevalidateImpl(newURI);\n+\t\t\t\trevalidateImpl(newRepoSelection);\n \t\t\t}\n \t\t});\n \t}\n \n-\tprivate void revalidateImpl(final URIish newURI) {\n+\tprivate void revalidateImpl(final RepositorySelection newRepoSelection) {\n \t\tif (label.isDisposed() || !isCurrentPage())\n \t\t\treturn;\n+\n \t\ttry {\n \t\t\tgetContainer().run(true, true, new IRunnableWithProgress() {\n \t\t\t\tpublic void run(final IProgressMonitor pm)\n@@ -231,7 +233,8 @@ class SourceBranchPage extends WizardPage {\n \t\t\t\t\t\tmonitor = pm;\n \t\t\t\t\ttry {\n \t\t\t\t\t\tfinal Repository db = new Repository(new File(\"/tmp\"));\n-\t\t\t\t\t\tfinal Transport tn = Transport.open(db, newURI);\n+\t\t\t\t\t\tfinal Transport tn = Transport.open(db,\n+\t\t\t\t\t\t\t\tnewRepoSelection.getURI());\n \t\t\t\t\t\tfinal Collection<Ref> adv;\n \t\t\t\t\t\tfinal FetchConnection fn = tn.openFetch();\n \t\t\t\t\t\ttry {\n@@ -247,20 +250,20 @@ class SourceBranchPage extends WizardPage {\n \t\t\t\t\t\t\tfinal String n = r.getName();\n \t\t\t\t\t\t\tif (!n.startsWith(Constants.HEADS_PREFIX + \"/\"))\n \t\t\t\t\t\t\t\tcontinue;\n-\t\t\t\t\t\t\tavailable.add(r);\n+\t\t\t\t\t\t\tavailableRefs.add(r);\n \t\t\t\t\t\t\tif (idHEAD == null || head != null)\n \t\t\t\t\t\t\t\tcontinue;\n \t\t\t\t\t\t\tif (r.getObjectId().equals(idHEAD.getObjectId()))\n \t\t\t\t\t\t\t\thead = r;\n \t\t\t\t\t\t}\n-\t\t\t\t\t\tCollections.sort(available, new Comparator<Ref>() {\n+\t\t\t\t\t\tCollections.sort(availableRefs, new Comparator<Ref>() {\n \t\t\t\t\t\t\tpublic int compare(final Ref o1, final Ref o2) {\n \t\t\t\t\t\t\t\treturn o1.getName().compareTo(o2.getName());\n \t\t\t\t\t\t\t}\n \t\t\t\t\t\t});\n \t\t\t\t\t\tif (idHEAD != null && head == null) {\n \t\t\t\t\t\t\thead = idHEAD;\n-\t\t\t\t\t\t\tavailable.add(0, idHEAD);\n+\t\t\t\t\t\t\tavailableRefs.add(0, idHEAD);\n \t\t\t\t\t\t}\n \t\t\t\t\t} catch (Exception err) {\n \t\t\t\t\t\tthrow new InvocationTargetException(err);\n@@ -287,29 +290,22 @@ class SourceBranchPage extends WizardPage {\n \t\t\treturn;\n \t\t}\n \n-\t\tvalidated = newURI;\n-\t\tallSelected = true;\n-\t\tfor (final Ref r : available) {\n+\t\tvalidatedRepoSelection = newRepoSelection;\n+\t\tfor (final Ref r : availableRefs) {\n \t\t\tString n = r.getName();\n \t\t\tif (n.startsWith(Constants.HEADS_PREFIX + \"/\"))\n \t\t\t\tn = n.substring((Constants.HEADS_PREFIX + \"/\").length());\n-\t\t\tfinal TableItem ti = new TableItem(availTable, SWT.NONE);\n+\t\t\tfinal TableItem ti = new TableItem(refsTable, SWT.NONE);\n \t\t\tti.setText(n);\n \t\t\tti.setChecked(true);\n+\t\t\tselectedRefs.add(r);\n \t\t}\n-\t\tnotifyChanged();\n-\t\tsetErrorMessage(null);\n-\t\tsetPageComplete(isPageComplete());\n+\t\tnotifySelectionChanged();\n+\t\tcheckPage();\n \t}\n \n \tprivate void transportError(final String msg) {\n \t\ttransportError = msg;\n-\t\tsetErrorMessage(msg);\n-\t\tsetPageComplete(false);\n-\t}\n-\n-\tprivate void notifyChanged() {\n-\t\tfor (final BranchChangeListener l : branchChangeListeners)\n-\t\t\tl.branchesChanged();\n+\t\tcheckPage();\n \t}\n }\n\\ No newline at end of file\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/BaseWizardPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/BaseWizardPage.java\nnew file mode 100644\nindex 0000000..bb5d1d4\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/BaseWizardPage.java\n@@ -0,0 +1,58 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.components;\n+\n+import java.util.LinkedList;\n+import java.util.List;\n+\n+import org.eclipse.jface.wizard.WizardPage;\n+\n+/**\n+ * Base wizard page class for pages that need support for inter-pages\n+ * dependencies.\n+ * <p>\n+ * This abstract class maintains list of selection change listeners and provides\n+ * method to notify them about selection change.\n+ * \n+ * @see SelectionChangeListener\n+ */\n+public abstract class BaseWizardPage extends WizardPage {\n+\tprivate final List<SelectionChangeListener> selectionListeners;\n+\n+\t/**\n+\t * Create base wizard with specified name. Listeners list is empty.\n+\t * \n+\t * @see WizardPage#WizardPage(String)\n+\t * @param pageName\n+\t *            page name.\n+\t */\n+\tpublic BaseWizardPage(final String pageName) {\n+\t\tsuper(pageName);\n+\t\tselectionListeners = new LinkedList<SelectionChangeListener>();\n+\t}\n+\n+\t/**\n+\t * Add {@link SelectionChangeListener} to list of listeners notified on\n+\t * selection change on this page.\n+\t * \n+\t * @param l\n+\t *            listener that will be notified about changes.\n+\t */\n+\tpublic void addSelectionListener(final SelectionChangeListener l) {\n+\t\tselectionListeners.add(l);\n+\t}\n+\n+\t/**\n+\t * Notifies registered listeners about selection change.\n+\t */\n+\tprotected void notifySelectionChanged() {\n+\t\tfor (final SelectionChangeListener l : selectionListeners)\n+\t\t\tl.selectionChanged();\n+\t}\n+\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelection.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelection.java\nnew file mode 100644\nindex 0000000..d9aa6d5\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelection.java\n@@ -0,0 +1,133 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+\n+package org.spearce.egit.ui.internal.components;\n+\n+import java.util.Collections;\n+import java.util.List;\n+\n+import org.spearce.jgit.transport.RemoteConfig;\n+import org.spearce.jgit.transport.URIish;\n+\n+/**\n+ * Data class representing selection of remote repository made by user.\n+ * Selection is an URI or remote repository configuration.\n+ * <p>\n+ * Each immutable instance has at least one of two class fields (URI, remote\n+ * config) set to null. null value indicates that it has illegal value or this\n+ * form of repository selection is not selected.\n+ * <p>\n+ * If remote configuration is selected, it always has non-empty URIs list.\n+ */\n+public class RepositorySelection {\n+\tprivate URIish uri;\n+\n+\tprivate RemoteConfig config;\n+\n+\tstatic final RepositorySelection INVALID_SELECTION = new RepositorySelection(\n+\t\t\tnull, null);\n+\n+\t/**\n+\t * @param uri\n+\t *            the new specified URI. null if the new URI is invalid or user\n+\t *            chosen to specify repository as remote config instead of URI.\n+\t * @param config\n+\t *            the new remote config. null if user chosen to specify\n+\t *            repository as URI.\n+\t */\n+\tRepositorySelection(final URIish uri, final RemoteConfig config) {\n+\t\tif (config != null && uri != null)\n+\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\"URI and config cannot be set at the same time.\");\n+\t\tthis.config = config;\n+\t\tthis.uri = uri;\n+\t}\n+\n+\t/**\n+\t * @return the selected URI (if specified by user as valid custom URI) or\n+\t *         first URI from selected configuration (if specified by user as\n+\t *         May be null if there is no valid selection.\n+\t */\n+\tpublic URIish getURI() {\n+\t\tif (isConfigSelected())\n+\t\t\treturn config.getURIs().get(0);\n+\t\treturn uri;\n+\t}\n+\n+\t/**\n+\t * @return list of all selected URIs - either the one specified as custom\n+\t *         URI or all URIs from selected configuration. May be null in case\n+\t *         of no valid selection.\n+\t */\n+\tpublic List<URIish> getAllURIs() {\n+\t\tif (isURISelected())\n+\t\t\treturn Collections.singletonList(uri);\n+\t\tif (isConfigSelected())\n+\t\t\treturn config.getURIs();\n+\t\treturn null;\n+\t}\n+\n+\t/**\n+\t * @return the selected remote configuration. null if user chosen to select\n+\t *         repository as URI.\n+\t */\n+\tpublic RemoteConfig getConfig() {\n+\t\treturn config;\n+\t}\n+\n+\t/**\n+\t * @return selected remote configuration name or null if selection is not a\n+\t *         remote configuration.\n+\t */\n+\tpublic String getConfigName() {\n+\t\tif (isConfigSelected())\n+\t\t\treturn config.getName();\n+\t\treturn null;\n+\t}\n+\n+\t/**\n+\t * @return true if selection contains valid URI or remote config, false if\n+\t *         there is no valid selection.\n+\t */\n+\tpublic boolean isValidSelection() {\n+\t\treturn uri != null || config != null;\n+\t}\n+\n+\t/**\n+\t * @return true if user selected valid URI, false if user selected invalid\n+\t *         URI or remote config.\n+\t */\n+\tpublic boolean isURISelected() {\n+\t\treturn uri != null;\n+\t}\n+\n+\t/**\n+\t * @return true if user selected remote configuration, false if user\n+\t *         selected (invalid or valid) URI.\n+\t */\n+\tpublic boolean isConfigSelected() {\n+\t\treturn config != null;\n+\t}\n+\n+\t@Override\n+\tpublic boolean equals(final Object obj) {\n+\t\tif (obj instanceof RepositorySelection) {\n+\t\t\tfinal RepositorySelection other = (RepositorySelection) obj;\n+\t\t\tif (uri == null ^ other.uri == null)\n+\t\t\t\treturn false;\n+\t\t\tif (uri != null && !uri.equals(other.uri))\n+\t\t\t\treturn false;\n+\n+\t\t\tif (config != other.config)\n+\t\t\t\treturn false;\n+\n+\t\t\treturn true;\n+\t\t} else\n+\t\t\treturn false;\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionListener.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionListener.java\ndeleted file mode 100644\nindex ef5c33b..0000000\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionListener.java\n+++ /dev/null\n@@ -1,32 +0,0 @@\n-/*******************************************************************************\n- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n- *\n- * All rights reserved. This program and the accompanying materials\n- * are made available under the terms of the Eclipse Public License v1.0\n- * See LICENSE for the full license text, also available.\n- *******************************************************************************/\n-package org.spearce.egit.ui.internal.components;\n-\n-import org.spearce.jgit.transport.RemoteConfig;\n-import org.spearce.jgit.transport.URIish;\n-\n-/**\n- * Interface for listeners of repository selection events from repository\n- * selection dialogs.\n- */\n-public interface RepositorySelectionListener {\n-\t/**\n-\t * Notify the receiver that the repository selection has changed. Each time\n-\t * at least one argument of this call is null, which indicates that it has\n-\t * illegal value or this form of repository selection is not selected.\n-\t * \n-\t * @param newURI\n-\t *            the new specified URI. null if the new URI is invalid or user\n-\t *            chosen to specify repository as remote config instead of URI.\n-\t * @param newConfig\n-\t *            the new remote config. null if user chosen to specify\n-\t *            repository as URI.\n-\t */\n-\tpublic void selectionChanged(URIish newURI, RemoteConfig newConfig);\n-}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionPage.java\nindex a487cb1..abb7ddb 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RepositorySelectionPage.java\n@@ -13,11 +13,9 @@ package org.spearce.egit.ui.internal.components;\n import java.io.File;\n import java.net.URISyntaxException;\n import java.util.Iterator;\n-import java.util.LinkedList;\n import java.util.List;\n import java.util.regex.Pattern;\n \n-import org.eclipse.jface.wizard.WizardPage;\n import org.eclipse.osgi.util.NLS;\n import org.eclipse.swt.SWT;\n import org.eclipse.swt.events.ModifyEvent;\n@@ -45,7 +43,7 @@ import org.spearce.jgit.util.FS;\n  * Wizard page that allows the user entering the location of a remote repository\n  * by specifying URL manually or selecting a preconfigured remote repository.\n  */\n-public class RepositorySelectionPage extends WizardPage {\n+public class RepositorySelectionPage extends BaseWizardPage {\n \tprivate static final int REMOTE_CONFIG_TEXT_MAX_LENGTH = 80;\n \n \tprivate static final String DEFAULT_REMOTE_NAME = \"origin\";\n@@ -75,7 +73,7 @@ public class RepositorySelectionPage extends WizardPage {\n \t\tDEFAULT_SCHEMES[S_FTP] = \"ftp\";\n \t\tDEFAULT_SCHEMES[S_FILE] = \"file\";\n \t}\n-\t\n+\n \tprivate static void setEnabledRecursively(final Control control,\n \t\t\tfinal boolean enable) {\n \t\tcontrol.setEnabled(enable);\n@@ -84,8 +82,6 @@ public class RepositorySelectionPage extends WizardPage {\n \t\t\t\tsetEnabledRecursively(child, enable);\n \t}\n \n-\tprivate final List<RepositorySelectionListener> selectionListeners;\n-\n \tprivate final List<RemoteConfig> configuredRemotes;\n \n \tprivate Group authGroup;\n@@ -110,9 +106,7 @@ public class RepositorySelectionPage extends WizardPage {\n \n \tprivate RemoteConfig remoteConfig;\n \n-\tprivate RemoteConfig exposedRemoteConfig;\n-\n-\tprivate URIish exposedURI;\n+\tprivate RepositorySelection selection;\n \n \tprivate Composite remotePanel;\n \n@@ -155,6 +149,7 @@ public class RepositorySelectionPage extends WizardPage {\n \t\t\tthis.configuredRemotes = configuredRemotes;\n \t\t\tthis.remoteConfig = selectDefaultRemoteConfig();\n \t\t}\n+\t\tselection = RepositorySelection.INVALID_SELECTION;\n \n \t\tif (sourceSelection) {\n \t\t\tsetTitle(UIText.RepositorySelectionPage_sourceSelectionTitle);\n@@ -163,7 +158,6 @@ public class RepositorySelectionPage extends WizardPage {\n \t\t\tsetTitle(UIText.RepositorySelectionPage_destinationSelectionTitle);\n \t\t\tsetDescription(UIText.RepositorySelectionPage_destinationSelectionDescription);\n \t\t}\n-\t\tselectionListeners = new LinkedList<RepositorySelectionListener>();\n \t}\n \n \t/**\n@@ -180,15 +174,22 @@ public class RepositorySelectionPage extends WizardPage {\n \t}\n \n \t/**\n-\t * Add {@link RepositorySelectionListener} to list of listeners notified on\n-\t * repository selection change.\n+\t * @return repository selection representing current page state.\n+\t */\n+\tpublic RepositorySelection getSelection() {\n+\t\treturn selection;\n+\t}\n+\n+\t/**\n+\t * Compare current repository selection set by user to provided one.\n \t * \n-\t * @param l\n-\t *            listener that will be notified about changes\n+\t * @param s\n+\t *            repository selection to compare.\n+\t * @return true if provided selection is equal to current page selection,\n+\t *         false otherwise.\n \t */\n-\tpublic void addRepositorySelectionListener(\n-\t\t\tfinal RepositorySelectionListener l) {\n-\t\tselectionListeners.add(l);\n+\tpublic boolean selectionEquals(final RepositorySelection s) {\n+\t\treturn selection.equals(s);\n \t}\n \n \tpublic void createControl(final Composite parent) {\n@@ -423,23 +424,6 @@ public class RepositorySelectionPage extends WizardPage {\n \t\treturn new GridData(SWT.FILL, SWT.DEFAULT, true, false);\n \t}\n \n-\t/**\n-\t * @return the URI entered in the Wizard page. null if URI is invalid or\n-\t *         user chosen to select remote config instead of providing direct\n-\t *         URI.\n-\t */\n-\tpublic URIish getURI() {\n-\t\treturn exposedURI;\n-\t}\n-\n-\t/**\n-\t * @return the selected remote configuration in the Wizard page. null if\n-\t *         user chosen to select repository as URI.\n-\t */\n-\tpublic RemoteConfig getRemoteConfig() {\n-\t\treturn exposedRemoteConfig;\n-\t}\n-\n \tprivate static boolean isGIT(final URIish uri) {\n \t\treturn \"git\".equals(uri.getScheme());\n \t}\n@@ -637,13 +621,12 @@ public class RepositorySelectionPage extends WizardPage {\n \t}\n \n \tprivate void setExposedSelection(final URIish u, final RemoteConfig rc) {\n-\t\tif (u == exposedURI && rc == exposedRemoteConfig) // nothing changed\n+\t\tfinal RepositorySelection newSelection = new RepositorySelection(u, rc);\n+\t\tif (newSelection.equals(selection))\n \t\t\treturn;\n-\t\tthis.exposedURI = u;\n-\t\tthis.exposedRemoteConfig = rc;\n \n-\t\tfor (final RepositorySelectionListener l : selectionListeners)\n-\t\t\tl.selectionChanged(u, rc);\n+\t\tselection = newSelection;\n+\t\tnotifySelectionChanged();\n \t}\n \n \tprivate void updateRemoteAndURIPanels() {\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/BranchChangeListener.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/SelectionChangeListener.java\nsimilarity index 50%\nrename from org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/BranchChangeListener.java\nrename to org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/SelectionChangeListener.java\nindex 3ef8e16..bde1d21 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/BranchChangeListener.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/SelectionChangeListener.java\n@@ -1,13 +1,19 @@\n /*******************************************************************************\n- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n  *\n  * All rights reserved. This program and the accompanying materials\n  * are made available under the terms of the Eclipse Public License v1.0\n  * See LICENSE for the full license text, also available.\n  *******************************************************************************/\n-package org.spearce.egit.ui.internal.clone;\n+package org.spearce.egit.ui.internal.components;\n \n-interface BranchChangeListener {\n-\t/** Notify the receiver that the branches have changed. */\n-\tvoid branchesChanged();\n+/**\n+ * General interface for receivers of selection-changed notifications from\n+ * various components.\n+ */\n+public interface SelectionChangeListener {\n+\t/**\n+\t * Called when selection in calling component has changed.\n+\t */\n+\tpublic void selectionChanged();\n }\n-- \n1.5.6.3\n"},{"id":"87480","messageId":"1219005852-21496-21-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-20-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 20/31] Move clone logic away from GitCloneWizard to CloneOperation","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:01Z","receivedAt":"2008-08-17T20:44:01Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Clone logic was unnaturally splitted between GitCloneWizard and\nCloneOperation classes. Now, repository initialization part is moved\nto CloneOperation, so there is cleaner separation of responsibilties.\n\nFailure handling is also improved, incompletely cloned repository\ndirectory is removed before reporting problem to user.\n\nDirectory creation is problematic issue because of potential errors,\nso it's keeped in GitCloneWizard.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../org/spearce/egit/core/op/CloneOperation.java   |  129 +++++++++++++++++---\n .../src/org/spearce/egit/ui/UIText.java            |    4 +\n .../egit/ui/internal/clone/GitCloneWizard.java     |   78 +++----------\n .../src/org/spearce/egit/ui/uitext.properties      |    1 +\n 4 files changed, 130 insertions(+), 82 deletions(-)\n\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/op/CloneOperation.java b/org.spearce.egit.core/src/org/spearce/egit/core/op/CloneOperation.java\nindex 656f3cb..531045b 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/op/CloneOperation.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/CloneOperation.java\n@@ -3,6 +3,7 @@\n  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>\n  * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>\n  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n  *\n  * All rights reserved. This program and the accompanying materials\n  * are made available under the terms of the Eclipse Public License v1.0\n@@ -10,8 +11,11 @@\n  *******************************************************************************/\n package org.spearce.egit.core.op;\n \n+import java.io.File;\n import java.io.IOException;\n import java.lang.reflect.InvocationTargetException;\n+import java.net.URISyntaxException;\n+import java.util.Collection;\n \n import org.eclipse.core.runtime.IProgressMonitor;\n import org.eclipse.core.runtime.NullProgressMonitor;\n@@ -31,39 +35,71 @@ import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.lib.Tree;\n import org.spearce.jgit.lib.WorkDirCheckout;\n import org.spearce.jgit.transport.FetchResult;\n+import org.spearce.jgit.transport.RefSpec;\n import org.spearce.jgit.transport.RemoteConfig;\n import org.spearce.jgit.transport.Transport;\n+import org.spearce.jgit.transport.URIish;\n \n /**\n  * Clones a repository from a remote location to a local location.\n  */\n public class CloneOperation implements IRunnableWithProgress {\n-\tprivate final Repository local;\n+\tprivate static final String HEADS_PREFIX = Constants.HEADS_PREFIX;\n \n-\tprivate final RemoteConfig remote;\n+\tprivate static final String REMOTES_PREFIX_S = Constants.REMOTES_PREFIX\n+\t\t\t+ \"/\";\n+\n+\tprivate final URIish uri;\n+\n+\tprivate final boolean allSelected;\n+\n+\tprivate final Collection<Ref> selectedBranches;\n+\n+\tprivate final File workdir;\n \n \tprivate final String branch;\n \n+\tprivate final String remoteName;\n+\n+\tprivate Repository local;\n+\n+\tprivate RemoteConfig remoteConfig;\n+\n \tprivate FetchResult fetchResult;\n \n \t/**\n \t * Create a new clone operation.\n \t * \n-\t * @param r\n-\t *            repository the checkout will happen within.\n-\t * @param t\n+\t * @param uri\n \t *            remote we should fetch from.\n-\t * @param b\n+\t * @param allSelected\n+\t *            true when all branches have to be fetched (indicates wildcard\n+\t *            in created fetch refspec), false otherwise.\n+\t * @param selectedBranches\n+\t *            collection of branches to fetch. Ignored when allSelected is\n+\t *            true.\n+\t * @param workdir\n+\t *            working directory to clone to. The directory may or may not\n+\t *            already exist.\n+\t * @param branch\n \t *            branch to initially clone from.\n+\t * @param remoteName\n+\t *            name of created remote config as source remote (typically\n+\t *            named \"origin\").\n \t */\n-\tpublic CloneOperation(final Repository r, final RemoteConfig t,\n-\t\t\tfinal String b) {\n-\t\tlocal = r;\n-\t\tremote = t;\n-\t\tbranch = b;\n+\tpublic CloneOperation(final URIish uri, final boolean allSelected,\n+\t\t\tfinal Collection<Ref> selectedBranches, final File workdir,\n+\t\t\tfinal String branch, final String remoteName) {\n+\t\tthis.uri = uri;\n+\t\tthis.allSelected = allSelected;\n+\t\tthis.selectedBranches = selectedBranches;\n+\t\tthis.workdir = workdir;\n+\t\tthis.branch = branch;\n+\t\tthis.remoteName = remoteName;\n \t}\n \n-\tpublic void run(final IProgressMonitor pm) throws InvocationTargetException {\n+\tpublic void run(final IProgressMonitor pm)\n+\t\t\tthrows InvocationTargetException, InterruptedException {\n \t\tfinal IProgressMonitor monitor;\n \t\tif (pm == null)\n \t\t\tmonitor = new NullProgressMonitor();\n@@ -71,21 +107,65 @@ public class CloneOperation implements IRunnableWithProgress {\n \t\t\tmonitor = pm;\n \n \t\ttry {\n-\t\t\tmonitor.beginTask(NLS.bind(CoreText.CloneOperation_title, remote\n-\t\t\t\t\t.getURIs().get(0).toString()), 5000);\n-\t\t\tdoFetch(new SubProgressMonitor(monitor, 4000));\n-\t\t\tdoCheckout(new SubProgressMonitor(monitor, 1000));\n-\t\t} catch (IOException e) {\n-\t\t\tif (!monitor.isCanceled())\n+\t\t\tmonitor.beginTask(NLS.bind(CoreText.CloneOperation_title, uri),\n+\t\t\t\t\t5000);\n+\t\t\ttry {\n+\t\t\t\tdoInit(new SubProgressMonitor(monitor, 100));\n+\t\t\t\tdoFetch(new SubProgressMonitor(monitor, 4000));\n+\t\t\t\tdoCheckout(new SubProgressMonitor(monitor, 900));\n+\t\t\t} finally {\n+\t\t\t\tcloseLocal();\n+\t\t\t}\n+\t\t} catch (final Exception e) {\n+\t\t\tdelete(workdir);\n+\t\t\tif (monitor.isCanceled())\n+\t\t\t\tthrow new InterruptedException();\n+\t\t\telse\n \t\t\t\tthrow new InvocationTargetException(e);\n \t\t} finally {\n \t\t\tmonitor.done();\n \t\t}\n \t}\n \n+\tprivate void closeLocal() {\n+\t\tif (local != null) {\n+\t\t\tlocal.close();\n+\t\t\tlocal = null;\n+\t\t}\n+\t}\n+\n+\tprivate void doInit(final IProgressMonitor monitor)\n+\t\t\tthrows URISyntaxException, IOException {\n+\t\tmonitor.setTaskName(\"Initializing local repository\");\n+\n+\t\tfinal File gitdir = new File(workdir, \".git\");\n+\t\tlocal = new Repository(gitdir);\n+\t\tlocal.create();\n+\t\tlocal.writeSymref(Constants.HEAD, branch);\n+\n+\t\tremoteConfig = new RemoteConfig(local.getConfig(), remoteName);\n+\t\tremoteConfig.addURI(uri);\n+\n+\t\tfinal String dst = REMOTES_PREFIX_S + remoteConfig.getName();\n+\t\tRefSpec wcrs = new RefSpec();\n+\t\twcrs = wcrs.setForceUpdate(true);\n+\t\twcrs = wcrs.setSourceDestination(HEADS_PREFIX + \"/*\", dst + \"/*\");\n+\n+\t\tif (allSelected) {\n+\t\t\tremoteConfig.addFetchRefSpec(wcrs);\n+\t\t} else {\n+\t\t\tfor (final Ref ref : selectedBranches)\n+\t\t\t\tif (wcrs.matchSource(ref))\n+\t\t\t\t\tremoteConfig.addFetchRefSpec(wcrs.expandFromSource(ref));\n+\t\t}\n+\n+\t\tremoteConfig.update(local.getConfig());\n+\t\tlocal.getConfig().save();\n+\t}\n+\n \tprivate void doFetch(final IProgressMonitor monitor)\n \t\t\tthrows NotSupportedException, TransportException {\n-\t\tfinal Transport tn = Transport.open(local, remote);\n+\t\tfinal Transport tn = Transport.open(local, remoteConfig);\n \t\ttry {\n \t\t\tfinal EclipseGitProgressTransformer pm;\n \t\t\tpm = new EclipseGitProgressTransformer(monitor);\n@@ -116,4 +196,15 @@ public class CloneOperation implements IRunnableWithProgress {\n \t\tmonitor.setTaskName(\"Writing index\");\n \t\tindex.write();\n \t}\n+\n+\tprivate static void delete(final File d) {\n+\t\tif (d.isDirectory()) {\n+\t\t\tfinal File[] items = d.listFiles();\n+\t\t\tif (items != null) {\n+\t\t\t\tfor (final File c : items)\n+\t\t\t\t\tdelete(c);\n+\t\t\t}\n+\t\t}\n+\t\td.delete();\n+\t}\n }\n\\ No newline at end of file\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex 0d39440..9150832 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -53,6 +53,9 @@ public class UIText extends NLS {\n \tpublic static String GitCloneWizard_failed;\n \n \t/** */\n+\tpublic static String GitCloneWizard_errorCannotCreate;\n+\n+\t/** */\n \tpublic static String RepositorySelectionPage_sourceSelectionTitle;\n \n \t/** */\n@@ -180,6 +183,7 @@ public class UIText extends NLS {\n \n \t/** */\n \tpublic static String ResourceHistory_toggleCommentFill;\n+\n \t/** */\n \tpublic static String ResourceHistory_toggleRevDetail;\n \ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\nindex 9c1d691..efcf57f 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\n@@ -10,9 +10,8 @@\n package org.spearce.egit.ui.internal.clone;\n \n import java.io.File;\n-import java.io.IOException;\n import java.lang.reflect.InvocationTargetException;\n-import java.net.URISyntaxException;\n+import java.util.Collection;\n \n import org.eclipse.core.runtime.IProgressMonitor;\n import org.eclipse.core.runtime.IStatus;\n@@ -29,22 +28,13 @@ import org.spearce.egit.ui.Activator;\n import org.spearce.egit.ui.UIIcons;\n import org.spearce.egit.ui.UIText;\n import org.spearce.egit.ui.internal.components.RepositorySelectionPage;\n-import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.Ref;\n-import org.spearce.jgit.lib.Repository;\n-import org.spearce.jgit.transport.RefSpec;\n-import org.spearce.jgit.transport.RemoteConfig;\n import org.spearce.jgit.transport.URIish;\n \n /**\n  * Import Git Repository Wizard. A front end to a git clone operation.\n  */\n public class GitCloneWizard extends Wizard implements IImportWizard {\n-\tprivate static final String HEADS_PREFIX = Constants.HEADS_PREFIX;\n-\n-\tprivate static final String REMOTES_PREFIX_S = Constants.REMOTES_PREFIX\n-\t\t\t+ \"/\";\n-\n \tprivate RepositorySelectionPage cloneSource;\n \n \tprivate SourceBranchPage validSource;\n@@ -68,62 +58,35 @@ public class GitCloneWizard extends Wizard implements IImportWizard {\n \n \t@Override\n \tpublic boolean performFinish() {\n-\t\tfinal URIish uri;\n-\t\tfinal Repository db;\n-\t\tfinal RemoteConfig origin;\n-\n-\t\turi = cloneSource.getSelection().getURI();\n-\n+\t\tfinal URIish uri = cloneSource.getSelection().getURI();\n+\t\tfinal boolean allSelected = validSource.isAllSelected();\n+\t\tfinal Collection<Ref> selectedBranches = validSource\n+\t\t\t\t.getSelectedBranches();\n \t\tfinal File workdir = cloneDestination.getDestinationFile();\n \t\tfinal String branch = cloneDestination.getInitialBranch();\n-\t\tfinal File gitdir = new File(workdir, \".git\");\n-\t\ttry {\n-\t\t\tdb = new Repository(gitdir);\n-\t\t\tdb.create();\n-\t\t\tdb.writeSymref(Constants.HEAD, branch);\n+\t\tfinal String remoteName = cloneDestination.getRemote();\n \n-\t\t\tfinal String rn = cloneDestination.getRemote();\n-\t\t\torigin = new RemoteConfig(db.getConfig(), rn);\n-\t\t\torigin.addURI(uri);\n-\n-\t\t\tfinal String dst = REMOTES_PREFIX_S + origin.getName();\n-\t\t\tRefSpec wcrs = new RefSpec();\n-\t\t\twcrs = wcrs.setForceUpdate(true);\n-\t\t\twcrs = wcrs.setSourceDestination(HEADS_PREFIX + \"/*\", dst + \"/*\");\n-\n-\t\t\tif (validSource.isAllSelected()) {\n-\t\t\t\torigin.addFetchRefSpec(wcrs);\n-\t\t\t} else {\n-\t\t\t\tfor (final Ref ref : validSource.getSelectedBranches())\n-\t\t\t\t\tif (wcrs.matchSource(ref))\n-\t\t\t\t\t\torigin.addFetchRefSpec(wcrs.expandFromSource(ref));\n-\t\t\t}\n-\n-\t\t\torigin.update(db.getConfig());\n-\t\t\tdb.getConfig().save();\n-\t\t} catch (IOException err) {\n-\t\t\tActivator.logError(UIText.GitCloneWizard_failed, err);\n+\t\tif (!workdir.mkdirs()) {\n+\t\t\tfinal String errorMessage = NLS.bind(\n+\t\t\t\t\tUIText.GitCloneWizard_errorCannotCreate, workdir.getPath());\n \t\t\tErrorDialog.openError(getShell(), getWindowTitle(),\n \t\t\t\t\tUIText.GitCloneWizard_failed, new Status(IStatus.ERROR,\n-\t\t\t\t\t\t\tActivator.getPluginId(), 0, err.getMessage(), err));\n-\t\t\treturn false;\n-\t\t} catch (URISyntaxException e) {\n+\t\t\t\t\t\t\tActivator.getPluginId(), 0, errorMessage, null));\n+\t\t\t// let's give user a chance to fix this minor problem\n \t\t\treturn false;\n \t\t}\n \n-\t\tfinal CloneOperation op = new CloneOperation(db, origin, branch);\n+\t\tfinal CloneOperation op = new CloneOperation(uri, allSelected,\n+\t\t\t\tselectedBranches, workdir, branch, remoteName);\n \t\tfinal Job job = new Job(NLS.bind(UIText.GitCloneWizard_jobName, uri\n \t\t\t\t.toString())) {\n \t\t\t@Override\n \t\t\tprotected IStatus run(final IProgressMonitor monitor) {\n \t\t\t\ttry {\n \t\t\t\t\top.run(monitor);\n-\t\t\t\t\tif (monitor.isCanceled()) {\n-\t\t\t\t\t\tdb.close();\n-\t\t\t\t\t\tdelete(workdir);\n-\t\t\t\t\t\treturn Status.CANCEL_STATUS;\n-\t\t\t\t\t}\n \t\t\t\t\treturn Status.OK_STATUS;\n+\t\t\t\t} catch (InterruptedException e) {\n+\t\t\t\t\treturn Status.CANCEL_STATUS;\n \t\t\t\t} catch (InvocationTargetException e) {\n \t\t\t\t\tThrowable thr = e.getCause();\n \t\t\t\t\treturn new Status(IStatus.ERROR, Activator.getPluginId(),\n@@ -135,15 +98,4 @@ public class GitCloneWizard extends Wizard implements IImportWizard {\n \t\tjob.schedule();\n \t\treturn true;\n \t}\n-\n-\tprivate static void delete(final File d) {\n-\t\tif (d.isDirectory()) {\n-\t\t\tfinal File[] items = d.listFiles();\n-\t\t\tif (items != null) {\n-\t\t\t\tfor (final File c : items)\n-\t\t\t\t\tdelete(c);\n-\t\t\t}\n-\t\t}\n-\t\td.delete();\n-\t}\n }\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex 857568a..420b610 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -30,6 +30,7 @@ ExistingOrNewPage_createInParent=Create repository in project's parent directory\n GitCloneWizard_title=Import Git Repository\n GitCloneWizard_jobName=Cloning from {0}\n GitCloneWizard_failed=Git repository clone failed.\n+GitCloneWizard_errorCannotCreate=Cannot create directory {0}.\n \n RepositorySelectionPage_sourceSelectionTitle=Source Git Repository\n RepositorySelectionPage_sourceSelectionDescription=Enter the location of the source repository.\n-- \n1.5.6.3\n"},{"id":"87486","messageId":"1219005852-21496-22-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-21-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 21/31] Add canCreateSubdir() heuristic in CloneDestinationPage","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:02Z","receivedAt":"2008-08-17T20:44:02Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"This method provide user with live-feedback when chosen subdirectory\ncan't be created for sure.\n\nThis is only a heuristic (may produce false-positive errors), as it is\nproblematic to handle File#canWrite() noisy return values for Windows'\nJava.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../ui/internal/clone/CloneDestinationPage.java    |   23 ++++++++++++++++++-\n 1 files changed, 21 insertions(+), 2 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\nindex 01bf54a..e1e9858 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\n@@ -220,9 +220,17 @@ class CloneDestinationPage extends WizardPage {\n \t\t\tsetPageComplete(false);\n \t\t\treturn;\n \t\t}\n-\t\tif (new File(dstpath).exists()) {\n+\t\tfinal File dstFile = new File(dstpath);\n+\t\tif (dstFile.exists()) {\n \t\t\tsetErrorMessage(NLS.bind(UIText.CloneDestinationPage_errorExists,\n-\t\t\t\t\tnew File(dstpath).getName()));\n+\t\t\t\t\tdstFile.getName()));\n+\t\t\tsetPageComplete(false);\n+\t\t\treturn;\n+\t\t}\n+\t\tfinal File absoluteFile = dstFile.getAbsoluteFile();\n+\t\tif (!canCreateSubdir(absoluteFile.getParentFile())) {\n+\t\t\tsetErrorMessage(NLS.bind(UIText.GitCloneWizard_errorCannotCreate,\n+\t\t\t\t\tdstFile.getPath()));\n \t\t\tsetPageComplete(false);\n \t\t\treturn;\n \t\t}\n@@ -243,6 +251,17 @@ class CloneDestinationPage extends WizardPage {\n \t\tsetPageComplete(true);\n \t}\n \n+\t// this is actually just an optimistic heuristic - should be named\n+\t// isThereHopeThatCanCreateSubdir() as probably there is no 100% reliable\n+\t// way to check that in Java for Windows\n+\tprivate static boolean canCreateSubdir(final File parent) {\n+\t\tif (parent == null)\n+\t\t\treturn true;\n+\t\tif (parent.exists())\n+\t\t\treturn parent.isDirectory() && parent.canWrite();\n+\t\treturn canCreateSubdir(parent.getParentFile());\n+\t}\n+\n \tprivate void revalidate() {\n \t\tif (sourcePage.selectionEquals(validatedRepoSelection)\n \t\t\t\t&& branchPage.selectionEquals(validatedSelectedBranches,\n-- \n1.5.6.3\n"},{"id":"87476","messageId":"1219005852-21496-23-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-22-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 22/31] Set FileDialog selection appropriately in clone wizard","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:03Z","receivedAt":"2008-08-17T20:44:03Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Previous selection scheme was probably not correct, or at least not\nportable. It hasn't worked on GTK+ SWT implementation.\n\nNew code still doesn't work on GTK+ SWT in Eclipse 3.3, but it works in\nEclipse 3.4. FileDialog for GTK+ in 3.3 was probably buggy. I've created\na bug at eclipse.org bugzilla for that (241824), but it won't be fixed\nfor 3.3.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../ui/internal/clone/CloneDestinationPage.java    |    7 ++++---\n 1 files changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\nindex e1e9858..a445cf5 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\n@@ -128,9 +128,10 @@ class CloneDestinationPage extends WizardPage {\n \n \t\t\t\td = new FileDialog(getShell(), SWT.APPLICATION_MODAL | SWT.SAVE);\n \t\t\t\tif (directoryText.getText().length() > 0) {\n-\t\t\t\t\tfinal File f = new File(directoryText.getText());\n-\t\t\t\t\td.setFilterPath(f.getAbsoluteFile().getAbsolutePath());\n-\t\t\t\t\td.setFileName(f.getName());\n+\t\t\t\t\tfinal File file = new File(directoryText.getText())\n+\t\t\t\t\t\t\t.getAbsoluteFile();\n+\t\t\t\t\td.setFilterPath(file.getParent());\n+\t\t\t\t\td.setFileName(file.getName());\n \t\t\t\t}\n \t\t\t\tfinal String r = d.open();\n \t\t\t\tif (r != null)\n-- \n1.5.6.3\n"},{"id":"87478","messageId":"1219005852-21496-24-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-23-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 23/31] Allow selecting empty dir in clone wizard","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:04Z","receivedAt":"2008-08-17T20:44:04Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Previous behavior was to not allow selecting empty directory. While\nit seems to be sensible to use empty dir as destination and it's just\na little bit more complex, this feature is added.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |    2 +-\n .../ui/internal/clone/CloneDestinationPage.java    |   20 ++++++++++++++------\n .../egit/ui/internal/clone/GitCloneWizard.java     |    3 ++-\n .../src/org/spearce/egit/ui/uitext.properties      |    2 +-\n 4 files changed, 18 insertions(+), 9 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex 9150832..189b769 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -170,7 +170,7 @@ public class UIText extends NLS {\n \tpublic static String CloneDestinationPage_browseButton;\n \n \t/** */\n-\tpublic static String CloneDestinationPage_errorExists;\n+\tpublic static String CloneDestinationPage_errorNotEmptyDir;\n \n \t/** */\n \tpublic static String Decorator_failedLazyLoading;\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\nindex a445cf5..97f166a 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\n@@ -221,17 +221,17 @@ class CloneDestinationPage extends WizardPage {\n \t\t\tsetPageComplete(false);\n \t\t\treturn;\n \t\t}\n-\t\tfinal File dstFile = new File(dstpath);\n-\t\tif (dstFile.exists()) {\n-\t\t\tsetErrorMessage(NLS.bind(UIText.CloneDestinationPage_errorExists,\n-\t\t\t\t\tdstFile.getName()));\n+\t\tfinal File absoluteFile = new File(dstpath).getAbsoluteFile();\n+\t\tif (!isEmptyDir(absoluteFile)) {\n+\t\t\tsetErrorMessage(NLS.bind(UIText.CloneDestinationPage_errorNotEmptyDir,\n+\t\t\t\t\tabsoluteFile.getPath()));\n \t\t\tsetPageComplete(false);\n \t\t\treturn;\n \t\t}\n-\t\tfinal File absoluteFile = dstFile.getAbsoluteFile();\n+\n \t\tif (!canCreateSubdir(absoluteFile.getParentFile())) {\n \t\t\tsetErrorMessage(NLS.bind(UIText.GitCloneWizard_errorCannotCreate,\n-\t\t\t\t\tdstFile.getPath()));\n+\t\t\t\t\tabsoluteFile.getPath()));\n \t\t\tsetPageComplete(false);\n \t\t\treturn;\n \t\t}\n@@ -252,6 +252,14 @@ class CloneDestinationPage extends WizardPage {\n \t\tsetPageComplete(true);\n \t}\n \n+\tprivate static boolean isEmptyDir(final File dir) {\n+\t\tif (!dir.exists())\n+\t\t\treturn true;\n+\t\tif (!dir.isDirectory())\n+\t\t\treturn false;\n+\t\treturn dir.listFiles().length == 0;\n+\t}\n+\n \t// this is actually just an optimistic heuristic - should be named\n \t// isThereHopeThatCanCreateSubdir() as probably there is no 100% reliable\n \t// way to check that in Java for Windows\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\nindex efcf57f..10b2cd4 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\n@@ -66,7 +66,8 @@ public class GitCloneWizard extends Wizard implements IImportWizard {\n \t\tfinal String branch = cloneDestination.getInitialBranch();\n \t\tfinal String remoteName = cloneDestination.getRemote();\n \n-\t\tif (!workdir.mkdirs()) {\n+\t\tworkdir.mkdirs();\n+\t\tif (!workdir.isDirectory()) {\n \t\t\tfinal String errorMessage = NLS.bind(\n \t\t\t\t\tUIText.GitCloneWizard_errorCannotCreate, workdir.getPath());\n \t\t\tErrorDialog.openError(getShell(), getWindowTitle(),\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex 420b610..f46e183 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -73,7 +73,7 @@ CloneDestinationPage_promptInitialBranch=Initial branch\n CloneDestinationPage_promptRemoteName=Remote name\n CloneDestinationPage_fieldRequired={0} is required.\n CloneDestinationPage_browseButton=Browse\n-CloneDestinationPage_errorExists={0} already exists.\n+CloneDestinationPage_errorNotEmptyDir={0} is not an empty directory.\n \n Decorator_failedLazyLoading=Resource decorator failed to load tree contents on demand.\n QuickDiff_failedLoading=Quick diff failed to obtain file data.\n-- \n1.5.6.3\n"},{"id":"87490","messageId":"1219005852-21496-25-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-24-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 24/31] Clone wizard: force dir to suggested path only if repo selection change","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:05Z","receivedAt":"2008-08-17T20:44:05Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"When user moves backward in wizard from destination page to source\nbranch selection, then moves forward, we shouldn't overwrite his/her\ndirectory selection in destination page.\nLet's do it only when repository selection changes.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../ui/internal/clone/CloneDestinationPage.java    |   16 ++++++++++------\n 1 files changed, 10 insertions(+), 6 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\nindex 97f166a..508781d 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/CloneDestinationPage.java\n@@ -278,15 +278,19 @@ class CloneDestinationPage extends WizardPage {\n \t\t\tcheckPage();\n \t\t\treturn;\n \t\t}\n-\t\tvalidatedRepoSelection = sourcePage.getSelection();\n+\t\t\n+\t\tif (!sourcePage.selectionEquals(validatedRepoSelection)) {\n+\t\t\tvalidatedRepoSelection = sourcePage.getSelection();\n+\t\t\t// update repo-related selection only if it changed\n+\t\t\tfinal String n = getSuggestedName();\n+\t\t\tsetDescription(NLS.bind(UIText.CloneDestinationPage_description, n));\n+\t\t\tdirectoryText.setText(new File(ResourcesPlugin.getWorkspace()\n+\t\t\t\t\t.getRoot().getRawLocation().toFile(), n).getAbsolutePath());\n+\t\t}\n+\t\t\n \t\tvalidatedSelectedBranches = branchPage.getSelectedBranches();\n \t\tvalidatedHEAD = branchPage.getHEAD();\n \n-\t\tfinal String n = getSuggestedName();\n-\t\tsetDescription(NLS.bind(UIText.CloneDestinationPage_description, n));\n-\t\tdirectoryText.setText(new File(ResourcesPlugin.getWorkspace().getRoot()\n-\t\t\t\t.getRawLocation().toFile(), n).getAbsolutePath());\n-\n \t\tinitialBranch.removeAll();\n \t\tfinal Ref head = branchPage.getHEAD();\n \t\tint newix = 0;\n-- \n1.5.6.3\n"},{"id":"87489","messageId":"1219005852-21496-26-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-25-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 25/31] Create ListRemoteOperation for listing remote repo branches","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:06Z","receivedAt":"2008-08-17T20:44:06Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"This code is a common task, so it's now bundled in this operation\nwith simple progress monitor information.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/egit/core/CoreText.java        |    3 +\n .../src/org/spearce/egit/core/coretext.properties  |    2 +\n .../spearce/egit/core/op/ListRemoteOperation.java  |  104 ++++++++++++++++++++\n 3 files changed, 109 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/op/ListRemoteOperation.java\n\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/CoreText.java b/org.spearce.egit.core/src/org/spearce/egit/core/CoreText.java\nindex 8fbda4a..5974a5f 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/CoreText.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/CoreText.java\n@@ -95,6 +95,9 @@ public class CoreText extends NLS {\n \t/** */\n \tpublic static String CloneOperation_title;\n \n+\t/** */\n+\tpublic static String ListRemoteOperation_title;\n+\n \tstatic {\n \t\tfinal Class c = CoreText.class;\n \t\tinitializeMessages(c.getPackage().getName() + \".coretext\", c);\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/coretext.properties b/org.spearce.egit.core/src/org/spearce/egit/core/coretext.properties\nindex 3b3c229..c412161 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/coretext.properties\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/coretext.properties\n@@ -55,3 +55,5 @@ CheckpointJob_writingTrees=modified trees\n CheckpointJob_failed=Failed to write modified objects.\n \n CloneOperation_title=Cloning from {0}\n+\n+ListRemoteOperation_title=Getting remote branches information\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/op/ListRemoteOperation.java b/org.spearce.egit.core/src/org/spearce/egit/core/op/ListRemoteOperation.java\nnew file mode 100644\nindex 0000000..d3f777a\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/ListRemoteOperation.java\n@@ -0,0 +1,104 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.core.op;\n+\n+import java.lang.reflect.InvocationTargetException;\n+import java.util.Collection;\n+import java.util.Map;\n+\n+import org.eclipse.core.runtime.IProgressMonitor;\n+import org.eclipse.jface.operation.IRunnableWithProgress;\n+import org.spearce.egit.core.CoreText;\n+import org.spearce.jgit.errors.NotSupportedException;\n+import org.spearce.jgit.errors.TransportException;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.transport.Connection;\n+import org.spearce.jgit.transport.Transport;\n+import org.spearce.jgit.transport.URIish;\n+\n+/**\n+ * Operation of listing remote repository advertised refs.\n+ */\n+public class ListRemoteOperation implements IRunnableWithProgress {\n+\tprivate final Repository localDb;\n+\n+\tprivate final URIish uri;\n+\n+\tprivate Map<String, Ref> remoteRefsMap;\n+\n+\t/**\n+\t * Create listing operation for specified local repository (needed by\n+\t * transport) and remote repository URI.\n+\t * \n+\t * @param localDb\n+\t *            local repository (needed for transport) where fetch would\n+\t *            occur.\n+\t * @param uri\n+\t *            URI of remote repository to list.\n+\t */\n+\tpublic ListRemoteOperation(final Repository localDb, final URIish uri) {\n+\t\tthis.localDb = localDb;\n+\t\tthis.uri = uri;\n+\t}\n+\n+\t/**\n+\t * @return collection of refs advertised by remote side.\n+\t * @throws IllegalStateException\n+\t *             if error occurred during earlier remote refs listing.\n+\t */\n+\tpublic Collection<Ref> getRemoteRefs() {\n+\t\tcheckState();\n+\t\treturn remoteRefsMap.values();\n+\t}\n+\n+\t/**\n+\t * @param refName\n+\t *            remote ref name to search for.\n+\t * @return ref with specified refName or null if not found.\n+\t * @throws IllegalStateException\n+\t *             if error occurred during earlier remote refs listing.\n+\t */\n+\tpublic Ref getRemoteRef(final String refName) {\n+\t\tcheckState();\n+\t\treturn remoteRefsMap.get(refName);\n+\t}\n+\n+\tpublic void run(IProgressMonitor pm) throws InvocationTargetException,\n+\t\t\tInterruptedException {\n+\t\tTransport transport = null;\n+\t\tConnection connection = null;\n+\t\ttry {\n+\t\t\ttransport = Transport.open(localDb, uri);\n+\n+\t\t\tif (pm != null)\n+\t\t\t\tpm.beginTask(CoreText.ListRemoteOperation_title,\n+\t\t\t\t\t\tIProgressMonitor.UNKNOWN);\n+\t\t\tconnection = transport.openFetch();\n+\t\t\tremoteRefsMap = connection.getRefsMap();\n+\t\t} catch (NotSupportedException e) {\n+\t\t\tthrow new InvocationTargetException(e);\n+\t\t} catch (TransportException e) {\n+\t\t\tthrow new InvocationTargetException(e);\n+\t\t} finally {\n+\t\t\tif (connection != null)\n+\t\t\t\tconnection.close();\n+\t\t\tif (transport != null)\n+\t\t\t\ttransport.close();\n+\t\t\tif (pm != null)\n+\t\t\t\tpm.done();\n+\t\t}\n+\t}\n+\n+\tprivate void checkState() {\n+\t\tif (remoteRefsMap == null)\n+\t\t\tthrow new IllegalStateException(\n+\t\t\t\t\t\"Error occurred during remote repo listing, no refs available\");\n+\t}\n+}\n\\ No newline at end of file\n-- \n1.5.6.3\n"},{"id":"87491","messageId":"1219005852-21496-27-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-26-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 26/31] Make Clone's SourceBranchPage more user-friendly","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:07Z","receivedAt":"2008-08-17T20:44:07Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"-Show page description and progress monitor so user get some feedback\nwhat's happening when we're listing remote refs (which may take some\ntime... sometimes).\n-Don't display error during connection to remote repo.\n-Set page error before showing error dialog (avoid flashing over Our\nDear User eyers).\n-Enable/disable select all and unselect all buttons in response to\ncurrent selections.\n-Use ListRemoteOperation instead of our own anonymous class. This\nintroduces some progress information. It also remove unnecessary\n(wrong?) code for cancellation handling and always close Transport (even\nif fetch is not supported by Transport).\n\nPerhaps-making-too-big-commit...\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |    9 +-\n .../egit/ui/internal/clone/GitCloneWizard.java     |    1 +\n .../egit/ui/internal/clone/SourceBranchPage.java   |  134 +++++++++-----------\n .../src/org/spearce/egit/ui/uitext.properties      |    3 +-\n 4 files changed, 67 insertions(+), 80 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex 189b769..9b290f3 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -119,6 +119,9 @@ public class UIText extends NLS {\n \tpublic static String SourceBranchPage_title;\n \n \t/** */\n+\tpublic static String SourceBranchPage_description;\n+\n+\t/** */\n \tpublic static String SourceBranchPage_branchList;\n \n \t/** */\n@@ -134,15 +137,15 @@ public class UIText extends NLS {\n \tpublic static String SourceBranchPage_transportError;\n \n \t/** */\n-\tpublic static String SourceBranchPage_interrupted;\n-\n-\t/** */\n \tpublic static String SourceBranchPage_cannotListBranches;\n \n \t/** */\n \tpublic static String SourceBranchPage_remoteListingCancelled;\n \n \t/** */\n+\tpublic static String SourceBranchPage_cannotCreateTemp;\n+\n+\t/** */\n \tpublic static String CloneDestinationPage_title;\n \n \t/** */\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\nindex 10b2cd4..a69dc52 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java\n@@ -44,6 +44,7 @@ public class GitCloneWizard extends Wizard implements IImportWizard {\n \tpublic void init(IWorkbench arg0, IStructuredSelection arg1) {\n \t\tsetWindowTitle(UIText.GitCloneWizard_title);\n \t\tsetDefaultPageImageDescriptor(UIIcons.WIZBAN_IMPORT_REPO);\n+\t\tsetNeedsProgressMonitor(true);\n \t\tcloneSource = new RepositorySelectionPage(true);\n \t\tvalidSource = new SourceBranchPage(cloneSource);\n \t\tcloneDestination = new CloneDestinationPage(cloneSource, validSource);\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java\nindex 7710612..030419d 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/SourceBranchPage.java\n@@ -10,20 +10,16 @@\n package org.spearce.egit.ui.internal.clone;\n \n import java.io.File;\n+import java.io.IOException;\n import java.lang.reflect.InvocationTargetException;\n import java.util.ArrayList;\n-import java.util.Collection;\n import java.util.Collections;\n import java.util.Comparator;\n import java.util.List;\n \n-import org.eclipse.core.runtime.IProgressMonitor;\n import org.eclipse.core.runtime.IStatus;\n-import org.eclipse.core.runtime.NullProgressMonitor;\n-import org.eclipse.core.runtime.OperationCanceledException;\n import org.eclipse.core.runtime.Status;\n import org.eclipse.jface.dialogs.ErrorDialog;\n-import org.eclipse.jface.operation.IRunnableWithProgress;\n import org.eclipse.osgi.util.NLS;\n import org.eclipse.swt.SWT;\n import org.eclipse.swt.events.SelectionAdapter;\n@@ -36,6 +32,7 @@ import org.eclipse.swt.widgets.Composite;\n import org.eclipse.swt.widgets.Label;\n import org.eclipse.swt.widgets.Table;\n import org.eclipse.swt.widgets.TableItem;\n+import org.spearce.egit.core.op.ListRemoteOperation;\n import org.spearce.egit.ui.Activator;\n import org.spearce.egit.ui.UIText;\n import org.spearce.egit.ui.internal.components.BaseWizardPage;\n@@ -45,8 +42,7 @@ import org.spearce.egit.ui.internal.components.SelectionChangeListener;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.Ref;\n import org.spearce.jgit.lib.Repository;\n-import org.spearce.jgit.transport.FetchConnection;\n-import org.spearce.jgit.transport.Transport;\n+import org.spearce.jgit.transport.URIish;\n \n class SourceBranchPage extends BaseWizardPage {\n \tprivate final RepositorySelectionPage sourcePage;\n@@ -69,7 +65,8 @@ class SourceBranchPage extends BaseWizardPage {\n \t\tsuper(SourceBranchPage.class.getName());\n \t\tsourcePage = sp;\n \t\tsetTitle(UIText.SourceBranchPage_title);\n-\n+\t\tsetDescription(UIText.SourceBranchPage_description);\n+\t\t\n \t\tsourcePage.addSelectionListener(new SelectionChangeListener() {\n \t\t\tpublic void selectionChanged() {\n \t\t\t\tif (!sourcePage.selectionEquals(validatedRepoSelection))\n@@ -134,10 +131,10 @@ class SourceBranchPage extends BaseWizardPage {\n \n \t\tfinal Composite bPanel = new Composite(panel, SWT.NONE);\n \t\tbPanel.setLayout(new RowLayout());\n-\t\tButton b;\n-\t\tb = new Button(bPanel, SWT.PUSH);\n-\t\tb.setText(UIText.SourceBranchPage_selectAll);\n-\t\tb.addSelectionListener(new SelectionAdapter() {\n+\t\tfinal Button selectB;\n+\t\tselectB = new Button(bPanel, SWT.PUSH);\n+\t\tselectB.setText(UIText.SourceBranchPage_selectAll);\n+\t\tselectB.addSelectionListener(new SelectionAdapter() {\n \t\t\tpublic void widgetSelected(final SelectionEvent e) {\n \t\t\t\tfor (int i = 0; i < refsTable.getItemCount(); i++)\n \t\t\t\t\trefsTable.getItem(i).setChecked(true);\n@@ -147,9 +144,9 @@ class SourceBranchPage extends BaseWizardPage {\n \t\t\t\tcheckPage();\n \t\t\t}\n \t\t});\n-\t\tb = new Button(bPanel, SWT.PUSH);\n-\t\tb.setText(UIText.SourceBranchPage_selectNone);\n-\t\tb.addSelectionListener(new SelectionAdapter() {\n+\t\tfinal Button unselectB = new Button(bPanel, SWT.PUSH);\n+\t\tunselectB.setText(UIText.SourceBranchPage_selectNone);\n+\t\tunselectB.addSelectionListener(new SelectionAdapter() {\n \t\t\tpublic void widgetSelected(final SelectionEvent e) {\n \t\t\t\tfor (int i = 0; i < refsTable.getItemCount(); i++)\n \t\t\t\t\trefsTable.getItem(i).setChecked(false);\n@@ -160,6 +157,13 @@ class SourceBranchPage extends BaseWizardPage {\n \t\t});\n \t\tbPanel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));\n \n+\t\taddSelectionListener(new SelectionChangeListener() {\n+\t\t\tpublic void selectionChanged() {\n+\t\t\t\tselectB.setEnabled(selectedRefs.size() != availableRefs.size());\n+\t\t\t\tunselectB.setEnabled(selectedRefs.size() != 0);\n+\t\t\t}\n+\t\t});\n+\n \t\tsetControl(panel);\n \t\tcheckPage();\n \t}\n@@ -210,7 +214,8 @@ class SourceBranchPage extends BaseWizardPage {\n \t\tavailableRefs.clear();\n \t\tselectedRefs.clear();\n \t\trefsTable.removeAll();\n-\t\tcheckPage();\n+\t\tsetPageComplete(false);\n+\t\tsetErrorMessage(null);\n \t\tlabel.getDisplay().asyncExec(new Runnable() {\n \t\t\tpublic void run() {\n \t\t\t\trevalidateImpl(newRepoSelection);\n@@ -222,73 +227,50 @@ class SourceBranchPage extends BaseWizardPage {\n \t\tif (label.isDisposed() || !isCurrentPage())\n \t\t\treturn;\n \n+\t\tfinal ListRemoteOperation listRemoteOp; \n \t\ttry {\n-\t\t\tgetContainer().run(true, true, new IRunnableWithProgress() {\n-\t\t\t\tpublic void run(final IProgressMonitor pm)\n-\t\t\t\t\t\tthrows InvocationTargetException, InterruptedException {\n-\t\t\t\t\tfinal IProgressMonitor monitor;\n-\t\t\t\t\tif (pm == null)\n-\t\t\t\t\t\tmonitor = new NullProgressMonitor();\n-\t\t\t\t\telse\n-\t\t\t\t\t\tmonitor = pm;\n-\t\t\t\t\ttry {\n-\t\t\t\t\t\tfinal Repository db = new Repository(new File(\"/tmp\"));\n-\t\t\t\t\t\tfinal Transport tn = Transport.open(db,\n-\t\t\t\t\t\t\t\tnewRepoSelection.getURI());\n-\t\t\t\t\t\tfinal Collection<Ref> adv;\n-\t\t\t\t\t\tfinal FetchConnection fn = tn.openFetch();\n-\t\t\t\t\t\ttry {\n-\t\t\t\t\t\t\tadv = fn.getRefs();\n-\t\t\t\t\t\t} finally {\n-\t\t\t\t\t\t\tfn.close();\n-\t\t\t\t\t\t\ttn.close();\n-\t\t\t\t\t\t}\n-\n-\t\t\t\t\t\tfinal Ref idHEAD = fn.getRef(Constants.HEAD);\n-\t\t\t\t\t\thead = null;\n-\t\t\t\t\t\tfor (final Ref r : adv) {\n-\t\t\t\t\t\t\tfinal String n = r.getName();\n-\t\t\t\t\t\t\tif (!n.startsWith(Constants.HEADS_PREFIX + \"/\"))\n-\t\t\t\t\t\t\t\tcontinue;\n-\t\t\t\t\t\t\tavailableRefs.add(r);\n-\t\t\t\t\t\t\tif (idHEAD == null || head != null)\n-\t\t\t\t\t\t\t\tcontinue;\n-\t\t\t\t\t\t\tif (r.getObjectId().equals(idHEAD.getObjectId()))\n-\t\t\t\t\t\t\t\thead = r;\n-\t\t\t\t\t\t}\n-\t\t\t\t\t\tCollections.sort(availableRefs, new Comparator<Ref>() {\n-\t\t\t\t\t\t\tpublic int compare(final Ref o1, final Ref o2) {\n-\t\t\t\t\t\t\t\treturn o1.getName().compareTo(o2.getName());\n-\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t});\n-\t\t\t\t\t\tif (idHEAD != null && head == null) {\n-\t\t\t\t\t\t\thead = idHEAD;\n-\t\t\t\t\t\t\tavailableRefs.add(0, idHEAD);\n-\t\t\t\t\t\t}\n-\t\t\t\t\t} catch (Exception err) {\n-\t\t\t\t\t\tthrow new InvocationTargetException(err);\n-\t\t\t\t\t}\n-\t\t\t\t\tmonitor.done();\n-\t\t\t\t}\n-\t\t\t});\n+\t\t\tfinal URIish uri = newRepoSelection.getURI();\n+\t\t\tfinal Repository db = new Repository(new File(\"/tmp\"));\n+\t\t\tlistRemoteOp = new ListRemoteOperation(db, uri);\n+\t\t\tgetContainer().run(true, true, listRemoteOp);\n \t\t} catch (InvocationTargetException e) {\n \t\t\tThrowable why = e.getCause();\n-\t\t\tif ((why instanceof OperationCanceledException)) {\n-\t\t\t\ttransportError(UIText.SourceBranchPage_remoteListingCancelled);\n-\t\t\t\treturn;\n-\t\t\t} else {\n-\t\t\t\tErrorDialog.openError(getShell(),\n-\t\t\t\t\t\tUIText.SourceBranchPage_transportError,\n-\t\t\t\t\t\tUIText.SourceBranchPage_cannotListBranches, new Status(\n-\t\t\t\t\t\t\t\tIStatus.ERROR, Activator.getPluginId(), 0, why\n-\t\t\t\t\t\t\t\t\t\t.getMessage(), why.getCause()));\n-\t\t\t\ttransportError(why.getMessage());\n-\t\t\t}\n+\t\t\ttransportError(why.getMessage());\n+\t\t\tErrorDialog.openError(getShell(),\n+\t\t\t\t\tUIText.SourceBranchPage_transportError,\n+\t\t\t\t\tUIText.SourceBranchPage_cannotListBranches, new Status(\n+\t\t\t\t\t\t\tIStatus.ERROR, Activator.getPluginId(), 0, why\n+\t\t\t\t\t\t\t\t\t.getMessage(), why.getCause()));\n+\t\t\treturn;\n+\t\t} catch (IOException e) {\n+\t\t\ttransportError(UIText.SourceBranchPage_cannotCreateTemp);\n \t\t\treturn;\n \t\t} catch (InterruptedException e) {\n-\t\t\ttransportError(UIText.SourceBranchPage_interrupted);\n+\t\t\ttransportError(UIText.SourceBranchPage_remoteListingCancelled);\n \t\t\treturn;\n \t\t}\n+\t\t\n+\t\tfinal Ref idHEAD = listRemoteOp.getRemoteRef(Constants.HEAD);\n+\t\thead = null;\n+\t\tfor (final Ref r : listRemoteOp.getRemoteRefs()) {\n+\t\t\tfinal String n = r.getName();\n+\t\t\tif (!n.startsWith(Constants.HEADS_PREFIX + \"/\"))\n+\t\t\t\tcontinue;\n+\t\t\tavailableRefs.add(r);\n+\t\t\tif (idHEAD == null || head != null)\n+\t\t\t\tcontinue;\n+\t\t\tif (r.getObjectId().equals(idHEAD.getObjectId()))\n+\t\t\t\thead = r;\n+\t\t}\n+\t\tCollections.sort(availableRefs, new Comparator<Ref>() {\n+\t\t\tpublic int compare(final Ref o1, final Ref o2) {\n+\t\t\t\treturn o1.getName().compareTo(o2.getName());\n+\t\t\t}\n+\t\t});\n+\t\tif (idHEAD != null && head == null) {\n+\t\t\thead = idHEAD;\n+\t\t\tavailableRefs.add(0, idHEAD);\n+\t\t}\n \n \t\tvalidatedRepoSelection = newRepoSelection;\n \t\tfor (final Ref r : availableRefs) {\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex f46e183..55f6c87 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -54,14 +54,15 @@ RepositorySelectionPage_fileNotFound={0} does not exist.\n RepositorySelectionPage_internalError=Internal error; consult Eclipse error log.\n \n SourceBranchPage_title=Source Git Repository\n+SourceBranchPage_description=Select branches to clone from remote repository.\n SourceBranchPage_branchList=Branches of {0}:\n SourceBranchPage_selectAll=Select All\n SourceBranchPage_selectNone=Deselect All\n SourceBranchPage_errorBranchRequired=At least one branch must be selected.\n SourceBranchPage_transportError=Transport Error\n SourceBranchPage_cannotListBranches=Cannot list the available branches.\n-SourceBranchPage_interrupted=Connection attempt interrupted.\n SourceBranchPage_remoteListingCancelled=Operation cancelled\n+SourceBranchPage_cannotCreateTemp=Couldn't create temporary repository.\n \n \n CloneDestinationPage_title=Local Destination\n-- \n1.5.6.3\n"},{"id":"87487","messageId":"1219005852-21496-28-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-27-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 27/31] Add few EPL Eclipse icons","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:08Z","receivedAt":"2008-08-17T20:44:08Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"These are icons describing common taks, borrowed from official Eclipse\nicons set. Some of them can be accessed through Eclipse 3.4 API, but we\nstill should try to be compatible with 3.3 version.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n org.spearce.egit.ui/icons/elcl16/add.gif           |  Bin 0 -> 318 bytes\n org.spearce.egit.ui/icons/elcl16/clear.gif         |  Bin 0 -> 595 bytes\n org.spearce.egit.ui/icons/elcl16/delete.gif        |  Bin 0 -> 351 bytes\n org.spearce.egit.ui/icons/elcl16/trash.gif         |  Bin 0 -> 590 bytes\n .../src/org/spearce/egit/ui/UIIcons.java           |   12 ++++++++++++\n 5 files changed, 12 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.egit.ui/icons/elcl16/add.gif\n create mode 100644 org.spearce.egit.ui/icons/elcl16/clear.gif\n create mode 100644 org.spearce.egit.ui/icons/elcl16/delete.gif\n create mode 100644 org.spearce.egit.ui/icons/elcl16/trash.gif\n\ndiff --git a/org.spearce.egit.ui/icons/elcl16/add.gif b/org.spearce.egit.ui/icons/elcl16/add.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..252d7ebcb8c74d6e5de66ef0eb8856622a0e9d89\nGIT binary patch\nliteral 318\nzcmZ?wbhEHb6krfwxXQp_S{7na6>d=(Ze15;Qy1mX8t2lT<l2+$3M9LeU3-$eCZ~H%\nz&hVd=6EG_$WMN_G!orY+1rf_jLl+gstf`FOP#wFbvUf{x@0O5hJ42`K3|nv@V&Q>^\nzHOHgY9FJamJZkN+sI|wVk6ueSb}ixP)r4bLfn?%^+sU^crQUj&`rv8W|Ns9PC;*B-\nzSr{1@v>9|jW`O*}z!rUAYJrE2RKG`JYNRKhwpQrnor{(Ph`sVEnyR2Kc;c~(o$WdP\nzMb0Wl6K^dQ-0hgs;<SKI<H^q-;x25QTwKyJ($ZWU?Cc(_l2WWuEE3|(QH)cWq8R2f\nLL@ik0$Y2cszSC!^\n\nliteral 0\nHcmV?d00001\n\ndiff --git a/org.spearce.egit.ui/icons/elcl16/clear.gif b/org.spearce.egit.ui/icons/elcl16/clear.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..af30a42f83d2f91801206463e1f4601a747dd38e\nGIT binary patch\nliteral 595\nzcmZ?wbhEHb6krfwc*el+;Qr&!Uw{Aj`RCh@zn{MT*?;oxyHCG9ef|CO*PkChf49xr\nz*E9dn%Iz0#-hcJ>)9(*oeotO@Y}T67i#DHMvE%ZZJy-8O|Ni97&*$%czWwy;^S9r3\nzo`1jp^2fthKi+)${p!QdZ$ExNd-wC(&%YnO{{Hms_tzi4zyJI_W5&cyo7UdHe|Of*\nz{<*XJmMv^wG_UL6rjotu%lB?5-?_GG%gWmI%NmdGpZDg~qvKl(k8UnLx~2Hg#?qZ@\nzs#Y&<d2pit%BGAfn=)_ht+~1>v%S5&t*x!DuCAn{q`0`asHmv0urNPAKQAvYB_$;}\nzIXNyaE;cqcGBPqWG&DFkI4CG6FfcG6Ai&?>-_OtQ|Ns9C0}2#>vM@3*gfi%WTmgy`\nz2KEgNp{C6(&7tib?WP<(y|$*E9cHRboKu+W?9C^J>Z%9|aC6C9+u2x5)MuAemSmLN\nzAgMReQb|NuXu%?WgNcR`5^M(!OBhcylH0yhfQMJnWTKUV_?0Uxu3jz-u0F1z+G3{`\nzuzEW<Gq^Z<hw8|@d(Z6Z>)`F=drwRH^dde6KTp?5HjDwPt!%ua0$DEtSh$2VJr-3M\niJ8pc$DH7Bta<eUkOWnnFMTW7`(G$lfhcYoTSOWlzmhQX&\n\nliteral 0\nHcmV?d00001\n\ndiff --git a/org.spearce.egit.ui/icons/elcl16/delete.gif b/org.spearce.egit.ui/icons/elcl16/delete.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..b6922ac11cf64e16a15cf2976cdaa1e40118abed\nGIT binary patch\nliteral 351\nzcmZ?wbhEHb6krfwxXQqA+}QL>Vab=`lFvmYpNmUxXz4%DF}bf}d|cDunVH>FGrOY-\nz+D{EEKZPcINzVS9ob?$<req(LP(LQAaa2<Mn1arykc6YcsvqrxKG+9-@`(885pgB8\nz<o=u$r(!+N<;9%}cRLl~b~3{4YDdk<0Ovc)7MygqJLztJ!rlJ3t>uHn=&y~n$8D`1\nz2l;<(sQc1T_xQ+>|Ns9p5DgT6vM@3*7&7R9bb$QCz*ci$MuCTpRKNR-^bncK1eb^v\nzqDfrti^MpzbSB1VUt0b}aql73M;xvKD|}WtK1fk5iV!?;_@hVR1O_f%UNKGv4?_+f\nzX(=fl4i6boi78XK>s@7pr?3jKN=S;#aFP{YATA>-vrJN4*4aRuZH4n{cJUaejf<UD\nN@9a2m(9n^=8UUYcd3yi=\n\nliteral 0\nHcmV?d00001\n\ndiff --git a/org.spearce.egit.ui/icons/elcl16/trash.gif b/org.spearce.egit.ui/icons/elcl16/trash.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..bf961b3e00675b71052fc1798198d0e0f31d24cc\nGIT binary patch\nliteral 590\nzcmd6kJ8u&~6h<eGiK8SY27+V>1W9<<!2x!INyc#!46&U}c-f>$X*!oHsx4P*fkrfp\nzkdUUJ*m5MATV9D~=q}xqQ*MR)7pRQ=FZjx%Qyl3#IqmYs`^&7v{xD|Uc&KP7F;pgq\nzp(k)4ai~m~(&B}xhMGsZ6+<9Uq60fF4U`vYJ~QEBW@4miai9VL<)PxS9w-z*60I^W\nz(k2E{NYzlThf;t^$(2LtMKaP@jif@uW2p?KWWi-Tid#Z*Ap<#uQ<*doI%k{<q=<Ez\nz8U!-NSplg+tqH*zv{OP7lqLX+=!`a+G6K?Q!8oMMP=>~W&5X^^X5hv!nM~M!u#La@\nzImXV}_N-<02`l`F&yytoF+Uoy`Grd>SC*ER^9w8~tUlTc-u8ln-PJ<Eip90u^&4yD\nztHp1L-#GM-j{L)hFZk&j-hanWjvxPQpYdLwpS5>{_pIZ6_d0D))b^XTSFdZ${hFve\nzcv^Y!vQmFi5u0}(ZXI+tH@3IL{oD8M>^|>?V=veLG=FQyv9G_9(Tb>-T5K-=dHGsy\nT*s6DqlP~LgA8!8kORV`1ElJDZ\n\nliteral 0\nHcmV?d00001\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java\nindex edeffaa..ba14df3 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java\n@@ -47,6 +47,14 @@ public class UIIcons {\n \tpublic static final ImageDescriptor ELCL16_AUTHOR;\n \t/** Committer icon */\n \tpublic static final ImageDescriptor ELCL16_COMMITTER;\n+\t/** Delete icon */\n+\tpublic static final ImageDescriptor ELCL16_DELETE;\n+\t/** Add icon */\n+\tpublic static final ImageDescriptor ELCL16_ADD;\n+\t/** Trash icon */\n+\tpublic static final ImageDescriptor ELCL16_TRASH;\n+\t/** Clear icon */\n+\tpublic static final ImageDescriptor ELCL16_CLEAR;\n \n \t/** Import Wizard banner */\n \tpublic static final ImageDescriptor WIZBAN_IMPORT_REPO;\n@@ -68,6 +76,10 @@ public class UIIcons {\n \t\tELCL16_COMMENTS = map(\"elcl16/comment.gif\");\n \t\tELCL16_AUTHOR = map(\"elcl16/author.gif\");\n \t\tELCL16_COMMITTER = map(\"elcl16/committer.gif\");\n+\t\tELCL16_DELETE = map(\"elcl16/delete.gif\");\n+\t\tELCL16_ADD = map(\"elcl16/add.gif\");\n+\t\tELCL16_TRASH = map(\"elcl16/trash.gif\");\n+\t\tELCL16_CLEAR = map(\"elcl16/clear.gif\");\n \t}\n \n \tprivate static ImageDescriptor map(final String icon) {\n-- \n1.5.6.3\n"},{"id":"87488","messageId":"1219005852-21496-29-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-28-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 28/31] Checkbox images/screenshots","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:09Z","receivedAt":"2008-08-17T20:44:09Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Screenshots of checkboxes in various states, made at QT with Plastik\nstyle. These images may be used as workaround in cases when we can\nonly display images and handle buttons pressed, but can't display Button\ndirectly.\n\nI'm somewhat uncertain about license issues regarding these images.\nRecently, I realized that they are LGPL licensed perhaps. Is it possible\nto license that small stuff? If so, we may have to replace them by some\nother images or create ours.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../icons/checkboxes/disabled_checked.gif          |  Bin 0 -> 166 bytes\n .../icons/checkboxes/disabled_unchecked.gif        |  Bin 0 -> 125 bytes\n .../icons/checkboxes/enabled_checked.gif           |  Bin 0 -> 166 bytes\n .../icons/checkboxes/enabled_unchecked.gif         |  Bin 0 -> 157 bytes\n .../src/org/spearce/egit/ui/UIIcons.java           |   13 +++++++++++++\n 5 files changed, 13 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.egit.ui/icons/checkboxes/disabled_checked.gif\n create mode 100644 org.spearce.egit.ui/icons/checkboxes/disabled_unchecked.gif\n create mode 100644 org.spearce.egit.ui/icons/checkboxes/enabled_checked.gif\n create mode 100644 org.spearce.egit.ui/icons/checkboxes/enabled_unchecked.gif\n\ndiff --git a/org.spearce.egit.ui/icons/checkboxes/disabled_checked.gif b/org.spearce.egit.ui/icons/checkboxes/disabled_checked.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..6b86dc94de2bb83490c2f506fa76855e11b7b838\nGIT binary patch\nliteral 166\nzcmZ?wbhEHb<YnMxc+A1DcHP=z$B&&meeT@(bI+eYfB*VD5Pbjq{XY(%_)pNeC^fMp\nzHASI3vm`^o-P1RKLGdRGBNqb)gAM}_fDC3}u?uMM)SjUJy7rmmfrOp~FBly&8ZsYl\nwRFv9vVB@Kk9=FTwF10b`7-=vhDkuen`7m)zj=mVtWXE}9&9|hNTuKbq0JEx6@Bjb+\n\nliteral 0\nHcmV?d00001\n\ndiff --git a/org.spearce.egit.ui/icons/checkboxes/disabled_unchecked.gif b/org.spearce.egit.ui/icons/checkboxes/disabled_unchecked.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..4b20c7eddc78f300565f520e2661abcc4c081122\nGIT binary patch\nliteral 125\nzcmZ?wbhEHb<YnMxIK;-VcHP=@r_Viq{`~#x_dxLd^Y{N?p!iSFxhOTUBsE2$JhLQ2\nz!QIn0fI;ym3nLc;JA)1b5P%F|VAhq`aA$$)smL%zi|p0EAH;rR4DaY&YANLWdLQ@i\nZb07b3ZN2-b;lY20Ifou{s4y^C0|0zaIU@i7\n\nliteral 0\nHcmV?d00001\n\ndiff --git a/org.spearce.egit.ui/icons/checkboxes/enabled_checked.gif b/org.spearce.egit.ui/icons/checkboxes/enabled_checked.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..f25b4ae6b6f55585e2ecfeb3e4ab3ca430ec2c7a\nGIT binary patch\nliteral 166\nzcmZ?wbhEHb<YnMxc+A6KXkggWH)YDSDSLPAy?6iK^T*GD;Qj0OK=A$ZcOdxx`#&;J\nz{3qyKl$uzQnxasiS(2gP?&%xAp!k!8k&A(!L5BedKn63g*cG&RW>3(0eO67lVL{`e\nzM~oSqi@LVAWz48JHCu}#_IOiMHRtR%4<3EwSuXOZm8Zt3Lt~|ZV#NB0Lq`wEx-c+U\nF0|3l^QBME>\n\nliteral 0\nHcmV?d00001\n\ndiff --git a/org.spearce.egit.ui/icons/checkboxes/enabled_unchecked.gif b/org.spearce.egit.ui/icons/checkboxes/enabled_unchecked.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..82138fec0ce82ffa1c946740956c62d919356c63\nGIT binary patch\nliteral 157\nzcmZ?wbhEHb<YnMxc+ADn(>G=BuD$o}-+TV}IS{;m{T>LufBp^x|9}6-1d9Izor_Wv\nzOHxx5$}>wc6x=<10~i#4vM_Qn@H6Nz00GEg1{PC;22bq?TCdltJ@1^}qte-tT3O-o\nodKcU8eTEBUD&~pLdnYW{TwuX+tK-B$mjgLgtm54!0*nmS0LAf6#sB~S\n\nliteral 0\nHcmV?d00001\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java\nindex ba14df3..fcc5707 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java\n@@ -55,6 +55,15 @@ public class UIIcons {\n \tpublic static final ImageDescriptor ELCL16_TRASH;\n \t/** Clear icon */\n \tpublic static final ImageDescriptor ELCL16_CLEAR;\n+\t\n+\t/** Enabled, checked, checkbox image */\n+\tpublic static final ImageDescriptor CHECKBOX_ENABLED_CHECKED;\n+\t/** Enabled, unchecked, checkbox image */\n+\tpublic static final ImageDescriptor CHECKBOX_ENABLED_UNCHECKED;\n+\t/** Disabled, checked, checkbox image */\n+\tpublic static final ImageDescriptor CHECKBOX_DISABLED_CHECKED;\n+\t/** Disabled, unchecked, checkbox image */\n+\tpublic static final ImageDescriptor CHECKBOX_DISABLED_UNCHECKED;\n \n \t/** Import Wizard banner */\n \tpublic static final ImageDescriptor WIZBAN_IMPORT_REPO;\n@@ -80,6 +89,10 @@ public class UIIcons {\n \t\tELCL16_ADD = map(\"elcl16/add.gif\");\n \t\tELCL16_TRASH = map(\"elcl16/trash.gif\");\n \t\tELCL16_CLEAR = map(\"elcl16/clear.gif\");\n+\t\tCHECKBOX_ENABLED_CHECKED = map(\"checkboxes/enabled_checked.gif\");\n+\t\tCHECKBOX_ENABLED_UNCHECKED = map(\"checkboxes/enabled_unchecked.gif\");\n+\t\tCHECKBOX_DISABLED_CHECKED = map(\"checkboxes/disabled_checked.gif\");\n+\t\tCHECKBOX_DISABLED_UNCHECKED = map(\"checkboxes/disabled_unchecked.gif\");\n \t}\n \n \tprivate static ImageDescriptor map(final String icon) {\n-- \n1.5.6.3\n"},{"id":"87492","messageId":"1219005852-21496-30-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-29-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 29/31] Universal GUI for specifications edition: RefSpecPanel and related","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:10Z","receivedAt":"2008-08-17T20:44:10Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"RefSpecPanel provides universal GUI (Control) for editing list of RefSpec\nspecifications for both push or fetch, depending on panel configuration.\n\nIt is intended to allow user easily edit specifications, supporting user\nwith rich content assistant and giving feedback with error information as\nsoon as possible. Component uses editable specifications table and panels\nfor easy creation of new specifications basing on typical push/fetch\nschemes (like branch update, deletion, all branches update, saved\nconfiguration etc.).\n\nPossibly there are still some places when this panel usability could be\nimproved by some UI-engineer. I hope it shouldn't be very hard and panel\ncore and API should resist such changes. Keyboard handling could be\nimproved perhaps, some bugs are still open for MacOS, with one critical\nreported to bugzilla.\n\nRefSpecPage class is fetch/push WizardPage using RefSpecPanel extensively.\n\nBeside of RefSpecPanel class this commit introduces some components needed\nto build this one. As these components as potentially reusable, they are\nput in components package and declared as public.\n\nInfluenced-by-smart-comments-of: Shawn O. Pearce <spearce@spearce.org>\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |  207 +++\n .../components/CenteredImageLabelProvider.java     |   52 +\n .../internal/components/CheckboxLabelProvider.java |  138 ++\n .../internal/components/ClickableCellEditor.java   |   68 +\n .../internal/components/ComboLabelingSupport.java  |   77 +\n .../ui/internal/components/RefContentProposal.java |  140 ++\n .../egit/ui/internal/components/RefSpecPage.java   |  237 +++\n .../egit/ui/internal/components/RefSpecPanel.java  | 1823 ++++++++++++++++++++\n .../src/org/spearce/egit/ui/uitext.properties      |   71 +\n 9 files changed, 2813 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/CenteredImageLabelProvider.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/CheckboxLabelProvider.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/ClickableCellEditor.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/ComboLabelingSupport.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefContentProposal.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPanel.java\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex 9b290f3..ff2b541 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -176,6 +176,213 @@ public class UIText extends NLS {\n \tpublic static String CloneDestinationPage_errorNotEmptyDir;\n \n \t/** */\n+\tpublic static String RefSpecPanel_refChooseSome;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_refChooseSomeWildcard;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_clickToChange;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_columnDst;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_columnForce;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_columnMode;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_columnRemove;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_columnSrc;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_creationButton;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_creationButtonDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_creationDst;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_creationGroup;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_creationSrc;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_deletionButton;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_deletionButtonDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_deletionGroup;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_deletionRef;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_dstDeletionDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_dstFetchDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_dstPushDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_errorRemoteConfigDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_errorRemoteConfigTitle;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_fetch;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_srcFetchDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_forceAll;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_forceAllDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_forceDeleteDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_forceFalseDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_forceTrueDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_modeDelete;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_modeDeleteDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_modeUpdate;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_modeUpdateDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_predefinedAll;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_predefinedAllDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_predefinedConfigured;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_predefinedConfiguredDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_predefinedGroup;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_predefinedTags;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_predefinedTagsDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_push;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_srcPushDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_removeAll;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_removeAllDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_removeDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_specifications;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_srcDeleteDescription;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationDstInvalidExpression;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationDstRequired;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationRefDeleteRequired;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationRefDeleteWildcard;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationRefInvalidExpression;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationRefInvalidLocal;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationRefNonExistingRemote;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationRefNonExistingRemoteDelete;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationRefNonMatchingLocal;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationRefNonMatchingRemote;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationSpecificationsOverlappingDestination;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationSrcUpdateRequired;\n+\n+\t/** */\n+\tpublic static String RefSpecPanel_validationWildcardInconsistent;\n+\n+\t/** */\n+\tpublic static String RefSpecPage_descriptionFetch;\n+\n+\t/** */\n+\tpublic static String RefSpecPage_descriptionPush;\n+\n+\t/** */\n+\tpublic static String RefSpecPage_errorDontMatchSrc;\n+\n+\t/** */\n+\tpublic static String RefSpecPage_errorTransportDialogMessage;\n+\n+\t/** */\n+\tpublic static String RefSpecPage_errorTransportDialogTitle;\n+\n+\t/** */\n+\tpublic static String RefSpecPage_operationCancelled;\n+\n+\t/** */\n+\tpublic static String RefSpecPage_saveSpecifications;\n+\n+\t/** */\n+\tpublic static String RefSpecPage_titleFetch;\n+\n+\t/** */\n+\tpublic static String RefSpecPage_titlePush;\n+\n+\t/** */\n \tpublic static String Decorator_failedLazyLoading;\n \n \t/** */\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/CenteredImageLabelProvider.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/CenteredImageLabelProvider.java\nnew file mode 100644\nindex 0000000..8752a60\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/CenteredImageLabelProvider.java\n@@ -0,0 +1,52 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.components;\n+\n+import org.eclipse.jface.viewers.OwnerDrawLabelProvider;\n+import org.eclipse.swt.graphics.Image;\n+import org.eclipse.swt.graphics.Rectangle;\n+import org.eclipse.swt.widgets.Event;\n+import org.eclipse.swt.widgets.TableItem;\n+\n+/**\n+ * Label provider displaying image centered.\n+ * <p>\n+ * This implementation is actually workaround for lacking SWT/JFace features.\n+ * Code is based on official snippet found on Internet.\n+ */\n+// FIXME: doesn't work on Mac OS X 10.5 / Eclipse 3.3\n+public abstract class CenteredImageLabelProvider extends OwnerDrawLabelProvider {\n+\t/**\n+\t * @param element\n+\t *            element to provide label for.\n+\t * @return image for provided element.\n+\t */\n+\tprotected abstract Image getImage(final Object element);\n+\n+\t@Override\n+\tprotected void measure(Event event, Object element) {\n+\t\t// empty\n+\t}\n+\n+\t@Override\n+\tprotected void paint(final Event event, final Object element) {\n+\t\tfinal Image image = getImage(element);\n+\t\tfinal Rectangle bounds = ((TableItem) event.item)\n+\t\t\t\t.getBounds(event.index);\n+\t\tfinal Rectangle imgBounds = image.getBounds();\n+\t\tbounds.width /= 2;\n+\t\tbounds.width -= imgBounds.width / 2;\n+\t\tbounds.height /= 2;\n+\t\tbounds.height -= imgBounds.height / 2;\n+\n+\t\tfinal int x = bounds.width > 0 ? bounds.x + bounds.width : bounds.x;\n+\t\tfinal int y = bounds.height > 0 ? bounds.y + bounds.height : bounds.y;\n+\n+\t\tevent.gc.drawImage(image, x, y);\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/CheckboxLabelProvider.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/CheckboxLabelProvider.java\nnew file mode 100644\nindex 0000000..2222afc\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/CheckboxLabelProvider.java\n@@ -0,0 +1,138 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.components;\n+\n+import org.eclipse.swt.graphics.Image;\n+import org.eclipse.swt.widgets.Control;\n+import org.spearce.egit.ui.UIIcons;\n+\n+/**\n+ * Label provider displaying native check boxes images for boolean values.\n+ * Label-image is centralized.\n+ * <p>\n+ * Concrete implementations must provide object to boolean mapping.\n+ * <p>\n+ * This implementation is actually workaround for lacking features in\n+ * TableViewer. It is based on (workaround) snippets&tricks found on Internet.\n+ */\n+public abstract class CheckboxLabelProvider extends CenteredImageLabelProvider {\n+\tprivate static Image createCheckboxImage(final Control control,\n+\t\t\tboolean checked, boolean enabled) {\n+\t\t// Temporary workaround?\n+\t\tif (enabled) {\n+\t\t\tif (checked)\n+\t\t\t\treturn UIIcons.CHECKBOX_ENABLED_CHECKED.createImage();\n+\t\t\treturn UIIcons.CHECKBOX_ENABLED_UNCHECKED.createImage();\n+\t\t}\n+\t\tif (checked)\n+\t\t\treturn UIIcons.CHECKBOX_DISABLED_CHECKED.createImage();\n+\t\treturn UIIcons.CHECKBOX_DISABLED_UNCHECKED.createImage();\n+\t\t\n+\t\t// FIXME: Shawn says that blinking shell caused by below code is very\n+\t\t// annoying...(at least on Mac) - anyone knows better workaround?\n+\t\t// final Shell s = new Shell(control.getShell(), SWT.NO_TRIM);\n+\t\t// // Hopefully no platform uses exactly this color because we'll make\n+\t\t// // it transparent in the image.\n+\t\t// final Color greenScreen = new Color(control.getDisplay(), 222, 223,\n+\t\t// 224);\n+\t\t//\n+\t\t// // otherwise we have a default gray color\n+\t\t// s.setBackground(greenScreen);\n+\t\t//\n+\t\t// final Button b = new Button(s, SWT.CHECK);\n+\t\t// b.setSelection(checked);\n+\t\t// b.setEnabled(enabled);\n+\t\t// b.setBackground(greenScreen);\n+\t\t//\n+\t\t// // otherwise an image is located in a corner\n+\t\t// b.setLocation(0, 0);\n+\t\t// final Point bSize = b.computeSize(SWT.DEFAULT, SWT.DEFAULT);\n+\t\t// // otherwise an image is stretched by width\n+\t\t// bSize.x = Math.max(bSize.x, bSize.y);\n+\t\t// bSize.y = Math.max(bSize.x, bSize.y);\n+\t\t// b.setSize(bSize);\n+\t\t// s.setSize(bSize);\n+\t\t// s.open();\n+\t\t//\n+\t\t// final GC gc = new GC(b);\n+\t\t// final Image image = new Image(control.getShell().getDisplay(),\n+\t\t// bSize.x,\n+\t\t// bSize.y);\n+\t\t// gc.copyArea(image, 0, 0);\n+\t\t// gc.dispose();\n+\t\t// s.close();\n+\t\t//\n+\t\t// final ImageData imageData = image.getImageData();\n+\t\t// imageData.transparentPixel = imageData.palette.getPixel(greenScreen\n+\t\t// .getRGB());\n+\t\t// return new Image(control.getDisplay(), imageData);\n+\t}\n+\n+\tprivate final Image imageCheckedEnabled;\n+\n+\tprivate final Image imageUncheckedEnabled;\n+\n+\tprivate final Image imageCheckedDisabled;\n+\n+\tprivate final Image imageUncheckedDisabled;\n+\n+\t/**\n+\t * Create label provider for provided viewer.\n+\t * \n+\t * @param control\n+\t *            viewer where label provided is used.\n+\t */\n+\tpublic CheckboxLabelProvider(final Control control) {\n+\t\timageCheckedEnabled = createCheckboxImage(control, true, true);\n+\t\timageUncheckedEnabled = createCheckboxImage(control, false, true);\n+\t\timageCheckedDisabled = createCheckboxImage(control, true, false);\n+\t\timageUncheckedDisabled = createCheckboxImage(control, false, false);\n+\t}\n+\n+\t@Override\n+\tpublic void dispose() {\n+\t\tsuper.dispose();\n+\t\timageCheckedEnabled.dispose();\n+\t\timageUncheckedEnabled.dispose();\n+\t\timageCheckedDisabled.dispose();\n+\t\timageUncheckedDisabled.dispose();\n+\t}\n+\n+\t@Override\n+\tprotected Image getImage(final Object element) {\n+\t\tif (isEnabled(element)) {\n+\t\t\tif (isChecked(element))\n+\t\t\t\treturn imageCheckedEnabled;\n+\t\t\treturn imageUncheckedEnabled;\n+\t\t} else {\n+\t\t\tif (isChecked(element))\n+\t\t\t\treturn imageCheckedDisabled;\n+\t\t\treturn imageUncheckedDisabled;\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * @param element\n+\t *            element to provide label for.\n+\t * @return true if checkbox label should be checked for this element, false\n+\t *         otherwise.\n+\t */\n+\tprotected abstract boolean isChecked(Object element);\n+\n+\t/**\n+\t * Default implementation always return true.\n+\t * \n+\t * @param element\n+\t *            element to provide label for.\n+\t * @return true if checkbox label should be enabled for this element, false\n+\t *         otherwise.\n+\t */\n+\tprotected boolean isEnabled(final Object element) {\n+\t\treturn true;\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/ClickableCellEditor.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/ClickableCellEditor.java\nnew file mode 100644\nindex 0000000..af4cb86\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/ClickableCellEditor.java\n@@ -0,0 +1,68 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.components;\n+\n+import org.eclipse.jface.viewers.CellEditor;\n+import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;\n+import org.eclipse.swt.SWT;\n+import org.eclipse.swt.widgets.Composite;\n+import org.eclipse.swt.widgets.Control;\n+import org.eclipse.swt.widgets.Table;\n+\n+/**\n+ * Workaround class allowing usage of clickable element as a TableViewer cell,\n+ * acting as button.\n+ * <p>\n+ * setValue method of EditingSupport is called on cell click, with this cell\n+ * editor configured.\n+ * \n+ */\n+public class ClickableCellEditor extends CellEditor {\n+\n+\t/**\n+\t * Create cell editor for provided table.\n+\t * \n+\t * @param table\n+\t *            the parent table.\n+\t */\n+\tpublic ClickableCellEditor(final Table table) {\n+\t\tsuper(table, SWT.NONE);\n+\t}\n+\n+\t@Override\n+\tprotected Control createControl(Composite parent) {\n+\t\treturn null;\n+\t}\n+\n+\t@Override\n+\tprotected Object doGetValue() {\n+\t\treturn null;\n+\t}\n+\n+\t@Override\n+\tprotected void doSetFocus() {\n+\t\t// nothing to do\n+\t}\n+\n+\t@Override\n+\tprotected void doSetValue(Object value) {\n+\t\t// nothing to do\n+\t}\n+\n+\t@Override\n+\tpublic void activate() {\n+\t\t// just force setValue on editing support\n+\t\tfireApplyEditorValue();\n+\t}\n+\n+\tpublic void activate(ColumnViewerEditorActivationEvent activationEvent) {\n+\t\tif (activationEvent.eventType != ColumnViewerEditorActivationEvent.TRAVERSAL) {\n+\t\t\tsuper.activate(activationEvent);\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/ComboLabelingSupport.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/ComboLabelingSupport.java\nnew file mode 100644\nindex 0000000..724c570\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/ComboLabelingSupport.java\n@@ -0,0 +1,77 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.components;\n+\n+import java.util.Collections;\n+import java.util.List;\n+\n+import org.eclipse.jface.fieldassist.IContentProposal;\n+import org.eclipse.swt.events.SelectionAdapter;\n+import org.eclipse.swt.events.SelectionEvent;\n+import org.eclipse.swt.events.SelectionListener;\n+import org.eclipse.swt.widgets.Combo;\n+\n+/**\n+ * Support class for Combo, extending its functionality to differentiate between\n+ * item label and item content.\n+ * <p>\n+ * This implementation takes {@link IContentProposal} instances as data source.\n+ */\n+public class ComboLabelingSupport {\n+\tprivate final Combo combo;\n+\n+\tprivate List<? extends IContentProposal> proposals;\n+\n+\t/**\n+\t * Installs labeling support on provided combo. setItems method of combo\n+\t * shouldn't be called manually after that installation.\n+\t * <p>\n+\t * Support class is initialized with empty proposals list.\n+\t * \n+\t * @param combo\n+\t *            target combo to install on.\n+\t * @param selectionListener\n+\t *            listener that is notified when content is filled after label\n+\t *            being clicked. May be null.\n+\t */\n+\tpublic ComboLabelingSupport(final Combo combo,\n+\t\t\tfinal SelectionListener selectionListener) {\n+\t\tthis.combo = combo;\n+\n+\t\tcombo.addSelectionListener(new SelectionAdapter() {\n+\t\t\t@Override\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tfinal int i = combo.getSelectionIndex();\n+\t\t\t\tif (i != -1 && i < proposals.size()) {\n+\t\t\t\t\tcombo.setText(proposals.get(i).getContent());\n+\t\t\t\t\tif (selectionListener != null)\n+\t\t\t\t\t\tselectionListener.widgetSelected(e);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t});\n+\t\tsetProposals(Collections.<IContentProposal> emptyList());\n+\t}\n+\n+\t/**\n+\t * Sets input data for combo.\n+\t * <p>\n+\t * Proposals are set in provided order.\n+\t * \n+\t * @param proposals\n+\t *            model of input data.\n+\t */\n+\tpublic void setProposals(final List<? extends IContentProposal> proposals) {\n+\t\tthis.proposals = proposals;\n+\n+\t\tfinal String[] itemsLabels = new String[proposals.size()];\n+\t\tint i = 0;\n+\t\tfor (final IContentProposal p : proposals)\n+\t\t\titemsLabels[i++] = p.getLabel();\n+\t\tcombo.setItems(itemsLabels);\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefContentProposal.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefContentProposal.java\nnew file mode 100644\nindex 0000000..feeac26\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefContentProposal.java\n@@ -0,0 +1,140 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.components;\n+\n+import java.io.IOException;\n+import java.sql.Blob;\n+\n+import org.eclipse.jface.fieldassist.IContentProposal;\n+import org.spearce.egit.ui.Activator;\n+import org.spearce.jgit.lib.Commit;\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.PersonIdent;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.lib.Tag;\n+import org.spearce.jgit.lib.Tree;\n+\n+/**\n+ * Content proposal class for refs names, specifically Ref objects - name with\n+ * optionally associated object id. This class can be used for Eclipse field\n+ * assist as content proposal.\n+ * <p>\n+ * Content of this proposal is simply a ref name, but description and labels\n+ * tries to be smarter - showing easier to read label for user (stripping\n+ * prefixes) and information about pointed object if it exists locally.\n+ */\n+public class RefContentProposal implements IContentProposal {\n+\tprivate static final String PREFIXES[] = new String[] {\n+\t\t\tConstants.HEADS_PREFIX + \"/\", Constants.REMOTES_PREFIX + \"/\",\n+\t\t\tConstants.TAGS_PREFIX + \"/\" };\n+\n+\tprivate static final String PREFIXES_DESCRIPTIONS[] = new String[] {\n+\t\t\t\" [branch]\", \" [tracking branch]\", \" [tag]\" };\n+\n+\tprivate static void appendObjectSummary(final StringBuilder sb,\n+\t\t\tfinal String type, final PersonIdent author, final String message) {\n+\t\tsb.append(type + \" by \");\n+\t\tsb.append(author.getName());\n+\t\tsb.append(\"\\n\");\n+\t\tsb.append(author.getWhen());\n+\t\tsb.append(\"\\n\\n\");\n+\t\tfinal int newLine = message.indexOf('\\n');\n+\t\tfinal int last = (newLine != -1 ? newLine : message.length());\n+\t\tsb.append(message.substring(0, last));\n+\t}\n+\n+\tprivate final Repository db;\n+\n+\tprivate final String refName;\n+\n+\tprivate final ObjectId objectId;\n+\n+\t/**\n+\t * Create content proposal for specified ref.\n+\t * \n+\t * @param repo\n+\t *            repository for accessing information about objects. Could be a\n+\t *            local repository even for remote objects.\n+\t * @param ref\n+\t *            ref being a content proposal. May have null or locally\n+\t *            non-existent object id.\n+\t */\n+\tpublic RefContentProposal(final Repository repo, final Ref ref) {\n+\t\tthis(repo, ref.getName(), ref.getObjectId());\n+\t}\n+\n+\t/**\n+\t * Create content proposal for specified ref name and object id.\n+\t * \n+\t * @param repo\n+\t *            repository for accessing information about objects. Could be a\n+\t *            local repository even for remote objects.\n+\t * @param refName\n+\t *            ref name being a content proposal.\n+\t * @param objectId\n+\t *            object being pointed by this ref name. May be null or locally\n+\t *            non-existent object.\n+\t */\n+\tpublic RefContentProposal(final Repository repo, final String refName,\n+\t\t\tfinal ObjectId objectId) {\n+\t\tthis.db = repo;\n+\t\tthis.refName = refName;\n+\t\tthis.objectId = objectId;\n+\t}\n+\n+\tpublic String getContent() {\n+\t\treturn refName;\n+\t}\n+\n+\tpublic int getCursorPosition() {\n+\t\treturn refName.length();\n+\t}\n+\n+\tpublic String getDescription() {\n+\t\tif (objectId == null)\n+\t\t\treturn null;\n+\t\tfinal Object obj;\n+\t\ttry {\n+\t\t\tobj = db.mapObject(objectId, refName);\n+\t\t} catch (IOException e) {\n+\t\t\tActivator.logError(\"Unable to read object \" + objectId\n+\t\t\t\t\t+ \" for content proposal assistance\", e);\n+\t\t\treturn null;\n+\t\t}\n+\n+\t\tfinal StringBuilder sb = new StringBuilder();\n+\t\tsb.append(refName);\n+\t\tsb.append('\\n');\n+\t\tsb.append(objectId.abbreviate(db));\n+\t\tsb.append(\" - \");\n+\t\tif (obj instanceof Commit) {\n+\t\t\tfinal Commit c = ((Commit) obj);\n+\t\t\tappendObjectSummary(sb, \"commit\", c.getAuthor(), c.getMessage());\n+\t\t} else if (obj instanceof Tag) {\n+\t\t\tfinal Tag t = ((Tag) obj);\n+\t\t\tappendObjectSummary(sb, \"tag\", t.getAuthor(), t.getMessage());\n+\t\t} else if (obj instanceof Tree) {\n+\t\t\tsb.append(\"tree\");\n+\t\t} else if (obj instanceof Blob) {\n+\t\t\tsb.append(\"blob\");\n+\t\t} else\n+\t\t\tsb.append(\"locally unknown object\");\n+\t\treturn sb.toString();\n+\t}\n+\n+\tpublic String getLabel() {\n+\t\tfor (int i = 0; i < PREFIXES.length; i++)\n+\t\t\tif (refName.startsWith(PREFIXES[i]))\n+\t\t\t\treturn refName.substring(PREFIXES[i].length())\n+\t\t\t\t\t\t+ PREFIXES_DESCRIPTIONS[i];\n+\t\treturn refName;\n+\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java\nnew file mode 100644\nindex 0000000..4471e24\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java\n@@ -0,0 +1,237 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.components;\n+\n+import java.lang.reflect.InvocationTargetException;\n+import java.util.ArrayList;\n+import java.util.Collections;\n+import java.util.List;\n+\n+import org.eclipse.core.runtime.IStatus;\n+import org.eclipse.core.runtime.Status;\n+import org.eclipse.jface.dialogs.ErrorDialog;\n+import org.eclipse.osgi.util.NLS;\n+import org.eclipse.swt.SWT;\n+import org.eclipse.swt.events.SelectionAdapter;\n+import org.eclipse.swt.events.SelectionEvent;\n+import org.eclipse.swt.layout.GridData;\n+import org.eclipse.swt.layout.GridLayout;\n+import org.eclipse.swt.widgets.Button;\n+import org.eclipse.swt.widgets.Composite;\n+import org.spearce.egit.core.op.ListRemoteOperation;\n+import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIText;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.transport.RefSpec;\n+import org.spearce.jgit.transport.URIish;\n+\n+/**\n+ * This wizard page allows user easy selection of specifications for push or\n+ * fetch (configurable).\n+ * <p>\n+ * Page is relying highly on {@link RefSpecPanel} component, see its description\n+ * for details.\n+ * <p>\n+ * Page is designed to be successor of {@link RepositorySelectionPage} in\n+ * wizard.\n+ */\n+public class RefSpecPage extends BaseWizardPage {\n+\n+\tprivate final Repository local;\n+\n+\tprivate final RepositorySelectionPage repoPage;\n+\n+\tprivate final boolean pushPage;\n+\n+\tprivate RepositorySelection validatedRepoSelection;\n+\n+\tprivate RefSpecPanel specsPanel;\n+\n+\tprivate Button saveButton;\n+\n+\tprivate String transportError;\n+\n+\t/**\n+\t * Create specifications selection page for provided context.\n+\t * \n+\t * @param local\n+\t *            local repository.\n+\t * @param pushPage\n+\t *            true if this page is used for push specifications selection,\n+\t *            false if it used for fetch specifications selection.\n+\t * @param repoPage\n+\t *            repository selection page - must be predecessor of this page\n+\t *            in wizard.\n+\t */\n+\tpublic RefSpecPage(final Repository local, final boolean pushPage,\n+\t\t\tfinal RepositorySelectionPage repoPage) {\n+\t\tsuper(RefSpecPage.class.getName());\n+\t\tthis.local = local;\n+\t\tthis.repoPage = repoPage;\n+\t\tthis.pushPage = pushPage;\n+\t\tif (pushPage) {\n+\t\t\tsetTitle(UIText.RefSpecPage_titlePush);\n+\t\t\tsetDescription(UIText.RefSpecPage_descriptionPush);\n+\t\t} else {\n+\t\t\tsetTitle(UIText.RefSpecPage_titleFetch);\n+\t\t\tsetDescription(UIText.RefSpecPage_descriptionFetch);\n+\t\t}\n+\n+\t\trepoPage.addSelectionListener(new SelectionChangeListener() {\n+\t\t\tpublic void selectionChanged() {\n+\t\t\t\tif (!repoPage.selectionEquals(validatedRepoSelection))\n+\t\t\t\t\tsetPageComplete(false);\n+\t\t\t\telse\n+\t\t\t\t\tcheckPage();\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tpublic void createControl(Composite parent) {\n+\t\tfinal Composite panel = new Composite(parent, SWT.NULL);\n+\t\tpanel.setLayout(new GridLayout());\n+\n+\t\tspecsPanel = new RefSpecPanel(panel, pushPage);\n+\t\tspecsPanel.getControl().setLayoutData(\n+\t\t\t\tnew GridData(SWT.FILL, SWT.FILL, true, true));\n+\t\tspecsPanel.addRefSpecTableListener(new SelectionChangeListener() {\n+\t\t\tpublic void selectionChanged() {\n+\t\t\t\tnotifySelectionChanged();\n+\t\t\t\tcheckPage();\n+\t\t\t}\n+\t\t});\n+\n+\t\tsaveButton = new Button(panel, SWT.CHECK);\n+\t\tsaveButton.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, true, false));\n+\t\tsaveButton.addSelectionListener(new SelectionAdapter() {\n+\t\t\t@Override\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tnotifySelectionChanged();\n+\t\t\t}\n+\t\t});\n+\n+\t\tsetControl(panel);\n+\t\tnotifySelectionChanged();\n+\t\tcheckPage();\n+\t}\n+\n+\t@Override\n+\tpublic void setVisible(final boolean visible) {\n+\t\tif (visible)\n+\t\t\trevalidate();\n+\t\tsuper.setVisible(visible);\n+\t}\n+\n+\t/**\n+\t * @return ref specifications as selected by user. Returned collection is a\n+\t *         copy, so it may be modified by caller.\n+\t */\n+\tpublic List<RefSpec> getRefSpecs() {\n+\t\tif (specsPanel == null)\n+\t\t\treturn Collections.emptyList();\n+\t\telse\n+\t\t\treturn new ArrayList<RefSpec>(specsPanel.getRefSpecs());\n+\t}\n+\n+\t/**\n+\t * @return true if user chosen to save selected specification in remote\n+\t *         configuration, false otherwise.\n+\t */\n+\tpublic boolean isSaveRequested() {\n+\t\treturn saveButton.getSelection();\n+\t}\n+\n+\t/**\n+\t * Compare provided specifications to currently selected ones.\n+\t * \n+\t * @param specs\n+\t *            specifications to compare to. May be null.\n+\t * @return true if provided specifications are equal to currently selected\n+\t *         ones, false otherwise.\n+\t */\n+\tpublic boolean specsSelectionEquals(final List<RefSpec> specs) {\n+\t\treturn getRefSpecs().equals(specs);\n+\t}\n+\n+\tprivate void revalidate() {\n+\t\tfinal RepositorySelection newRepoSelection = repoPage.getSelection();\n+\n+\t\tif (repoPage.selectionEquals(validatedRepoSelection)) {\n+\t\t\t// nothing changed on previous page\n+\t\t\tcheckPage();\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tspecsPanel.clearRefSpecs();\n+\t\tspecsPanel.setEnable(false);\n+\t\tsaveButton.setVisible(false);\n+\t\tsaveButton.setSelection(false);\n+\t\tnotifySelectionChanged();\n+\t\tvalidatedRepoSelection = null;\n+\t\ttransportError = null;\n+\t\tgetControl().getDisplay().asyncExec(new Runnable() {\n+\t\t\tpublic void run() {\n+\t\t\t\trevalidateImpl(newRepoSelection);\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate void revalidateImpl(final RepositorySelection newRepoSelection) {\n+\t\tfinal ListRemoteOperation listRemotesOp;\n+\t\ttry {\n+\t\t\tfinal URIish uri;\n+\t\t\turi = newRepoSelection.getURI();\n+\t\t\tlistRemotesOp = new ListRemoteOperation(local, uri);\n+\t\t\tgetContainer().run(true, true, listRemotesOp);\n+\t\t} catch (InvocationTargetException e) {\n+\t\t\tfinal Throwable cause = e.getCause();\n+\t\t\ttransportError(cause.getMessage());\n+\t\t\tErrorDialog.openError(getShell(), UIText.RefSpecPage_errorTransportDialogTitle,\n+\t\t\t\t\tUIText.RefSpecPage_errorTransportDialogMessage, new Status(\n+\t\t\t\t\t\t\tIStatus.ERROR, Activator.getPluginId(), 0, cause\n+\t\t\t\t\t\t\t\t\t.getMessage(), cause));\n+\t\t\treturn;\n+\t\t} catch (InterruptedException e) {\n+\t\t\ttransportError(UIText.RefSpecPage_operationCancelled);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tthis.validatedRepoSelection = newRepoSelection;\n+\t\tfinal String remoteName = validatedRepoSelection.getConfigName();\n+\t\tspecsPanel.setAssistanceData(local, listRemotesOp.getRemoteRefs(),\n+\t\t\t\tremoteName);\n+\t\tif (newRepoSelection.isConfigSelected()) {\n+\t\t\tsaveButton.setVisible(true);\n+\t\t\tsaveButton.setText(NLS.bind(UIText.RefSpecPage_saveSpecifications,\n+\t\t\t\t\tremoteName));\n+\t\t\tsaveButton.pack();\n+\t\t}\n+\t\tcheckPage();\n+\t}\n+\n+\tprivate void transportError(final String message) {\n+\t\ttransportError = message;\n+\t\tcheckPage();\n+\t}\n+\n+\tprivate void checkPage() {\n+\t\tif (transportError != null) {\n+\t\t\tsetErrorMessage(transportError);\n+\t\t\tsetPageComplete(false);\n+\t\t\treturn;\n+\t\t}\n+\t\tif (!specsPanel.isEmpty() && specsPanel.isValid()\n+\t\t\t\t&& !specsPanel.isMatchingAnyRefs()) {\n+\t\t\tsetErrorMessage(UIText.RefSpecPage_errorDontMatchSrc);\n+\t\t\tsetPageComplete(false);\n+\t\t\treturn;\n+\t\t}\n+\t\tsetErrorMessage(specsPanel.getErrorMessage());\n+\t\tsetPageComplete(!specsPanel.isEmpty() && specsPanel.isValid());\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPanel.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPanel.java\nnew file mode 100644\nindex 0000000..caef4d2\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPanel.java\n@@ -0,0 +1,1823 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.components;\n+\n+import java.io.IOException;\n+import java.net.URISyntaxException;\n+import java.util.ArrayList;\n+import java.util.Collection;\n+import java.util.Collections;\n+import java.util.Comparator;\n+import java.util.HashMap;\n+import java.util.HashSet;\n+import java.util.LinkedList;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.Set;\n+import java.util.TreeSet;\n+import java.util.regex.Pattern;\n+\n+import org.eclipse.core.runtime.IStatus;\n+import org.eclipse.core.runtime.Status;\n+import org.eclipse.jface.dialogs.ErrorDialog;\n+import org.eclipse.jface.fieldassist.ComboContentAdapter;\n+import org.eclipse.jface.fieldassist.ContentProposalAdapter;\n+import org.eclipse.jface.fieldassist.ControlDecoration;\n+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;\n+import org.eclipse.jface.fieldassist.IContentProposal;\n+import org.eclipse.jface.fieldassist.IContentProposalListener;\n+import org.eclipse.jface.fieldassist.IContentProposalProvider;\n+import org.eclipse.jface.fieldassist.TextContentAdapter;\n+import org.eclipse.jface.layout.TableColumnLayout;\n+import org.eclipse.jface.resource.ImageRegistry;\n+import org.eclipse.jface.viewers.CellEditor;\n+import org.eclipse.jface.viewers.CheckboxCellEditor;\n+import org.eclipse.jface.viewers.ColumnLabelProvider;\n+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;\n+import org.eclipse.jface.viewers.ColumnWeightData;\n+import org.eclipse.jface.viewers.EditingSupport;\n+import org.eclipse.jface.viewers.IElementComparer;\n+import org.eclipse.jface.viewers.IStructuredContentProvider;\n+import org.eclipse.jface.viewers.TableViewer;\n+import org.eclipse.jface.viewers.TableViewerColumn;\n+import org.eclipse.jface.viewers.TextCellEditor;\n+import org.eclipse.jface.viewers.Viewer;\n+import org.eclipse.osgi.util.NLS;\n+import org.eclipse.swt.SWT;\n+import org.eclipse.swt.events.DisposeEvent;\n+import org.eclipse.swt.events.DisposeListener;\n+import org.eclipse.swt.events.ModifyEvent;\n+import org.eclipse.swt.events.ModifyListener;\n+import org.eclipse.swt.events.SelectionAdapter;\n+import org.eclipse.swt.events.SelectionEvent;\n+import org.eclipse.swt.events.TraverseEvent;\n+import org.eclipse.swt.events.TraverseListener;\n+import org.eclipse.swt.graphics.Color;\n+import org.eclipse.swt.graphics.Image;\n+import org.eclipse.swt.layout.GridData;\n+import org.eclipse.swt.layout.GridLayout;\n+import org.eclipse.swt.layout.RowLayout;\n+import org.eclipse.swt.widgets.Button;\n+import org.eclipse.swt.widgets.Combo;\n+import org.eclipse.swt.widgets.Composite;\n+import org.eclipse.swt.widgets.Control;\n+import org.eclipse.swt.widgets.Group;\n+import org.eclipse.swt.widgets.Label;\n+import org.eclipse.swt.widgets.Table;\n+import org.eclipse.swt.widgets.TableColumn;\n+import org.eclipse.swt.widgets.Text;\n+import org.eclipse.ui.fieldassist.ContentAssistCommandAdapter;\n+import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIIcons;\n+import org.spearce.egit.ui.UIText;\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.lib.Ref.Storage;\n+import org.spearce.jgit.transport.FetchConnection;\n+import org.spearce.jgit.transport.RefSpec;\n+import org.spearce.jgit.transport.RemoteConfig;\n+import org.spearce.jgit.transport.Transport;\n+\n+/**\n+ * This class provides universal panel for editing list of {@link RefSpec} -\n+ * specifications for both push or fetch, depending on panel configuration.\n+ * <p>\n+ * It is intended to allow user easily edit specifications, supporting user with\n+ * content assistant and giving feedback with error information as soon as\n+ * possible. Component uses editable specifications table and panels for easy\n+ * creation of new specifications basing on typical push/fetch schemes (like\n+ * branch update, deletion, all branches update, saved configuration etc.).\n+ * <p>\n+ * The model of specifications list behind panel is accessible by public methods\n+ * - giving both read and write access. Listener interface for handling changes\n+ * in model is provided by {@link SelectionChangeListener}.\n+ * <p>\n+ * Typical class usage:\n+ * \n+ * <pre>\n+ * // create panel for editing push-specifications\n+ * RefSpecPanel panel = new RefSpecPanel(parent, true);\n+ * // register model listener\n+ * panel.addRefSpecPanelListener(listener);\n+ * \n+ * // provide information about local and remote refs\n+ * panel.setRefsData(localRepo, remoteRefs, remoteName);\n+ * \n+ * // get result data \n+ * List&lt;RefSpec&gt; result = panel.getRefSpecs();\n+ * // further processing: push or save configuration...\n+ * </pre>\n+ * \n+ * @see SelectionChangeListener\n+ */\n+public class RefSpecPanel {\n+\tprivate static final String REMOTES_PREFIX_S = Constants.REMOTES_PREFIX + '/';\n+\n+\tprivate static final String HEADS_PREFIX_S = Constants.HEADS_PREFIX + '/';\n+\n+\tprivate static final String IMAGE_ADD = \"ADD\"; //$NON-NLS-1$\n+\n+\tprivate static final String IMAGE_DELETE = \"DELETE\"; //$NON-NLS-1$\n+\n+\tprivate static final String IMAGE_TRASH = \"TRASH\"; //$NON-NLS-1$\n+\n+\tprivate static final String IMAGE_CLEAR = \"CLEAR\"; //$NON-NLS-1$\n+\n+\tprivate static final int TABLE_PREFERRED_HEIGHT = 165;\n+\n+\tprivate static final int TABLE_PREFERRED_WIDTH = 560;\n+\n+\tprivate static final int COLUMN_MODE_WEIGHT = 23;\n+\n+\tprivate static final int COLUMN_SRC_WEIGHT = 40;\n+\n+\tprivate static final int COLUMN_DST_WEIGHT = 40;\n+\n+\tprivate static final int COLUMN_FORCE_WEIGHT = 30;\n+\n+\tprivate static final int COLUMN_REMOVE_WEIGHT = 20;\n+\n+\tprivate static boolean isDeleteRefSpec(final Object element) {\n+\t\treturn ((RefSpec) element).getSource() == null;\n+\t}\n+\n+\tprivate static boolean isValidRefExpression(final String s) {\n+\t\tif (RefSpec.isWildcard(s)) {\n+\t\t\t// replace wildcard with some legal name just for checking\n+\t\t\treturn Repository\n+\t\t\t\t\t.isValidRefName(s.substring(0, s.length() - 1) + 'X');\n+\t\t} else\n+\t\t\treturn Repository.isValidRefName(s);\n+\t}\n+\n+\tprivate static RefSpec setRefSpecSource(final RefSpec spec, final String src) {\n+\t\tfinal String dst;\n+\t\tif (RefSpec.isWildcard(src))\n+\t\t\tdst = wildcardSpecComponent(spec.getDestination());\n+\t\telse\n+\t\t\tdst = unwildcardSpecComponent(spec.getDestination(), src);\n+\t\treturn spec.setSourceDestination(src, dst);\n+\t}\n+\n+\tprivate static RefSpec setRefSpecDestination(final RefSpec spec,\n+\t\t\tfinal String dst) {\n+\t\tfinal String src;\n+\t\tif (RefSpec.isWildcard(dst))\n+\t\t\tsrc = wildcardSpecComponent(spec.getSource());\n+\t\telse\n+\t\t\tsrc = unwildcardSpecComponent(spec.getSource(), dst);\n+\t\treturn spec.setSourceDestination(src, dst);\n+\t}\n+\n+\tprivate static String wildcardSpecComponent(final String comp) {\n+\t\tfinal int i;\n+\t\tif (RefSpec.isWildcard(comp))\n+\t\t\treturn comp;\n+\t\tif (comp == null || (i = comp.lastIndexOf('/')) == -1) {\n+\t\t\t// That's somewhat ugly. What better can we do here?\n+\t\t\treturn UIText.RefSpecPanel_refChooseSomeWildcard;\n+\t\t}\n+\t\treturn comp.substring(0, i + 1) + '*';\n+\t}\n+\n+\tprivate static String unwildcardSpecComponent(final String comp,\n+\t\t\tfinal String other) {\n+\t\tif (!RefSpec.isWildcard(comp))\n+\t\t\treturn comp;\n+\t\tif (other == null || other.length() == 0)\n+\t\t\treturn \"\"; //$NON-NLS-1$\n+\t\tfinal int i = other.lastIndexOf('/');\n+\t\treturn comp.substring(0, comp.length() - 1) + other.substring(i + 1);\n+\t}\n+\n+\tprivate static List<RefContentProposal> createProposalsFilteredRemote(\n+\t\t\tfinal List<RefContentProposal> proposals) {\n+\t\tfinal List<RefContentProposal> result = new ArrayList<RefContentProposal>();\n+\t\tfor (final RefContentProposal p : proposals) {\n+\t\t\tfinal String content = p.getContent();\n+\t\t\tif (content.equals(Constants.HEAD)\n+\t\t\t\t\t|| content.startsWith(HEADS_PREFIX_S))\n+\t\t\t\tresult.add(p);\n+\t\t}\n+\t\treturn result;\n+\t}\n+\n+\tprivate static Image getDecorationImage(final String key) {\n+\t\treturn FieldDecorationRegistry.getDefault().getFieldDecoration(key)\n+\t\t\t\t.getImage();\n+\t}\n+\n+\tprivate static void setControlDecoration(final ControlDecoration control,\n+\t\t\tfinal String imageKey, final String description) {\n+\t\tcontrol.setImage(getDecorationImage(imageKey));\n+\t\tcontrol.setDescriptionText(description);\n+\t\tcontrol.show();\n+\t}\n+\n+\tprivate final List<RefSpec> specs = new ArrayList<RefSpec>();\n+\n+\tprivate final Composite panel;\n+\n+\tprivate TableViewer tableViewer;\n+\n+\tprivate CellEditor modeCellEditor;\n+\n+\tprivate CellEditor localRefCellEditor;\n+\n+\tprivate CellEditor remoteRefCellEditor;\n+\n+\tprivate CellEditor forceUpdateCellEditor;\n+\n+\tprivate CellEditor removeSpecCellEditor;\n+\n+\tprivate int srcColumnIndex;\n+\n+\tprivate Button removeAllSpecButton;\n+\n+\tprivate Button forceUpdateAllButton;\n+\n+\tprivate Button creationButton;\n+\n+\tprivate Button addConfiguredButton;\n+\n+\tprivate Button addTagsButton;\n+\n+\tprivate Button addBranchesButton;\n+\n+\tprivate ControlDecoration creationSrcDecoration;\n+\n+\tprivate ControlDecoration creationDstDecoration;\n+\n+\tprivate ControlDecoration deleteRefDecoration;\n+\n+\tprivate Combo creationSrcCombo;\n+\n+\tprivate Combo creationDstCombo;\n+\n+\tprivate Combo deleteRefCombo;\n+\n+\tprivate Button deleteButton;\n+\n+\tprivate Repository localDb;\n+\n+\tprivate String remoteName;\n+\n+\tprivate Set<String> localRefNames = Collections.emptySet();\n+\n+\tprivate Set<String> remoteRefNames = Collections.emptySet();\n+\n+\tprivate List<RefSpec> predefinedConfigured = Collections.emptyList();\n+\n+\tprivate RefSpec predefinedBranches = null;\n+\n+\tprivate final RefContentProposalProvider remoteProposalProvider;\n+\n+\tprivate final RefContentProposalProvider localProposalProvider;\n+\n+\tprivate ComboLabelingSupport creationSrcComboSupport;\n+\n+\tprivate ComboLabelingSupport creationDstComboSupport;\n+\n+\tprivate ComboLabelingSupport deleteRefComboSupport;\n+\n+\tprivate final boolean pushSpecs;\n+\n+\tprivate final List<SelectionChangeListener> listeners = new LinkedList<SelectionChangeListener>();\n+\n+\tprivate final ImageRegistry imageRegistry;\n+\n+\tprivate boolean matchingAnyRefs;\n+\n+\tprivate RefSpec invalidSpec;\n+\n+\tprivate RefSpec invalidSpecSameDst;\n+\n+\tprivate String errorMessage;\n+\n+\tprivate Color errorBackgroundColor;\n+\n+\tprivate Color errorTextColor;\n+\n+\t/**\n+\t * Create a new panel and install it on a provided composite. Panel is\n+\t * created either for editing push or fetch specifications - this setting\n+\t * can't be changed later, after constructing object.\n+\t * <p>\n+\t * Panel is created with an empty model, with no provided assistant. It\n+\t * can't be used by user until\n+\t * {@link #setAssistanceData(Repository, Collection, String)} method is\n+\t * called, and to this time is disabled.\n+\t * \n+\t * @param parent\n+\t *            parent control for panel.\n+\t * @param pushSpecs\n+\t *            true if panel is used for editing push specifications, false\n+\t *            if panel is used for editing fetch specifications.\n+\t */\n+\tpublic RefSpecPanel(final Composite parent, final boolean pushSpecs) {\n+\t\tthis.pushSpecs = pushSpecs;\n+\t\tthis.localProposalProvider = new RefContentProposalProvider(true);\n+\t\tthis.remoteProposalProvider = new RefContentProposalProvider(false);\n+\t\tthis.imageRegistry = new ImageRegistry(parent.getDisplay());\n+\n+\t\tpanel = new Composite(parent, SWT.NONE);\n+\t\tpanel.setLayout(new GridLayout());\n+\n+\t\tsafeCreateResources();\n+\n+\t\tcreateCreationPanel();\n+\t\tif (pushSpecs)\n+\t\t\tcreateDeleteCreationPanel();\n+\t\tcreatePredefinedCreationPanel();\n+\t\tcreateTableGroup();\n+\n+\t\taddRefSpecTableListener(new SelectionChangeListener() {\n+\t\t\tpublic void selectionChanged() {\n+\t\t\t\tvalidateSpecs();\n+\t\t\t}\n+\t\t});\n+\t\tsetEnable(false);\n+\t}\n+\n+\t/**\n+\t * Enable or disable panel controls.\n+\t * \n+\t * @param enable\n+\t *            true to enable panel, false to disable.\n+\t */\n+\tpublic void setEnable(final boolean enable) {\n+\t\tgetControl().setEnabled(enable);\n+\t}\n+\n+\t/**\n+\t * Set information needed for assisting user with entering data and\n+\t * validating user input. This method automatically enables the panel.\n+\t * \n+\t * @param localRepo\n+\t *            local repository where specifications will be applied.\n+\t * @param remoteRefs\n+\t *            collection of remote refs as advertised by remote repository.\n+\t *            Typically they are collected by {@link FetchConnection}\n+\t *            implementation.\n+\t * @param remoteName\n+\t *            optional name for remote configuration, if edited\n+\t *            specification list is related to this remote configuration.\n+\t *            Can be null. When not null, panel is filled with default\n+\t *            fetch/push specifications for this remote configuration.\n+\t */\n+\tpublic void setAssistanceData(final Repository localRepo,\n+\t\t\tfinal Collection<Ref> remoteRefs, final String remoteName) {\n+\t\tthis.localDb = localRepo;\n+\t\tthis.remoteName = remoteName;\n+\n+\t\tfinal List<RefContentProposal> remoteProposals = createContentProposals(\n+\t\t\t\tremoteRefs, null);\n+\t\tremoteProposalProvider.setProposals(remoteProposals);\n+\t\tremoteRefNames = new HashSet<String>();\n+\t\tfor (final RefContentProposal p : remoteProposals)\n+\t\t\tremoteRefNames.add(p.getContent());\n+\n+\t\tRef HEAD = null;\n+\t\ttry {\n+\t\t\tfinal ObjectId id = localDb.resolve(Constants.HEAD);\n+\t\t\tif (id != null)\n+\t\t\t\tHEAD = new Ref(Storage.LOOSE, Constants.HEAD, id);\n+\t\t} catch (IOException e) {\n+\t\t\tActivator.logError(\"Couldn't read HEAD from local repository\", e); //$NON-NLS-1$\n+\t\t}\n+\t\tfinal List<RefContentProposal> localProposals = createContentProposals(\n+\t\t\t\tlocalDb.getAllRefs().values(), HEAD);\n+\t\tlocalProposalProvider.setProposals(localProposals);\n+\t\tlocalRefNames = new HashSet<String>();\n+\t\tfor (final RefContentProposal ref : localProposals)\n+\t\t\tlocalRefNames.add(ref.getContent());\n+\n+\t\tfinal List<RefContentProposal> localFilteredProposals = createProposalsFilteredLocal(localProposals);\n+\t\tfinal List<RefContentProposal> remoteFilteredProposals = createProposalsFilteredRemote(remoteProposals);\n+\n+\t\tif (pushSpecs) {\n+\t\t\tcreationSrcComboSupport.setProposals(localFilteredProposals);\n+\t\t\tcreationDstComboSupport.setProposals(remoteFilteredProposals);\n+\t\t} else {\n+\t\t\tcreationSrcComboSupport.setProposals(remoteFilteredProposals);\n+\t\t\tcreationDstComboSupport.setProposals(localFilteredProposals);\n+\t\t}\n+\t\tvalidateCreationPanel();\n+\n+\t\tif (pushSpecs) {\n+\t\t\tdeleteRefComboSupport.setProposals(remoteFilteredProposals);\n+\t\t\tvalidateDeleteCreationPanel();\n+\t\t}\n+\n+\t\ttry {\n+\t\t\tfinal RemoteConfig rc = new RemoteConfig(localDb.getConfig(),\n+\t\t\t\t\tremoteName);\n+\t\t\tif (pushSpecs)\n+\t\t\t\tpredefinedConfigured = rc.getPushRefSpecs();\n+\t\t\telse\n+\t\t\t\tpredefinedConfigured = rc.getFetchRefSpecs();\n+\t\t\tfor (final RefSpec spec : predefinedConfigured)\n+\t\t\t\taddRefSpec(spec);\n+\t\t} catch (URISyntaxException e) {\n+\t\t\tpredefinedConfigured = null;\n+\t\t\tErrorDialog.openError(panel.getShell(),\n+\t\t\t\t\tUIText.RefSpecPanel_errorRemoteConfigTitle,\n+\t\t\t\t\tUIText.RefSpecPanel_errorRemoteConfigDescription,\n+\t\t\t\t\tnew Status(IStatus.ERROR, Activator.getPluginId(), 0, e\n+\t\t\t\t\t\t\t.getMessage(), e));\n+\t\t}\n+\t\tupdateAddPredefinedButton(addConfiguredButton, predefinedConfigured);\n+\t\tif (pushSpecs)\n+\t\t\tpredefinedBranches = Transport.REFSPEC_PUSH_ALL;\n+\t\telse\n+\t\t\tpredefinedBranches = new RefSpec(\"refs/heads/*:refs/remotes/\" //$NON-NLS-1$\n+\t\t\t\t\t+ remoteName + \"/*\"); //$NON-NLS-1$\n+\t\tupdateAddPredefinedButton(addBranchesButton, predefinedBranches);\n+\t\tsetEnable(true);\n+\t}\n+\n+\t/**\n+\t * @return underlying control for this panel.\n+\t */\n+\tpublic Control getControl() {\n+\t\treturn panel;\n+\t}\n+\n+\t/**\n+\t * Return current list of specifications of this panel.\n+\t * <p>\n+\t * This method should be called only from the UI thread.\n+\t * \n+\t * @return unmodifiable view of specifications list as edited by user in\n+\t *         this panel. Note that this view underlying model may change -\n+\t *         create a copy if needed.\n+\t */\n+\tpublic List<RefSpec> getRefSpecs() {\n+\t\treturn Collections.unmodifiableList(specs);\n+\t}\n+\n+\t/**\n+\t * @return true if specifications list is empty, false otherwise.\n+\t */\n+\tpublic boolean isEmpty() {\n+\t\treturn getRefSpecs().isEmpty();\n+\t}\n+\n+\t/**\n+\t * @return true if specifications match any ref(s) in source repository -\n+\t *         resolve to concrete ref updates, false otherwise. For non empty\n+\t *         specifications list, false value is possible only in case of\n+\t *         specifications with wildcards.\n+\t */\n+\tpublic boolean isMatchingAnyRefs() {\n+\t\treturn matchingAnyRefs;\n+\t}\n+\n+\t/**\n+\t * Add provided specification to this panel. Panel view is automatically\n+\t * refreshed, model is revalidated.\n+\t * <p>\n+\t * Note that the same reference can't be added twice to the panel, while two\n+\t * or more equals RefSpec (in terms of equals method) can be - likely\n+\t * causing validation error.\n+\t * <p>\n+\t * This method should be called only from the UI thread.\n+\t * \n+\t * @param spec\n+\t *            specification to add.\n+\t * @throws IllegalArgumentException\n+\t *             if specification with same reference already exists in panel.\n+\t */\n+\tpublic void addRefSpec(final RefSpec spec) {\n+\t\tfinal int i = indexOfSpec(spec);\n+\t\tif (i != -1)\n+\t\t\tthrow new IllegalArgumentException(\"RefSpec \" + spec //$NON-NLS-1$\n+\t\t\t\t\t+ \" already exists.\"); //$NON-NLS-1$\n+\t\tspecs.add(spec);\n+\t\ttableViewer.add(spec);\n+\t\tnotifySpecsChanged();\n+\t}\n+\n+\t/**\n+\t * Remove provided specification from this panel. Panel view is\n+\t * automatically refreshed, model is revalidated.\n+\t * <p>\n+\t * Provided specification must be equals with existing one in terms of\n+\t * reference equality, not an equals method.\n+\t * <p>\n+\t * This method should be called only from the UI thread.\n+\t * \n+\t * @param spec\n+\t *            specification to remove.\n+\t * @throws IllegalArgumentException\n+\t *             if specification with this reference doesn't exist in this\n+\t *             panel.\n+\t */\n+\tpublic void removeRefSpec(final RefSpec spec) {\n+\t\tfinal int i = indexOfSpec(spec);\n+\t\tif (i == -1)\n+\t\t\tthrow new IllegalArgumentException(\"RefSpec \" + spec //$NON-NLS-1$\n+\t\t\t\t\t+ \" not found.\"); //$NON-NLS-1$\n+\t\tspecs.remove(i);\n+\t\ttableViewer.remove(spec);\n+\t\tnotifySpecsChanged();\n+\t}\n+\n+\t/**\n+\t * Change some specification to the new one.\n+\t * <p>\n+\t * Old specification must exist in the panel, while new specification can't\n+\t * exist before (both in terms of reference equality).\n+\t * <p>\n+\t * This method should be called only from the UI thread.\n+\t * \n+\t * @param oldSpec\n+\t *            specification to change. Can't be null.\n+\t * @param newSpec\n+\t *            new specification to override existing one. Can't be null.\n+\t */\n+\tpublic void setRefSpec(final RefSpec oldSpec, final RefSpec newSpec) {\n+\t\tfinal int oldI = indexOfSpec(oldSpec);\n+\t\tif (oldI == -1)\n+\t\t\tthrow new IllegalArgumentException(\"RefSpec \" + oldSpec //$NON-NLS-1$\n+\t\t\t\t\t+ \" not found.\"); //$NON-NLS-1$\n+\t\tfinal int newI = indexOfSpec(newSpec);\n+\t\tif (newI != -1)\n+\t\t\tthrow new IllegalArgumentException(\"RefSpec \" + newSpec //$NON-NLS-1$\n+\t\t\t\t\t+ \" already exists.\"); //$NON-NLS-1$\n+\t\tspecs.set(oldI, newSpec);\n+\n+\t\t// have to refresh whole table as we are operating on immutable objects\n+\t\t// (this shouldn't be an issue)\n+\t\ttableViewer.refresh();\n+\t\tnotifySpecsChanged();\n+\t}\n+\n+\t/**\n+\t * Clear all specifications from this panel.\n+\t * <p>\n+\t * This method should be called only from the UI thread.\n+\t */\n+\tpublic void clearRefSpecs() {\n+\t\tfinal RefSpec toRemove[] = specs.toArray(new RefSpec[0]);\n+\t\tspecs.clear();\n+\t\ttableViewer.remove(toRemove);\n+\t\tnotifySpecsChanged();\n+\t}\n+\n+\t/**\n+\t * Add listener of changes in panel model.\n+\t * <p>\n+\t * Listeners are notified on events caused by both operations invoked by\n+\t * external calls and user interaction. Listener method(s) is always called\n+\t * from UI thread and shouln't perform long computations.\n+\t * <p>\n+\t * Order of adding listeners is significant. This method is not thread-safe.\n+\t * Listeners should be set up before panel usage.\n+\t * \n+\t * @param listener\n+\t *            listener to add.\n+\t */\n+\tpublic void addRefSpecTableListener(final SelectionChangeListener listener) {\n+\t\tlisteners.add(listener);\n+\t}\n+\n+\t/**\n+\t * Get user-friendly error message regarding invalid specification.\n+\t * \n+\t * @return user-readable information about invalid specification.\n+\t */\n+\tpublic String getErrorMessage() {\n+\t\treturn errorMessage;\n+\t}\n+\n+\t/**\n+\t * Return information about validity of specifications.\n+\t * <p>\n+\t * Specifications are considered valid if pushing/fetching (depending on\n+\t * panel configuration) shouldn't cause any error except for\n+\t * non-fast-forward or server-related errors complaint. I.e. specifications\n+\t * destinations don't overlap and every specification is correctly\n+\t * formulated, preferably none is referring to non-existing ref etc.\n+\t * \n+\t * @return true if all specifications in panel are valid, false if at least\n+\t *         one specification is invalid (in this case\n+\t *         {@link #getErrorMessage()} gives detailed information for user).\n+\t */\n+\tpublic boolean isValid() {\n+\t\treturn errorMessage == null;\n+\t}\n+\n+\tprivate int indexOfSpec(final RefSpec spec) {\n+\t\tint i;\n+\t\tfor (i = 0; i < specs.size(); i++) {\n+\t\t\t// we have to compare references, not use List#indexOf,\n+\t\t\t// as equals is implemented in RefSpec\n+\t\t\tif (specs.get(i) == spec)\n+\t\t\t\tbreak;\n+\t\t}\n+\t\tif (i == specs.size())\n+\t\t\treturn -1;\n+\t\treturn i;\n+\t}\n+\n+\tprivate void notifySpecsChanged() {\n+\t\tfor (final SelectionChangeListener listener : listeners)\n+\t\t\tlistener.selectionChanged();\n+\t}\n+\n+\tprivate void safeCreateResources() {\n+\t\timageRegistry.put(IMAGE_ADD, UIIcons.ELCL16_ADD);\n+\t\timageRegistry.put(IMAGE_DELETE, UIIcons.ELCL16_DELETE);\n+\t\timageRegistry.put(IMAGE_TRASH, UIIcons.ELCL16_TRASH);\n+\t\timageRegistry.put(IMAGE_CLEAR, UIIcons.ELCL16_CLEAR);\n+\t\terrorBackgroundColor = new Color(panel.getDisplay(), 255, 150, 150);\n+\t\terrorTextColor = new Color(panel.getDisplay(), 255, 0, 0);\n+\n+\t\tpanel.addDisposeListener(new DisposeListener() {\n+\t\t\tpublic void widgetDisposed(DisposeEvent e) {\n+\t\t\t\timageRegistry.dispose();\n+\t\t\t\terrorBackgroundColor.dispose();\n+\t\t\t\terrorTextColor.dispose();\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate RefContentProposalProvider getRefsProposalProvider(\n+\t\t\tfinal boolean local) {\n+\t\treturn (local ? localProposalProvider : remoteProposalProvider);\n+\t}\n+\n+\tprivate void createCreationPanel() {\n+\t\tfinal Group creationPanel = new Group(panel, SWT.NONE);\n+\t\tcreationPanel.setText(UIText.RefSpecPanel_creationGroup);\n+\t\tcreationPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,\n+\t\t\t\tfalse));\n+\t\tfinal GridLayout layout = new GridLayout();\n+\t\tlayout.numColumns = 3;\n+\t\tlayout.horizontalSpacing = 10;\n+\t\tlayout.verticalSpacing = 2;\n+\t\tcreationPanel.setLayout(layout);\n+\n+\t\tnew Label(creationPanel, SWT.NONE)\n+\t\t\t\t.setText(UIText.RefSpecPanel_creationSrc);\n+\t\tnew Label(creationPanel, SWT.NONE)\n+\t\t\t\t.setText(UIText.RefSpecPanel_creationDst);\n+\t\tcreationButton = new Button(creationPanel, SWT.PUSH);\n+\t\tcreationButton.setLayoutData(new GridData(SWT.RIGHT, SWT.BOTTOM, false,\n+\t\t\t\tfalse, 1, 2));\n+\t\tcreationButton.setImage(imageRegistry.get(IMAGE_ADD));\n+\t\tcreationButton.setText(UIText.RefSpecPanel_creationButton);\n+\t\tcreationButton.addSelectionListener(new SelectionAdapter() {\n+\t\t\t@Override\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tfinal String src = creationSrcCombo.getText();\n+\t\t\t\tfinal String dst = creationDstCombo.getText();\n+\t\t\t\tRefSpec spec = new RefSpec(src + ':' + dst);\n+\t\t\t\taddRefSpec(spec);\n+\t\t\t\tcreationSrcCombo.setText(\"\"); //$NON-NLS-1$\n+\t\t\t\tcreationDstCombo.setText(\"\"); //$NON-NLS-1$\n+\t\t\t}\n+\t\t});\n+\t\tcreationButton.setToolTipText(NLS.bind(\n+\t\t\t\tUIText.RefSpecPanel_creationButtonDescription, typeString()));\n+\n+\t\tcreationSrcDecoration = createAssistedDecoratedCombo(creationPanel,\n+\t\t\t\tgetRefsProposalProvider(pushSpecs),\n+\t\t\t\tnew IContentProposalListener() {\n+\t\t\t\t\tpublic void proposalAccepted(IContentProposal proposal) {\n+\t\t\t\t\t\ttryAutoCompleteSrcToDst();\n+\t\t\t\t\t}\n+\t\t\t\t});\n+\t\tcreationSrcCombo = (Combo) creationSrcDecoration.getControl();\n+\t\tcreationSrcCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,\n+\t\t\t\tfalse));\n+\t\tcreationSrcCombo.addTraverseListener(new TraverseListener() {\n+\t\t\tpublic void keyTraversed(TraverseEvent e) {\n+\t\t\t\t// SWT.TRAVERSE_RETURN may be also reasonable here, but\n+\t\t\t\t// it can be confused with RETURN for content proposal\n+\t\t\t\tif (e.detail == SWT.TRAVERSE_TAB_NEXT)\n+\t\t\t\t\ttryAutoCompleteSrcToDst();\n+\t\t\t}\n+\t\t});\n+\t\tif (pushSpecs)\n+\t\t\tcreationSrcCombo\n+\t\t\t\t\t.setToolTipText(UIText.RefSpecPanel_srcPushDescription);\n+\t\telse\n+\t\t\tcreationSrcCombo\n+\t\t\t\t\t.setToolTipText(UIText.RefSpecPanel_srcFetchDescription);\n+\t\tcreationSrcComboSupport = new ComboLabelingSupport(creationSrcCombo,\n+\t\t\t\tnew SelectionAdapter() {\n+\t\t\t\t\t@Override\n+\t\t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\t\t\ttryAutoCompleteSrcToDst();\n+\t\t\t\t\t}\n+\t\t\t\t});\n+\n+\t\tcreationDstDecoration = createAssistedDecoratedCombo(creationPanel,\n+\t\t\t\tgetRefsProposalProvider(!pushSpecs),\n+\t\t\t\tnew IContentProposalListener() {\n+\t\t\t\t\tpublic void proposalAccepted(IContentProposal proposal) {\n+\t\t\t\t\t\ttryAutoCompleteDstToSrc();\n+\t\t\t\t\t}\n+\t\t\t\t});\n+\t\tcreationDstCombo = (Combo) creationDstDecoration.getControl();\n+\t\tcreationDstCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,\n+\t\t\t\tfalse));\n+\t\tcreationDstCombo.addTraverseListener(new TraverseListener() {\n+\t\t\tpublic void keyTraversed(TraverseEvent e) {\n+\t\t\t\t// SWT.TRAVERSE_RETURN may be also reasonable here, but\n+\t\t\t\t// it can be confused with RETURN for content proposal\n+\t\t\t\tif (e.detail == SWT.TRAVERSE_TAB_NEXT)\n+\t\t\t\t\ttryAutoCompleteDstToSrc();\n+\t\t\t}\n+\t\t});\n+\t\tif (pushSpecs)\n+\t\t\tcreationDstCombo\n+\t\t\t\t\t.setToolTipText(UIText.RefSpecPanel_dstPushDescription);\n+\t\telse\n+\t\t\tcreationDstCombo\n+\t\t\t\t\t.setToolTipText(UIText.RefSpecPanel_dstFetchDescription);\n+\t\tcreationDstComboSupport = new ComboLabelingSupport(creationDstCombo,\n+\t\t\t\tnew SelectionAdapter() {\n+\t\t\t\t\t@Override\n+\t\t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\t\t\ttryAutoCompleteDstToSrc();\n+\t\t\t\t\t}\n+\t\t\t\t});\n+\n+\t\tvalidateCreationPanel();\n+\t\tfinal ModifyListener validator = new ModifyListener() {\n+\t\t\tpublic void modifyText(final ModifyEvent e) {\n+\t\t\t\tvalidateCreationPanel();\n+\t\t\t}\n+\t\t};\n+\t\tcreationSrcCombo.addModifyListener(validator);\n+\t\tcreationDstCombo.addModifyListener(validator);\n+\t}\n+\n+\tprivate void createDeleteCreationPanel() {\n+\t\tfinal Group deletePanel = new Group(panel, SWT.NONE);\n+\t\tdeletePanel.setText(UIText.RefSpecPanel_deletionGroup);\n+\t\tdeletePanel\n+\t\t\t\t.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));\n+\t\tfinal GridLayout layout = new GridLayout();\n+\t\tlayout.numColumns = 3;\n+\t\tlayout.horizontalSpacing = 10;\n+\t\tdeletePanel.setLayout(layout);\n+\n+\t\tfinal Label label = new Label(deletePanel, SWT.NONE);\n+\t\tlabel.setText(UIText.RefSpecPanel_deletionRef);\n+\t\tlabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));\n+\n+\t\tdeleteRefDecoration = createAssistedDecoratedCombo(deletePanel,\n+\t\t\t\tgetRefsProposalProvider(false), null);\n+\t\tdeleteRefCombo = (Combo) deleteRefDecoration.getControl();\n+\t\tdeleteRefCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,\n+\t\t\t\tfalse));\n+\t\tdeleteRefCombo\n+\t\t\t\t.setToolTipText(UIText.RefSpecPanel_dstDeletionDescription);\n+\t\tdeleteRefComboSupport = new ComboLabelingSupport(deleteRefCombo, null);\n+\n+\t\tdeleteButton = new Button(deletePanel, SWT.PUSH);\n+\t\tdeleteButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false,\n+\t\t\t\tfalse));\n+\t\tdeleteButton.setImage(imageRegistry.get(IMAGE_DELETE));\n+\t\tdeleteButton.setText(UIText.RefSpecPanel_deletionButton);\n+\t\tdeleteButton.addSelectionListener(new SelectionAdapter() {\n+\t\t\t@Override\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tRefSpec spec = new RefSpec(':' + deleteRefCombo.getText());\n+\t\t\t\taddRefSpec(spec);\n+\t\t\t\tdeleteRefCombo.setText(\"\"); //$NON-NLS-1$\n+\t\t\t}\n+\t\t});\n+\t\tdeleteButton\n+\t\t\t\t.setToolTipText(UIText.RefSpecPanel_deletionButtonDescription);\n+\t\tvalidateDeleteCreationPanel();\n+\n+\t\tdeleteRefCombo.addModifyListener(new ModifyListener() {\n+\t\t\tpublic void modifyText(final ModifyEvent e) {\n+\t\t\t\tvalidateDeleteCreationPanel();\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate void createPredefinedCreationPanel() {\n+\t\tfinal Group predefinedPanel = new Group(panel, SWT.NONE);\n+\t\tpredefinedPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,\n+\t\t\t\tfalse));\n+\t\tpredefinedPanel.setText(UIText.RefSpecPanel_predefinedGroup);\n+\t\tfinal GridLayout layout = new GridLayout();\n+\t\tlayout.numColumns = 3;\n+\t\tpredefinedPanel.setLayout(layout);\n+\n+\t\taddConfiguredButton = new Button(predefinedPanel, SWT.PUSH);\n+\t\taddConfiguredButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER,\n+\t\t\t\ttrue, false));\n+\t\taddConfiguredButton.setText(NLS.bind(\n+\t\t\t\tUIText.RefSpecPanel_predefinedConfigured, typeString()));\n+\t\taddConfiguredButton.addSelectionListener(new SelectionAdapter() {\n+\t\t\t@Override\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\taddPredefinedRefSpecs(predefinedConfigured);\n+\t\t\t}\n+\t\t});\n+\t\taddConfiguredButton\n+\t\t\t\t.setToolTipText(UIText.RefSpecPanel_predefinedConfiguredDescription);\n+\t\tupdateAddPredefinedButton(addConfiguredButton, predefinedConfigured);\n+\n+\t\taddBranchesButton = new Button(predefinedPanel, SWT.PUSH);\n+\t\taddBranchesButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER,\n+\t\t\t\ttrue, false));\n+\t\taddBranchesButton.setText(UIText.RefSpecPanel_predefinedAll);\n+\t\taddBranchesButton.addSelectionListener(new SelectionAdapter() {\n+\t\t\t@Override\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\taddPredefinedRefSpecs(predefinedBranches);\n+\t\t\t}\n+\t\t});\n+\t\taddBranchesButton\n+\t\t\t\t.setToolTipText(UIText.RefSpecPanel_predefinedAllDescription);\n+\t\tupdateAddPredefinedButton(addBranchesButton, predefinedBranches);\n+\n+\t\taddTagsButton = new Button(predefinedPanel, SWT.PUSH);\n+\t\taddTagsButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,\n+\t\t\t\tfalse));\n+\t\taddTagsButton.setText(UIText.RefSpecPanel_predefinedTags);\n+\t\taddTagsButton.addSelectionListener(new SelectionAdapter() {\n+\t\t\t@Override\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\taddPredefinedRefSpecs(Transport.REFSPEC_TAGS);\n+\t\t\t}\n+\t\t});\n+\t\taddTagsButton\n+\t\t\t\t.setToolTipText(UIText.RefSpecPanel_predefinedTagsDescription);\n+\t\tupdateAddPredefinedButton(addTagsButton, Transport.REFSPEC_TAGS);\n+\n+\t\taddRefSpecTableListener(new SelectionChangeListener() {\n+\t\t\tpublic void selectionChanged() {\n+\t\t\t\tupdateAddPredefinedButton(addConfiguredButton,\n+\t\t\t\t\t\tpredefinedConfigured);\n+\t\t\t\tupdateAddPredefinedButton(addBranchesButton, predefinedBranches);\n+\t\t\t\tupdateAddPredefinedButton(addTagsButton, Transport.REFSPEC_TAGS);\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate ControlDecoration createAssistedDecoratedCombo(\n+\t\t\tfinal Composite parent,\n+\t\t\tfinal IContentProposalProvider proposalProvider,\n+\t\t\tfinal IContentProposalListener listener) {\n+\t\t// FIXME: VERY ANNOYING! reported as 243991 in eclipse bugzilla\n+\t\t// when typing, pressing arrow-down key opens combo box drop-down\n+\t\t// instead of moving within autocompletion list (Mac 10.4&10.5, Eclipse\n+\t\t// 3.4)\n+\t\tfinal Combo combo = new Combo(parent, SWT.DROP_DOWN);\n+\t\tfinal ControlDecoration decoration = new ControlDecoration(combo,\n+\t\t\t\tSWT.BOTTOM | SWT.LEFT);\n+\t\tfinal ContentAssistCommandAdapter proposal = new ContentAssistCommandAdapter(\n+\t\t\t\tcombo, new ComboContentAdapter(), proposalProvider, null, null,\n+\t\t\t\ttrue);\n+\t\tproposal\n+\t\t\t\t.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);\n+\t\tif (listener != null)\n+\t\t\tproposal.addContentProposalListener(listener);\n+\t\treturn decoration;\n+\t}\n+\n+\tprivate void createTableGroup() {\n+\t\tfinal Group tableGroup = new Group(panel, SWT.NONE);\n+\t\ttableGroup.setText(NLS.bind(UIText.RefSpecPanel_specifications,\n+\t\t\t\ttypeString()));\n+\t\ttableGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));\n+\t\ttableGroup.setLayout(new GridLayout());\n+\n+\t\tcreateTable(tableGroup);\n+\t\tcreateSpecsButtonsPanel(tableGroup);\n+\t}\n+\n+\tprivate void createTable(final Group tableGroup) {\n+\t\tfinal Composite tablePanel = new Composite(tableGroup, SWT.NONE);\n+\t\tfinal GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);\n+\t\tlayoutData.heightHint = TABLE_PREFERRED_HEIGHT;\n+\t\tlayoutData.widthHint = TABLE_PREFERRED_WIDTH;\n+\t\ttablePanel.setLayoutData(layoutData);\n+\n+\t\ttableViewer = new TableViewer(tablePanel, SWT.FULL_SELECTION\n+\t\t\t\t| SWT.MULTI | SWT.BORDER | SWT.V_SCROLL);\n+\t\tColumnViewerToolTipSupport.enableFor(tableViewer);\n+\t\tfinal Table table = tableViewer.getTable();\n+\t\ttable.setLinesVisible(true);\n+\t\ttable.setHeaderVisible(true);\n+\n+\t\tcreateTableColumns(tablePanel);\n+\t\tcreateCellEditors(table);\n+\n+\t\ttableViewer.setContentProvider(new IStructuredContentProvider() {\n+\t\t\tpublic Object[] getElements(final Object inputElement) {\n+\t\t\t\treturn ((List) inputElement).toArray();\n+\t\t\t}\n+\n+\t\t\tpublic void dispose() {\n+\t\t\t\t// nothing to dispose\n+\t\t\t}\n+\n+\t\t\tpublic void inputChanged(Viewer viewer, Object oldInput,\n+\t\t\t\t\tObject newInput) {\n+\t\t\t\t// input is hard coded\n+\t\t\t}\n+\t\t});\n+\t\ttableViewer.setInput(specs);\n+\n+\t\ttableViewer.setComparer(new IElementComparer() {\n+\t\t\tpublic boolean equals(Object a, Object b) {\n+\t\t\t\t// need that as viewers are not designed to support 2 equals\n+\t\t\t\t// object, while we have RefSpec#equals implemented\n+\t\t\t\treturn a == b;\n+\t\t\t}\n+\n+\t\t\tpublic int hashCode(Object element) {\n+\t\t\t\treturn element.hashCode();\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate void createTableColumns(final Composite tablePanel) {\n+\t\tfinal TableColumnLayout columnLayout = new TableColumnLayout();\n+\t\ttablePanel.setLayout(columnLayout);\n+\n+\t\tcreateDummyColumn(columnLayout);\n+\t\tcreateModeColumn(columnLayout);\n+\t\tcreateSrcColumn(columnLayout);\n+\t\tcreateDstColumn(columnLayout);\n+\t\tcreateForceColumn(columnLayout);\n+\t\tcreateRemoveColumn(columnLayout);\n+\t}\n+\n+\tprivate void createDummyColumn(final TableColumnLayout columnLayout) {\n+\t\tfinal TableViewerColumn viewerColumn = new TableViewerColumn(\n+\t\t\t\ttableViewer, SWT.LEFT);\n+\t\tfinal TableColumn column = viewerColumn.getColumn();\n+\t\tcolumnLayout.setColumnData(column, new ColumnWeightData(0, 0, false));\n+\t\tviewerColumn.setLabelProvider(new ColumnLabelProvider());\n+\t\t// FIXME: first cell is left aligned on Mac OS X 10.4, Eclipse 3.4\n+\t}\n+\n+\tprivate void createModeColumn(final TableColumnLayout columnLayout) {\n+\t\tfinal TableViewerColumn column = createColumn(columnLayout,\n+\t\t\t\tUIText.RefSpecPanel_columnMode, COLUMN_MODE_WEIGHT, SWT.CENTER);\n+\t\tcolumn.setLabelProvider(new ColumnLabelProvider() {\n+\t\t\t@Override\n+\t\t\tpublic String getText(final Object element) {\n+\t\t\t\treturn (isDeleteRefSpec(element) ? UIText.RefSpecPanel_modeDelete\n+\t\t\t\t\t\t: UIText.RefSpecPanel_modeUpdate);\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic Image getImage(Object element) {\n+\t\t\t\treturn (isDeleteRefSpec(element) ? imageRegistry\n+\t\t\t\t\t\t.get(IMAGE_DELETE) : imageRegistry.get(IMAGE_ADD));\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic String getToolTipText(Object element) {\n+\t\t\t\tif (isDeleteRefSpec(element))\n+\t\t\t\t\treturn UIText.RefSpecPanel_modeDeleteDescription + '\\n'\n+\t\t\t\t\t\t\t+ UIText.RefSpecPanel_clickToChange;\n+\t\t\t\treturn UIText.RefSpecPanel_modeUpdateDescription + '\\n'\n+\t\t\t\t\t\t+ UIText.RefSpecPanel_clickToChange;\n+\t\t\t}\n+\t\t});\n+\t\tcolumn.setEditingSupport(new EditingSupport(tableViewer) {\n+\t\t\t@Override\n+\t\t\tprotected boolean canEdit(final Object element) {\n+\t\t\t\treturn true;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected CellEditor getCellEditor(final Object element) {\n+\t\t\t\treturn modeCellEditor;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected Object getValue(final Object element) {\n+\t\t\t\treturn isDeleteRefSpec(element);\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected void setValue(final Object element, final Object value) {\n+\t\t\t\tfinal RefSpec oldSpec = (RefSpec) element;\n+\t\t\t\tfinal RefSpec newSpec;\n+\t\t\t\tif ((Boolean) value) {\n+\t\t\t\t\tnewSpec = setRefSpecSource(oldSpec, null);\n+\t\t\t\t\tsetRefSpec(oldSpec, newSpec);\n+\t\t\t\t} else {\n+\t\t\t\t\tnewSpec = setRefSpecSource(oldSpec,\n+\t\t\t\t\t\t\tUIText.RefSpecPanel_refChooseSome);\n+\t\t\t\t\tsetRefSpec(oldSpec, newSpec);\n+\t\t\t\t\ttableViewer.getControl().getDisplay().asyncExec(\n+\t\t\t\t\t\t\tnew Runnable() {\n+\t\t\t\t\t\t\t\tpublic void run() {\n+\t\t\t\t\t\t\t\t\ttableViewer.editElement(newSpec,\n+\t\t\t\t\t\t\t\t\t\t\tsrcColumnIndex);\n+\t\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\t});\n+\t\t\t\t}\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate void createSrcColumn(final TableColumnLayout columnLayout) {\n+\t\tfinal TableViewerColumn column = createColumn(columnLayout,\n+\t\t\t\tUIText.RefSpecPanel_columnSrc, COLUMN_SRC_WEIGHT, SWT.LEFT);\n+\t\tcolumn.setLabelProvider(new ColumnLabelProvider() {\n+\t\t\t@Override\n+\t\t\tpublic String getText(final Object element) {\n+\t\t\t\treturn ((RefSpec) element).getSource();\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic String getToolTipText(Object element) {\n+\t\t\t\tif (isInvalidSpec(element))\n+\t\t\t\t\treturn errorMessage;\n+\t\t\t\tif (isDeleteRefSpec(element))\n+\t\t\t\t\treturn UIText.RefSpecPanel_srcDeleteDescription;\n+\t\t\t\tif (pushSpecs)\n+\t\t\t\t\treturn UIText.RefSpecPanel_srcPushDescription;\n+\t\t\t\treturn UIText.RefSpecPanel_srcFetchDescription;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic Color getBackground(final Object element) {\n+\t\t\t\tif (isInvalidSpec(element))\n+\t\t\t\t\treturn errorBackgroundColor;\n+\t\t\t\treturn null;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic Color getToolTipForegroundColor(Object element) {\n+\t\t\t\tif (isInvalidSpec(element))\n+\t\t\t\t\treturn errorTextColor;\n+\t\t\t\treturn null;\n+\t\t\t}\n+\t\t});\n+\t\tcolumn.setEditingSupport(new EditingSupport(tableViewer) {\n+\t\t\t@Override\n+\t\t\tprotected boolean canEdit(final Object element) {\n+\t\t\t\treturn !isDeleteRefSpec(element);\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected CellEditor getCellEditor(final Object element) {\n+\t\t\t\treturn (pushSpecs ? localRefCellEditor : remoteRefCellEditor);\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected Object getValue(final Object element) {\n+\t\t\t\treturn ((RefSpec) element).getSource();\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected void setValue(final Object element, final Object value) {\n+\t\t\t\tif (value == null || ((String) value).length() == 0\n+\t\t\t\t\t\t|| ObjectId.zeroId().toString().equals(value)) {\n+\t\t\t\t\t// Ignore empty strings or null objects - do not set them in\n+\t\t\t\t\t// model.User won't loose any information if we just fall\n+\t\t\t\t\t// back to the old value.\n+\t\t\t\t\t// If user want to delete ref, let change the mode.\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\n+\t\t\t\tfinal RefSpec oldSpec = (RefSpec) element;\n+\t\t\t\tfinal RefSpec newSpec = setRefSpecSource(oldSpec,\n+\t\t\t\t\t\t(String) value);\n+\t\t\t\tsetRefSpec(oldSpec, newSpec);\n+\t\t\t}\n+\t\t});\n+\n+\t\t// find index of this column - for later usage\n+\t\tfinal TableColumn[] columns = tableViewer.getTable().getColumns();\n+\t\tfor (srcColumnIndex = 0; srcColumnIndex < columns.length; srcColumnIndex++)\n+\t\t\tif (columns[srcColumnIndex] == column.getColumn())\n+\t\t\t\tbreak;\n+\n+\t}\n+\n+\tprivate void createDstColumn(final TableColumnLayout columnLayout) {\n+\t\tfinal TableViewerColumn column = createColumn(columnLayout,\n+\t\t\t\tUIText.RefSpecPanel_columnDst, COLUMN_DST_WEIGHT, SWT.LEFT);\n+\t\tcolumn.setLabelProvider(new ColumnLabelProvider() {\n+\t\t\t@Override\n+\t\t\tpublic String getText(final Object element) {\n+\t\t\t\treturn ((RefSpec) element).getDestination();\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic String getToolTipText(Object element) {\n+\t\t\t\tif (isInvalidSpec(element))\n+\t\t\t\t\treturn errorMessage;\n+\t\t\t\tif (isDeleteRefSpec(element))\n+\t\t\t\t\treturn UIText.RefSpecPanel_dstDeletionDescription;\n+\t\t\t\tif (pushSpecs)\n+\t\t\t\t\treturn UIText.RefSpecPanel_dstPushDescription;\n+\t\t\t\treturn UIText.RefSpecPanel_dstFetchDescription;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic Color getBackground(final Object element) {\n+\t\t\t\tif (isInvalidSpec(element))\n+\t\t\t\t\treturn errorBackgroundColor;\n+\t\t\t\treturn null;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic Color getToolTipForegroundColor(Object element) {\n+\t\t\t\tif (isInvalidSpec(element))\n+\t\t\t\t\treturn errorTextColor;\n+\t\t\t\treturn null;\n+\t\t\t}\n+\t\t});\n+\t\tcolumn.setEditingSupport(new EditingSupport(tableViewer) {\n+\t\t\t@Override\n+\t\t\tprotected boolean canEdit(final Object element) {\n+\t\t\t\treturn true;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected CellEditor getCellEditor(final Object element) {\n+\t\t\t\treturn (pushSpecs ? remoteRefCellEditor : localRefCellEditor);\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected Object getValue(final Object element) {\n+\t\t\t\treturn ((RefSpec) element).getDestination();\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected void setValue(final Object element, final Object value) {\n+\t\t\t\tif (value == null || ((String) value).length() == 0) {\n+\t\t\t\t\t// Ignore empty strings - do not set them in model.\n+\t\t\t\t\t// User won't loose any information if we just fall back\n+\t\t\t\t\t// to the old value.\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\n+\t\t\t\tfinal RefSpec oldSpec = (RefSpec) element;\n+\t\t\t\tfinal RefSpec newSpec = setRefSpecDestination(oldSpec,\n+\t\t\t\t\t\t(String) value);\n+\t\t\t\tsetRefSpec(oldSpec, newSpec);\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate void createForceColumn(final TableColumnLayout columnLayout) {\n+\t\tfinal TableViewerColumn column = createColumn(columnLayout,\n+\t\t\t\tUIText.RefSpecPanel_columnForce, COLUMN_FORCE_WEIGHT,\n+\t\t\t\tSWT.CENTER);\n+\t\tcolumn.setLabelProvider(new CheckboxLabelProvider(tableViewer\n+\t\t\t\t.getControl()) {\n+\t\t\t@Override\n+\t\t\tprotected boolean isChecked(final Object element) {\n+\t\t\t\treturn ((RefSpec) element).isForceUpdate();\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected boolean isEnabled(Object element) {\n+\t\t\t\treturn !isDeleteRefSpec(element);\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic String getToolTipText(Object element) {\n+\t\t\t\tif (!isEnabled(element))\n+\t\t\t\t\treturn UIText.RefSpecPanel_forceDeleteDescription;\n+\t\t\t\tif (isChecked(element))\n+\t\t\t\t\treturn UIText.RefSpecPanel_forceTrueDescription + '\\n'\n+\t\t\t\t\t\t\t+ UIText.RefSpecPanel_clickToChange;\n+\t\t\t\treturn UIText.RefSpecPanel_forceFalseDescription + '\\n'\n+\t\t\t\t\t\t+ UIText.RefSpecPanel_clickToChange;\n+\t\t\t}\n+\t\t});\n+\t\tcolumn.setEditingSupport(new EditingSupport(tableViewer) {\n+\t\t\t@Override\n+\t\t\tprotected boolean canEdit(final Object element) {\n+\t\t\t\treturn !isDeleteRefSpec(element);\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected CellEditor getCellEditor(final Object element) {\n+\t\t\t\treturn forceUpdateCellEditor;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected Object getValue(final Object element) {\n+\t\t\t\treturn ((RefSpec) element).isForceUpdate();\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected void setValue(final Object element, final Object value) {\n+\t\t\t\tfinal RefSpec oldSpec = (RefSpec) element;\n+\t\t\t\tfinal RefSpec newSpec = oldSpec.setForceUpdate((Boolean) value);\n+\t\t\t\tsetRefSpec(oldSpec, newSpec);\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate void createRemoveColumn(TableColumnLayout columnLayout) {\n+\t\tfinal TableViewerColumn column = createColumn(columnLayout,\n+\t\t\t\tUIText.RefSpecPanel_columnRemove, COLUMN_REMOVE_WEIGHT,\n+\t\t\t\tSWT.CENTER);\n+\t\tcolumn.setLabelProvider(new CenteredImageLabelProvider() {\n+\t\t\t@Override\n+\t\t\tpublic Image getImage(Object element) {\n+\t\t\t\treturn imageRegistry.get(IMAGE_TRASH);\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic String getToolTipText(Object element) {\n+\t\t\t\treturn NLS.bind(UIText.RefSpecPanel_removeDescription,\n+\t\t\t\t\t\ttypeString());\n+\t\t\t}\n+\t\t});\n+\t\tcolumn.setEditingSupport(new EditingSupport(tableViewer) {\n+\t\t\t@Override\n+\t\t\tprotected boolean canEdit(Object element) {\n+\t\t\t\treturn true;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected CellEditor getCellEditor(Object element) {\n+\t\t\t\treturn removeSpecCellEditor;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected Object getValue(Object element) {\n+\t\t\t\treturn null;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tprotected void setValue(Object element, Object value) {\n+\t\t\t\tremoveRefSpec((RefSpec) element);\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate TableViewerColumn createColumn(\n+\t\t\tfinal TableColumnLayout columnLayout, final String text,\n+\t\t\tfinal int weight, final int style) {\n+\t\tfinal TableViewerColumn viewerColumn = new TableViewerColumn(\n+\t\t\t\ttableViewer, style);\n+\t\tfinal TableColumn column = viewerColumn.getColumn();\n+\t\tcolumn.setText(text);\n+\t\tcolumnLayout.setColumnData(column, new ColumnWeightData(weight));\n+\t\treturn viewerColumn;\n+\t}\n+\n+\tprivate void createCellEditors(final Table table) {\n+\t\tmodeCellEditor = new CheckboxCellEditor(table);\n+\t\tlocalRefCellEditor = createLocalRefCellEditor(table);\n+\t\tremoteRefCellEditor = createRemoteRefCellEditor(table);\n+\t\tforceUpdateCellEditor = new CheckboxCellEditor(table);\n+\t\tremoveSpecCellEditor = new ClickableCellEditor(table);\n+\t}\n+\n+\tprivate CellEditor createLocalRefCellEditor(final Table table) {\n+\t\treturn createRefCellEditor(table, getRefsProposalProvider(true));\n+\t}\n+\n+\tprivate CellEditor createRemoteRefCellEditor(final Table table) {\n+\t\treturn createRefCellEditor(table, getRefsProposalProvider(false));\n+\t}\n+\n+\tprivate CellEditor createRefCellEditor(final Table table,\n+\t\t\tfinal IContentProposalProvider proposalProvider) {\n+\t\tfinal CellEditor cellEditor = new TextCellEditor(table);\n+\n+\t\tfinal Text text = (Text) cellEditor.getControl();\n+\t\tfinal ContentAssistCommandAdapter assist = new ContentAssistCommandAdapter(\n+\t\t\t\ttext, new TextContentAdapter(), proposalProvider, null, null,\n+\t\t\t\ttrue);\n+\t\tassist\n+\t\t\t\t.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);\n+\n+\t\treturn cellEditor;\n+\t}\n+\n+\tprivate void createSpecsButtonsPanel(final Composite parent) {\n+\t\tfinal Composite specsPanel = new Composite(parent, SWT.NONE);\n+\t\tspecsPanel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true,\n+\t\t\t\tfalse));\n+\t\tfinal RowLayout layout = new RowLayout();\n+\t\tlayout.spacing = 10;\n+\t\tspecsPanel.setLayout(layout);\n+\n+\t\tforceUpdateAllButton = new Button(specsPanel, SWT.PUSH);\n+\t\tforceUpdateAllButton.setText(UIText.RefSpecPanel_forceAll);\n+\t\tforceUpdateAllButton.addSelectionListener(new SelectionAdapter() {\n+\t\t\t@Override\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tfinal List<RefSpec> specsCopy = new ArrayList<RefSpec>(specs);\n+\t\t\t\tfor (final RefSpec spec : specsCopy) {\n+\t\t\t\t\tif (!isDeleteRefSpec(spec))\n+\t\t\t\t\t\tsetRefSpec(spec, spec.setForceUpdate(true));\n+\t\t\t\t}\n+\t\t\t}\n+\t\t});\n+\t\tforceUpdateAllButton\n+\t\t\t\t.setToolTipText(UIText.RefSpecPanel_forceAllDescription);\n+\t\tupdateForceUpdateAllButton();\n+\n+\t\tremoveAllSpecButton = new Button(specsPanel, SWT.PUSH);\n+\t\tremoveAllSpecButton.setImage(imageRegistry.get(IMAGE_CLEAR));\n+\t\tremoveAllSpecButton.setText(UIText.RefSpecPanel_removeAll);\n+\t\tremoveAllSpecButton.addSelectionListener(new SelectionAdapter() {\n+\t\t\t@Override\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tclearRefSpecs();\n+\t\t\t}\n+\t\t});\n+\t\tremoveAllSpecButton\n+\t\t\t\t.setToolTipText(UIText.RefSpecPanel_removeAllDescription);\n+\t\tupdateRemoveAllSpecButton();\n+\n+\t\taddRefSpecTableListener(new SelectionChangeListener() {\n+\t\t\tpublic void selectionChanged() {\n+\t\t\t\tupdateForceUpdateAllButton();\n+\t\t\t\tupdateRemoveAllSpecButton();\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate void tryAutoCompleteSrcToDst() {\n+\t\tfinal String src = creationSrcCombo.getText();\n+\t\tfinal String dst = creationDstCombo.getText();\n+\n+\t\tif (src == null || src.length() == 0)\n+\t\t\treturn;\n+\n+\t\tif (dst != null && dst.length() > 0) {\n+\t\t\t// dst is already there, just fix wildcards if needed\n+\t\t\tfinal String newDst;\n+\t\t\tif (RefSpec.isWildcard(src))\n+\t\t\t\tnewDst = wildcardSpecComponent(dst);\n+\t\t\telse\n+\t\t\t\tnewDst = unwildcardSpecComponent(dst, src);\n+\t\t\tcreationDstCombo.setText(newDst);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tif (!isValidRefExpression(src)) {\n+\t\t\t// no way to be smarter than user here\n+\t\t\treturn;\n+\t\t}\n+\n+\t\t// dst is empty, src is ref or wildcard, so we can rewrite it as user\n+\t\t// would perhaps\n+\t\tif (pushSpecs)\n+\t\t\tcreationDstCombo.setText(src);\n+\t\telse {\n+\t\t\tfor (final RefSpec spec : predefinedConfigured) {\n+\t\t\t\tif (spec.matchSource(src)) {\n+\t\t\t\t\tfinal String newDst = spec.expandFromSource(src)\n+\t\t\t\t\t\t\t.getDestination();\n+\t\t\t\t\tcreationDstCombo.setText(newDst);\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (remoteName != null && src.startsWith(HEADS_PREFIX_S)) {\n+\t\t\t\tfinal String newDst = REMOTES_PREFIX_S + remoteName + '/'\n+\t\t\t\t\t\t+ src.substring(HEADS_PREFIX_S.length());\n+\t\t\t\tcreationDstCombo.setText(newDst);\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tprivate void tryAutoCompleteDstToSrc() {\n+\t\tfinal String src = creationSrcCombo.getText();\n+\t\tfinal String dst = creationDstCombo.getText();\n+\n+\t\tif (dst == null || dst.length() == 0)\n+\t\t\treturn;\n+\n+\t\tif (src != null && src.length() > 0) {\n+\t\t\t// src is already there, fix wildcards if needed\n+\t\t\tfinal String newSrc;\n+\t\t\tif (RefSpec.isWildcard(dst))\n+\t\t\t\tnewSrc = wildcardSpecComponent(src);\n+\t\t\telse\n+\t\t\t\tnewSrc = unwildcardSpecComponent(src, dst);\n+\t\t\tcreationSrcCombo.setText(newSrc);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\tprivate void validateCreationPanel() {\n+\t\tfinal String src = creationSrcCombo.getText();\n+\t\tfinal String dst = creationDstCombo.getText();\n+\n+\t\t// check src ref field\n+\t\tboolean srcOk = false;\n+\t\tfinal boolean srcWildcard = RefSpec.isWildcard(src);\n+\t\tif (src == null || src.length() == 0)\n+\t\t\tsetControlDecoration(creationSrcDecoration,\n+\t\t\t\t\tFieldDecorationRegistry.DEC_REQUIRED,\n+\t\t\t\t\tUIText.RefSpecPanel_validationSrcUpdateRequired);\n+\t\telse if (pushSpecs) {\n+\t\t\tif (!srcWildcard && !isLocalRef(src))\n+\t\t\t\tsetControlDecoration(creationSrcDecoration,\n+\t\t\t\t\t\tFieldDecorationRegistry.DEC_ERROR, NLS.bind(\n+\t\t\t\t\t\t\t\tUIText.RefSpecPanel_validationRefInvalidLocal,\n+\t\t\t\t\t\t\t\tsrc));\n+\t\t\telse if (srcWildcard && !isValidRefExpression(src))\n+\t\t\t\tsetControlDecoration(\n+\t\t\t\t\t\tcreationSrcDecoration,\n+\t\t\t\t\t\tFieldDecorationRegistry.DEC_ERROR,\n+\t\t\t\t\t\tNLS\n+\t\t\t\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\t\t\t\tUIText.RefSpecPanel_validationRefInvalidExpression,\n+\t\t\t\t\t\t\t\t\t\tsrc));\n+\t\t\telse {\n+\t\t\t\tsrcOk = true;\n+\t\t\t\tif (srcWildcard && !isMatchingAny(src, localRefNames))\n+\t\t\t\t\tsetControlDecoration(\n+\t\t\t\t\t\t\tcreationSrcDecoration,\n+\t\t\t\t\t\t\tFieldDecorationRegistry.DEC_WARNING,\n+\t\t\t\t\t\t\tNLS\n+\t\t\t\t\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\t\t\t\t\tUIText.RefSpecPanel_validationRefNonMatchingLocal,\n+\t\t\t\t\t\t\t\t\t\t\tsrc));\n+\t\t\t\telse\n+\t\t\t\t\tcreationSrcDecoration.hide();\n+\t\t\t}\n+\t\t} else {\n+\t\t\tif (!srcWildcard && !isRemoteRef(src))\n+\t\t\t\tsetControlDecoration(\n+\t\t\t\t\t\tcreationSrcDecoration,\n+\t\t\t\t\t\tFieldDecorationRegistry.DEC_ERROR,\n+\t\t\t\t\t\tNLS\n+\t\t\t\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\t\t\t\tUIText.RefSpecPanel_validationRefNonExistingRemote,\n+\t\t\t\t\t\t\t\t\t\tsrc));\n+\t\t\telse if (srcWildcard && !isMatchingAny(src, remoteRefNames)) {\n+\t\t\t\tsetControlDecoration(\n+\t\t\t\t\t\tcreationSrcDecoration,\n+\t\t\t\t\t\tFieldDecorationRegistry.DEC_WARNING,\n+\t\t\t\t\t\tNLS\n+\t\t\t\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\t\t\t\tUIText.RefSpecPanel_validationRefNonMatchingRemote,\n+\t\t\t\t\t\t\t\t\t\tsrc));\n+\t\t\t\tsrcOk = true;\n+\t\t\t} else {\n+\t\t\t\tsrcOk = true;\n+\t\t\t\tcreationSrcDecoration.hide();\n+\t\t\t}\n+\t\t}\n+\n+\t\t// check dst ref field\n+\t\tboolean dstOk = false;\n+\t\tif (dst == null || dst.length() == 0)\n+\t\t\tsetControlDecoration(creationDstDecoration,\n+\t\t\t\t\tFieldDecorationRegistry.DEC_REQUIRED,\n+\t\t\t\t\tUIText.RefSpecPanel_validationDstRequired);\n+\t\telse if (!isValidRefExpression(dst))\n+\t\t\tsetControlDecoration(creationDstDecoration,\n+\t\t\t\t\tFieldDecorationRegistry.DEC_ERROR, NLS.bind(\n+\t\t\t\t\t\t\tUIText.RefSpecPanel_validationDstInvalidExpression,\n+\t\t\t\t\t\t\tdst));\n+\t\telse {\n+\t\t\tcreationDstDecoration.hide();\n+\t\t\tdstOk = true;\n+\t\t}\n+\t\t// leave duplicates dst checking for validateSpecs()\n+\n+\t\t// check the wildcard synergy\n+\t\tboolean wildcardOk = true;\n+\t\tif (srcOk && dstOk && (srcWildcard ^ RefSpec.isWildcard(dst))) {\n+\t\t\tsetControlDecoration(creationSrcDecoration,\n+\t\t\t\t\tFieldDecorationRegistry.DEC_ERROR,\n+\t\t\t\t\tUIText.RefSpecPanel_validationWildcardInconsistent);\n+\t\t\tsetControlDecoration(creationDstDecoration,\n+\t\t\t\t\tFieldDecorationRegistry.DEC_ERROR,\n+\t\t\t\t\tUIText.RefSpecPanel_validationWildcardInconsistent);\n+\t\t\twildcardOk = false;\n+\t\t}\n+\n+\t\tcreationButton.setEnabled(srcOk && dstOk && wildcardOk);\n+\t}\n+\n+\tprivate void validateDeleteCreationPanel() {\n+\t\tfinal String ref = deleteRefCombo.getText();\n+\n+\t\tdeleteButton.setEnabled(false);\n+\t\tif (ref == null || ref.length() == 0)\n+\t\t\tsetControlDecoration(deleteRefDecoration,\n+\t\t\t\t\tFieldDecorationRegistry.DEC_REQUIRED,\n+\t\t\t\t\tUIText.RefSpecPanel_validationRefDeleteRequired);\n+\t\telse if (!isValidRefExpression(ref))\n+\t\t\tsetControlDecoration(deleteRefDecoration,\n+\t\t\t\t\tFieldDecorationRegistry.DEC_ERROR, NLS.bind(\n+\t\t\t\t\t\t\tUIText.RefSpecPanel_validationRefInvalidExpression,\n+\t\t\t\t\t\t\tref));\n+\t\telse if (RefSpec.isWildcard(ref))\n+\t\t\tsetControlDecoration(deleteRefDecoration,\n+\t\t\t\t\tFieldDecorationRegistry.DEC_ERROR,\n+\t\t\t\t\tUIText.RefSpecPanel_validationRefDeleteWildcard);\n+\t\telse if (!isRemoteRef(ref))\n+\t\t\tsetControlDecoration(\n+\t\t\t\t\tdeleteRefDecoration,\n+\t\t\t\t\tFieldDecorationRegistry.DEC_ERROR,\n+\t\t\t\t\tNLS\n+\t\t\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\t\t\tUIText.RefSpecPanel_validationRefNonExistingRemoteDelete,\n+\t\t\t\t\t\t\t\t\tref));\n+\t\telse {\n+\t\t\tdeleteRefDecoration.hide();\n+\t\t\tdeleteButton.setEnabled(true);\n+\t\t}\n+\t}\n+\n+\tprivate void validateSpecs() {\n+\t\t// validate spec; display max. 1 error message for user at time\n+\t\tfinal RefSpec oldInvalidSpec = invalidSpec;\n+\t\tfinal RefSpec oldInvalidSpecSameDst = invalidSpecSameDst;\n+\t\terrorMessage = null;\n+\t\tinvalidSpec = null;\n+\t\tinvalidSpecSameDst = null;\n+\t\tfor (final RefSpec spec : specs) {\n+\t\t\terrorMessage = validateSpec(spec);\n+\t\t\tif (errorMessage != null) {\n+\t\t\t\tinvalidSpec = spec;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (errorMessage == null)\n+\t\t\tvalidateSpecsCrossDst();\n+\t\tif (invalidSpec != oldInvalidSpec\n+\t\t\t\t|| invalidSpecSameDst != oldInvalidSpecSameDst)\n+\t\t\ttableViewer.refresh();\n+\t}\n+\n+\tprivate String validateSpec(final RefSpec spec) {\n+\t\tfinal String src = spec.getSource();\n+\t\tfinal String dst = spec.getDestination();\n+\t\tfinal boolean wildcard = spec.isWildcard();\n+\n+\t\t// check src\n+\t\tif (pushSpecs) {\n+\t\t\tif (!isDeleteRefSpec(spec)) {\n+\t\t\t\tif (src.length() == 0)\n+\t\t\t\t\treturn UIText.RefSpecPanel_validationSrcUpdateRequired;\n+\t\t\t\telse if (!wildcard && !isLocalRef(src))\n+\t\t\t\t\treturn NLS.bind(\n+\t\t\t\t\t\t\tUIText.RefSpecPanel_validationRefInvalidLocal, src);\n+\t\t\t\telse if (wildcard && !isValidRefExpression(src))\n+\t\t\t\t\treturn NLS.bind(\n+\t\t\t\t\t\t\tUIText.RefSpecPanel_validationRefInvalidExpression,\n+\t\t\t\t\t\t\tsrc);\n+\t\t\t\t// ignore non-matching wildcard specs\n+\t\t\t}\n+\t\t} else {\n+\t\t\tif (src == null || src.length() == 0)\n+\t\t\t\treturn UIText.RefSpecPanel_validationSrcUpdateRequired;\n+\t\t\telse if (!wildcard && !isRemoteRef(src))\n+\t\t\t\treturn NLS\n+\t\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\t\tUIText.RefSpecPanel_validationRefNonExistingRemote,\n+\t\t\t\t\t\t\t\tsrc);\n+\t\t\t// ignore non-matching wildcard specs\n+\t\t}\n+\n+\t\t// check dst\n+\t\tif (dst == null || dst.length() == 0) {\n+\t\t\tif (isDeleteRefSpec(spec))\n+\t\t\t\treturn UIText.RefSpecPanel_validationRefDeleteRequired;\n+\t\t\treturn UIText.RefSpecPanel_validationDstRequired;\n+\t\t} else if (!isValidRefExpression(dst))\n+\t\t\treturn NLS.bind(UIText.RefSpecPanel_validationRefInvalidExpression,\n+\t\t\t\t\tdst);\n+\t\telse if (isDeleteRefSpec(spec) && !isRemoteRef(dst))\n+\t\t\treturn NLS.bind(\n+\t\t\t\t\tUIText.RefSpecPanel_validationRefNonExistingRemoteDelete,\n+\t\t\t\t\tdst);\n+\n+\t\treturn null;\n+\t}\n+\n+\tprivate boolean isInvalidSpec(Object element) {\n+\t\treturn element == invalidSpec || element == invalidSpecSameDst;\n+\t}\n+\n+\tprivate void validateSpecsCrossDst() {\n+\t\tfinal Map<String, RefSpec> dstsSpecsMap = new HashMap<String, RefSpec>();\n+\t\ttry {\n+\t\t\tfor (final RefSpec spec : specs) {\n+\t\t\t\tif (!spec.isWildcard()) {\n+\t\t\t\t\tif (!tryAddDestination(dstsSpecsMap, spec.getDestination(),\n+\t\t\t\t\t\t\tspec))\n+\t\t\t\t\t\treturn;\n+\t\t\t\t} else {\n+\t\t\t\t\tfinal Collection<String> srcNames;\n+\t\t\t\t\tif (pushSpecs)\n+\t\t\t\t\t\tsrcNames = localRefNames;\n+\t\t\t\t\telse\n+\t\t\t\t\t\tsrcNames = remoteRefNames;\n+\n+\t\t\t\t\tfor (final String src : srcNames) {\n+\t\t\t\t\t\tif (spec.matchSource(src)) {\n+\t\t\t\t\t\t\tfinal String dst = spec.expandFromSource(src)\n+\t\t\t\t\t\t\t\t\t.getDestination();\n+\t\t\t\t\t\t\tif (!tryAddDestination(dstsSpecsMap, dst, spec))\n+\t\t\t\t\t\t\t\treturn;\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tmatchingAnyRefs = !dstsSpecsMap.isEmpty();\n+\t\t}\n+\t}\n+\n+\tprivate boolean tryAddDestination(final Map<String, RefSpec> dstsSpecsMap,\n+\t\t\tfinal String dst, final RefSpec spec) {\n+\t\tfinal RefSpec other = dstsSpecsMap.put(dst, spec);\n+\t\tif (other != null) {\n+\t\t\terrorMessage = NLS\n+\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\tUIText.RefSpecPanel_validationSpecificationsOverlappingDestination,\n+\t\t\t\t\t\t\tdst);\n+\t\t\tinvalidSpec = other;\n+\t\t\tinvalidSpecSameDst = spec;\n+\t\t\treturn false;\n+\t\t}\n+\t\treturn true;\n+\t}\n+\n+\tprivate void updateAddPredefinedButton(final Button button,\n+\t\t\tfinal List<RefSpec> predefined) {\n+\t\tboolean enable = false;\n+\t\tfor (final RefSpec pre : predefined) {\n+\t\t\tif (!specs.contains(pre)) {\n+\t\t\t\tenable = true;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tbutton.setEnabled(enable);\n+\t}\n+\n+\tprivate void updateAddPredefinedButton(Button button,\n+\t\t\tfinal RefSpec predefined) {\n+\t\tbutton.setEnabled(!specs.contains(predefined));\n+\t}\n+\n+\tprivate void updateForceUpdateAllButton() {\n+\t\tboolean enable = false;\n+\t\tfor (final RefSpec spec : specs) {\n+\t\t\tif (!isDeleteRefSpec(spec) && !spec.isForceUpdate()) {\n+\t\t\t\tenable = true;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tforceUpdateAllButton.setEnabled(enable);\n+\t}\n+\n+\tprivate void updateRemoveAllSpecButton() {\n+\t\tremoveAllSpecButton.setEnabled(!specs.isEmpty());\n+\t}\n+\n+\tprivate String typeString() {\n+\t\treturn (pushSpecs ? UIText.RefSpecPanel_push\n+\t\t\t\t: UIText.RefSpecPanel_fetch);\n+\t}\n+\n+\tprivate void addPredefinedRefSpecs(final RefSpec predefined) {\n+\t\taddPredefinedRefSpecs(Collections.singletonList(predefined));\n+\t}\n+\n+\tprivate void addPredefinedRefSpecs(final List<RefSpec> predefined) {\n+\t\tfor (final RefSpec pre : predefined) {\n+\t\t\tif (!specs.contains(pre))\n+\t\t\t\taddRefSpec(pre);\n+\t\t}\n+\t}\n+\n+\tprivate List<RefContentProposal> createContentProposals(\n+\t\t\tfinal Collection<Ref> refs, final Ref HEAD) {\n+\t\tfinal TreeSet<Ref> set = new TreeSet<Ref>(new Comparator<Ref>() {\n+\t\t\tpublic int compare(Ref o1, Ref o2) {\n+\t\t\t\t// lexicographical ordering by name seems to be fine\n+\t\t\t\treturn o1.getName().compareTo(o2.getName());\n+\t\t\t}\n+\t\t});\n+\t\tset.addAll(refs);\n+\t\tif (HEAD != null)\n+\t\t\tset.add(HEAD);\n+\n+\t\tfinal List<RefContentProposal> result = new ArrayList<RefContentProposal>(\n+\t\t\t\tset.size());\n+\t\tfor (final Ref r : set)\n+\t\t\tresult.add(new RefContentProposal(localDb, r));\n+\t\treturn result;\n+\t}\n+\n+\tprivate List<RefContentProposal> createProposalsFilteredLocal(\n+\t\t\tfinal List<RefContentProposal> proposals) {\n+\t\tfinal List<RefContentProposal> result = new ArrayList<RefContentProposal>();\n+\t\tfor (final RefContentProposal p : proposals) {\n+\t\t\tfinal String content = p.getContent();\n+\t\t\tif (pushSpecs) {\n+\t\t\t\tif (content.equals(Constants.HEAD)\n+\t\t\t\t\t\t|| content.startsWith(HEADS_PREFIX_S))\n+\t\t\t\t\tresult.add(p);\n+\t\t\t} else {\n+\t\t\t\tif (content.startsWith(REMOTES_PREFIX_S))\n+\t\t\t\t\tresult.add(p);\n+\t\t\t}\n+\t\t}\n+\t\treturn result;\n+\t}\n+\n+\tprivate boolean isRemoteRef(String ref) {\n+\t\treturn remoteRefNames.contains(ref);\n+\t}\n+\n+\tprivate boolean isLocalRef(final String ref) {\n+\t\treturn tryResolveLocalRef(ref) != null;\n+\t}\n+\n+\tprivate boolean isMatchingAny(final String ref,\n+\t\t\tfinal Collection<String> names) {\n+\t\t// strip wildcard sign\n+\t\tfinal String prefix = ref.substring(0, ref.length() - 1);\n+\t\tfor (final String name : names)\n+\t\t\tif (name.startsWith(prefix))\n+\t\t\t\treturn true;\n+\t\treturn false;\n+\t}\n+\n+\tprivate ObjectId tryResolveLocalRef(final String ref) {\n+\t\ttry {\n+\t\t\treturn localDb.resolve(ref);\n+\t\t} catch (final IOException e) {\n+\t\t\tActivator.logError(\n+\t\t\t\t\t\"I/O error occurred during resolving expression: \" //$NON-NLS-1$\n+\t\t\t\t\t\t\t+ ref, e);\n+\t\t\treturn null;\n+\t\t}\n+\t}\n+\n+\tprivate class RefContentProposalProvider implements\n+\t\t\tIContentProposalProvider {\n+\t\tprivate List<RefContentProposal> proposals = Collections.emptyList();\n+\n+\t\tprivate final boolean tryResolvingLocally;\n+\n+\t\tprivate RefContentProposalProvider(final boolean tryResolvingLocally) {\n+\t\t\tthis.tryResolvingLocally = tryResolvingLocally;\n+\t\t}\n+\n+\t\tprivate void setProposals(final List<RefContentProposal> proposals) {\n+\t\t\tthis.proposals = proposals;\n+\t\t}\n+\n+\t\tpublic IContentProposal[] getProposals(final String contents,\n+\t\t\t\tint position) {\n+\t\t\tfinal List<RefContentProposal> result = new ArrayList<RefContentProposal>();\n+\n+\t\t\tif (contents.indexOf('*') != -1 || contents.indexOf('?') != -1) {\n+\t\t\t\t// contents contains wildcards\n+\n+\t\t\t\t// check if contents can be safely added as wildcard spec\n+\t\t\t\tif (isValidRefExpression(contents))\n+\t\t\t\t\tresult.add(new RefContentProposal(localDb, contents, null));\n+\n+\t\t\t\t// let's expand wildcards\n+\t\t\t\tfinal String regex = \".*\" //$NON-NLS-1$\n+\t\t\t\t\t\t+ contents.replace(\"*\", \".*\").replace(\"?\", \".?\") + \".*\"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$\n+\t\t\t\tfinal Pattern p = Pattern.compile(regex);\n+\t\t\t\tfor (final RefContentProposal prop : proposals)\n+\t\t\t\t\tif (p.matcher(prop.getContent()).matches())\n+\t\t\t\t\t\tresult.add(prop);\n+\t\t\t} else {\n+\t\t\t\tfor (final RefContentProposal prop : proposals)\n+\t\t\t\t\tif (prop.getContent().contains(contents))\n+\t\t\t\t\t\tresult.add(prop);\n+\n+\t\t\t\tif (tryResolvingLocally && result.isEmpty()) {\n+\t\t\t\t\tfinal ObjectId id = tryResolveLocalRef(contents);\n+\t\t\t\t\tif (id != null)\n+\t\t\t\t\t\tresult\n+\t\t\t\t\t\t\t\t.add(new RefContentProposal(localDb, contents,\n+\t\t\t\t\t\t\t\t\t\tid));\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\treturn result.toArray(new IContentProposal[0]);\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex 55f6c87..f2be3c1 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -76,6 +76,77 @@ CloneDestinationPage_fieldRequired={0} is required.\n CloneDestinationPage_browseButton=Browse\n CloneDestinationPage_errorNotEmptyDir={0} is not an empty directory.\n \n+RefSpecPanel_clickToChange=[Click to change]\n+RefSpecPanel_columnDst=Destination Ref\n+RefSpecPanel_columnForce=Force Update\n+RefSpecPanel_columnMode=Mode\n+RefSpecPanel_columnRemove=Remove\n+RefSpecPanel_columnSrc=Source Ref\n+RefSpecPanel_creationButton=Add spec\n+RefSpecPanel_creationButtonDescription=Add this create/update specification to set of {0} specifications. \n+RefSpecPanel_creationDst=Destination ref:\n+RefSpecPanel_creationGroup=Add create/update specificaton\n+RefSpecPanel_creationSrc=Source ref:\n+RefSpecPanel_deletionButton=Add spec\n+RefSpecPanel_deletionButtonDescription=Add this delete specification to set of push specifications.\n+RefSpecPanel_deletionGroup=Add delete ref specificaton\n+RefSpecPanel_deletionRef=Remote ref to delete:\n+RefSpecPanel_dstFetchDescription=Local destination ref(s) to fetch to - create or update.\n+RefSpecPanel_dstPushDescription=Remote destination ref(s) to push to - create or update.\n+RefSpecPanel_dstDeletionDescription=Remote ref to delete.\n+RefSpecPanel_errorRemoteConfigDescription=Can't parse remote configuration URIs.\n+RefSpecPanel_errorRemoteConfigTitle=Remote configuration problem\n+RefSpecPanel_fetch=fetch\n+RefSpecPanel_forceAll=Force update all specs\n+RefSpecPanel_forceAllDescription=Set force update setting to all speficications.\n+RefSpecPanel_forceDeleteDescription=Delete specification is always unconditional.\n+RefSpecPanel_forceFalseDescription=Allow only fast-forward update: old object must merge into new object.\n+RefSpecPanel_forceTrueDescription=Allow non-fast-forward update: old object doesn't have to merge to new object.\n+RefSpecPanel_modeDelete=Delete\n+RefSpecPanel_modeDeleteDescription=This is a delete specification.\n+RefSpecPanel_modeUpdate=Update\n+RefSpecPanel_modeUpdateDescription=This is a create/update specification.\n+RefSpecPanel_predefinedAll=Add all branches spec\n+RefSpecPanel_predefinedAllDescription=Add specification covering all branches.\n+RefSpecPanel_predefinedConfigured=Add configured {0} specs \n+RefSpecPanel_predefinedConfiguredDescription=Add previously configured specifications for this configured remote (if available).\n+RefSpecPanel_predefinedGroup=Add predefined specification\n+RefSpecPanel_predefinedTags=Add all tags spec\n+RefSpecPanel_predefinedTagsDescription=Add specification covering all tags.\n+RefSpecPanel_push=push\n+RefSpecPanel_refChooseSome=choose/some/ref\n+RefSpecPanel_refChooseSomeWildcard=choose/some/ref/*\n+RefSpecPanel_removeAll=Remove all specs\n+RefSpecPanel_removeAllDescription=Remove all speficications.\n+RefSpecPanel_removeDescription=Click to remove this specification.\n+RefSpecPanel_specifications=Specifications for {0}\n+RefSpecPanel_srcFetchDescription=Remote source ref(s) to fetch from.\n+RefSpecPanel_srcPushDescription=Local source ref(s) to push from.\n+RefSpecPanel_srcDeleteDescription=Delete specification always has an empty source ref.\n+RefSpecPanel_validationDstInvalidExpression={0} is not a valid ref expression for destination.\n+RefSpecPanel_validationDstRequired=Destination ref is required.\n+RefSpecPanel_validationRefDeleteRequired=Ref name to delete is required.\n+RefSpecPanel_validationRefDeleteWildcard=Delete ref cannot be a wildcard.\n+RefSpecPanel_validationRefInvalidExpression={0} is not a valid ref expression.\n+RefSpecPanel_validationRefInvalidLocal={0} is not a valid ref in local repository.\n+RefSpecPanel_validationRefNonExistingRemote={0} does not exist in remote repository.\n+RefSpecPanel_validationRefNonExistingRemoteDelete={0} already does not exist in remote repository.\n+RefSpecPanel_validationRefNonMatchingLocal={0} does not match any ref in local repository.\n+RefSpecPanel_validationRefNonMatchingRemote={0} does not match any ref in remote repository.\n+RefSpecPanel_validationSpecificationsOverlappingDestination=Two or more specifications point to {0} (the same destination).  \n+RefSpecPanel_validationSrcUpdateRequired=Source ref is required for update/create specification.\n+RefSpecPanel_validationWildcardInconsistent=Wildcard must be set either on both source and destination or on none of them.\n+\n+RefSpecPage_descriptionFetch=Select refs to fetch.\n+RefSpecPage_descriptionPush=Select refs to push.\n+RefSpecPage_errorDontMatchSrc=Specifications don't match any existing refs in source repository.\n+RefSpecPage_errorTransportDialogMessage=Cannot get remote repository refs.\n+RefSpecPage_errorTransportDialogTitle=Transport Error\n+RefSpecPage_operationCancelled=Operation cancelled.\n+RefSpecPage_saveSpecifications=Save specifications in \"{0}\" configuration\n+RefSpecPage_titleFetch=Fetch Ref Specifications\n+RefSpecPage_titlePush=Push Ref Specifications\n+\n Decorator_failedLazyLoading=Resource decorator failed to load tree contents on demand.\n QuickDiff_failedLoading=Quick diff failed to obtain file data.\n \n-- \n1.5.6.3\n"},{"id":"87493","messageId":"1219005852-21496-31-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-30-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 30/31] Add PushOperation to plugin","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:11Z","receivedAt":"2008-08-17T20:44:11Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"PushOperation is designed to handle push-to-one and push-to-many URIs,\nusing some supporting classes. Failure of connection to any of\nrepositories doesn't cause to fail whole operation - they are\nindependent that way. It is also possible to specify different expected\nold object id for each remote repository.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/egit/core/CoreText.java        |   15 +\n .../src/org/spearce/egit/core/coretext.properties  |    6 +\n .../org/spearce/egit/core/op/PushOperation.java    |  148 +++++++++++\n .../spearce/egit/core/op/PushOperationResult.java  |  273 ++++++++++++++++++++\n .../egit/core/op/PushOperationSpecification.java   |   82 ++++++\n 5 files changed, 524 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperation.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperationResult.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperationSpecification.java\n\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/CoreText.java b/org.spearce.egit.core/src/org/spearce/egit/core/CoreText.java\nindex 5974a5f..35e17b9 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/CoreText.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/CoreText.java\n@@ -98,6 +98,21 @@ public class CoreText extends NLS {\n \t/** */\n \tpublic static String ListRemoteOperation_title;\n \n+\t/** */\n+\tpublic static String PushOperation_resultCancelled;\n+\n+\t/** */\n+\tpublic static String PushOperation_resultNotSupported;\n+\n+\t/** */\n+\tpublic static String PushOperation_resultTransportError;\n+\n+\t/** */\n+\tpublic static String PushOperation_taskNameDryRun;\n+\n+\t/** */\n+\tpublic static String PushOperation_taskNameNormalRun;\n+\n \tstatic {\n \t\tfinal Class c = CoreText.class;\n \t\tinitializeMessages(c.getPackage().getName() + \".coretext\", c);\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/coretext.properties b/org.spearce.egit.core/src/org/spearce/egit/core/coretext.properties\nindex c412161..94cf4aa 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/coretext.properties\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/coretext.properties\n@@ -57,3 +57,9 @@ CheckpointJob_failed=Failed to write modified objects.\n CloneOperation_title=Cloning from {0}\n \n ListRemoteOperation_title=Getting remote branches information\n+\n+PushOperation_resultCancelled=Operation was cancelled.\n+PushOperation_resultNotSupported=Can't push to {0}\n+PushOperation_resultTransportError=Transport error occured during push operation: {0}\n+PushOperation_taskNameDryRun=Trying pushing to remote repositories\n+PushOperation_taskNameNormalRun=Pushing to remote repositories\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperation.java b/org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperation.java\nnew file mode 100644\nindex 0000000..853bcfc\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperation.java\n@@ -0,0 +1,148 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.core.op;\n+\n+import java.lang.reflect.InvocationTargetException;\n+\n+import org.eclipse.core.runtime.IProgressMonitor;\n+import org.eclipse.core.runtime.NullProgressMonitor;\n+import org.eclipse.core.runtime.SubProgressMonitor;\n+import org.eclipse.jface.operation.IRunnableWithProgress;\n+import org.eclipse.osgi.util.NLS;\n+import org.spearce.egit.core.CoreText;\n+import org.spearce.egit.core.EclipseGitProgressTransformer;\n+import org.spearce.jgit.errors.NotSupportedException;\n+import org.spearce.jgit.errors.TransportException;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.transport.PushResult;\n+import org.spearce.jgit.transport.RemoteConfig;\n+import org.spearce.jgit.transport.Transport;\n+import org.spearce.jgit.transport.URIish;\n+\n+/**\n+ * Push operation: pushing from local repository to one or many remote ones.\n+ */\n+public class PushOperation implements IRunnableWithProgress {\n+\tprivate static final int WORK_UNITS_PER_TRANSPORT = 10;\n+\n+\tprivate final Repository localDb;\n+\n+\tprivate final PushOperationSpecification specification;\n+\n+\tprivate final boolean dryRun;\n+\n+\tprivate final RemoteConfig rc;\n+\n+\tprivate final PushOperationResult operationResult = new PushOperationResult();\n+\n+\t/**\n+\t * Create push operation for provided specification.\n+\t * <p>\n+\t * Operation is not performed within constructor,\n+\t * {@link #run(IProgressMonitor)} method must be called for that.\n+\t * \n+\t * @param localDb\n+\t *            local repository.\n+\t * @param specification\n+\t *            specification of ref updates for remote repositories.\n+\t * @param rc\n+\t *            optional remote config to apply on used transports. May be\n+\t *            null.\n+\t * @param dryRun\n+\t *            true if push operation should just check for possible result\n+\t *            and not really update remote refs, false otherwise - when push\n+\t *            should act normally.\n+\t */\n+\tpublic PushOperation(final Repository localDb,\n+\t\t\tfinal PushOperationSpecification specification,\n+\t\t\tfinal boolean dryRun, final RemoteConfig rc) {\n+\t\tthis.localDb = localDb;\n+\t\tthis.specification = specification;\n+\t\tthis.dryRun = dryRun;\n+\t\tthis.rc = rc;\n+\t}\n+\n+\t/**\n+\t * @return push operation result.\n+\t */\n+\tpublic PushOperationResult getOperationResult() {\n+\t\treturn operationResult;\n+\t}\n+\n+\t/**\n+\t * @return operation specification, as provided in constructor.\n+\t */\n+\tpublic PushOperationSpecification getSpecification() {\n+\t\treturn specification;\n+\t}\n+\n+\t/**\n+\t * Execute operation and store result. Operation is executed independently\n+\t * on each remote repository.\n+\t * <p>\n+\t * \n+\t * @throws InvocationTargetException\n+\t *             Cause of this exceptions may include\n+\t *             {@link TransportException}, {@link NotSupportedException} or\n+\t *             some unexpected {@link RuntimeException}.\n+\t * @see IRunnableWithProgress#run(IProgressMonitor)\n+\t */\n+\tpublic void run(IProgressMonitor monitor) throws InvocationTargetException {\n+\t\tif (monitor == null)\n+\t\t\tmonitor = new NullProgressMonitor();\n+\n+\t\tfinal int totalWork = specification.getURIsNumber()\n+\t\t\t\t* WORK_UNITS_PER_TRANSPORT;\n+\t\tif (dryRun)\n+\t\t\tmonitor.beginTask(CoreText.PushOperation_taskNameDryRun, totalWork);\n+\t\telse\n+\t\t\tmonitor.beginTask(CoreText.PushOperation_taskNameNormalRun,\n+\t\t\t\t\ttotalWork);\n+\n+\t\tfor (final URIish uri : specification.getURIs()) {\n+\t\t\tfinal SubProgressMonitor subMonitor = new SubProgressMonitor(\n+\t\t\t\t\tmonitor, WORK_UNITS_PER_TRANSPORT,\n+\t\t\t\t\tSubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK);\n+\t\t\tTransport transport = null;\n+\t\t\ttry {\n+\t\t\t\tif (monitor.isCanceled()) {\n+\t\t\t\t\toperationResult.addOperationResult(uri,\n+\t\t\t\t\t\t\tCoreText.PushOperation_resultCancelled);\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\t\t\t\ttransport = Transport.open(localDb, uri);\n+\n+\t\t\t\tif (rc != null)\n+\t\t\t\t\ttransport.applyConfig(rc);\n+\t\t\t\ttransport.setDryRun(dryRun);\n+\t\t\t\tfinal EclipseGitProgressTransformer gitSubMonitor = new EclipseGitProgressTransformer(\n+\t\t\t\t\t\tsubMonitor);\n+\t\t\t\tfinal PushResult pr = transport.push(gitSubMonitor,\n+\t\t\t\t\t\tspecification.getRefUpdates(uri));\n+\t\t\t\toperationResult.addOperationResult(uri, pr);\n+\t\t\t} catch (final TransportException e) {\n+\t\t\t\toperationResult.addOperationResult(uri, NLS.bind(\n+\t\t\t\t\t\tCoreText.PushOperation_resultTransportError, e\n+\t\t\t\t\t\t\t\t.getMessage()));\n+\t\t\t} catch (final NotSupportedException e) {\n+\t\t\t\toperationResult.addOperationResult(uri, NLS.bind(\n+\t\t\t\t\t\tCoreText.PushOperation_resultNotSupported, e\n+\t\t\t\t\t\t\t\t.getMessage()));\n+\t\t\t} finally {\n+\t\t\t\tif (transport != null) {\n+\t\t\t\t\ttransport.close();\n+\t\t\t\t}\n+\t\t\t\t// Dirty trick to get things always working.\n+\t\t\t\tsubMonitor.beginTask(\"\", WORK_UNITS_PER_TRANSPORT); //$NON-NLS-1$\n+\t\t\t\tsubMonitor.done();\n+\t\t\t\tsubMonitor.done();\n+\t\t\t}\n+\t\t}\n+\t\tmonitor.done();\n+\t}\n+}\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperationResult.java b/org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperationResult.java\nnew file mode 100644\nindex 0000000..d78b79c\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperationResult.java\n@@ -0,0 +1,273 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.core.op;\n+\n+import java.io.IOException;\n+import java.util.ArrayList;\n+import java.util.Collection;\n+import java.util.Collections;\n+import java.util.LinkedHashMap;\n+import java.util.Set;\n+\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.transport.PushResult;\n+import org.spearce.jgit.transport.RemoteRefUpdate;\n+import org.spearce.jgit.transport.URIish;\n+\n+/**\n+ * Data class for storing push operation results for each remote repository/URI\n+ * being part of push operation.\n+ * <p>\n+ * One instance of this class is dedicated for result of one push operation:\n+ * either to one URI or to many URIs.\n+ * \n+ * @see PushOperation\n+ */\n+public class PushOperationResult {\n+\tprivate LinkedHashMap<URIish, Entry> urisEntries;\n+\n+\t/**\n+\t * Construct empty push operation result.\n+\t */\n+\tpublic PushOperationResult() {\n+\t\tthis.urisEntries = new LinkedHashMap<URIish, Entry>();\n+\t}\n+\n+\t/**\n+\t * Add push result for the repository (URI) with successful connection.\n+\t * \n+\t * @param uri\n+\t *            remote repository URI.\n+\t * @param result\n+\t *            push result.\n+\t */\n+\tpublic void addOperationResult(final URIish uri, final PushResult result) {\n+\t\turisEntries.put(uri, new Entry(result));\n+\t}\n+\n+\t/**\n+\t * Add error message for the repository (URI) with unsuccessful connection.\n+\t * \n+\t * @param uri\n+\t *            remote repository URI.\n+\t * @param errorMessage\n+\t *            failure error message.\n+\t */\n+\tpublic void addOperationResult(final URIish uri, final String errorMessage) {\n+\t\turisEntries.put(uri, new Entry(errorMessage));\n+\t}\n+\n+\t/**\n+\t * @return set of remote repositories URIis. Set is ordered in addition\n+\t *         sequence, which is usually the same as that from\n+\t *         {@link PushOperationSpecification}.\n+\t */\n+\tpublic Set<URIish> getURIs() {\n+\t\treturn Collections.unmodifiableSet(urisEntries.keySet());\n+\t}\n+\n+\t/**\n+\t * @param uri\n+\t *            remote repository URI.\n+\t * @return true if connection was successful for this repository (URI),\n+\t *         false if this operation ended with unsuccessful connection.\n+\t */\n+\tpublic boolean isSuccessfulConnection(final URIish uri) {\n+\t\treturn urisEntries.get(uri).isSuccessfulConnection();\n+\t}\n+\n+\t/**\n+\t * @return true if connection was successful for any repository (URI), false\n+\t *         otherwise.\n+\t */\n+\tpublic boolean isSuccessfulConnectionForAnyURI() {\n+\t\tfor (final URIish uri : getURIs()) {\n+\t\t\tif (isSuccessfulConnection(uri))\n+\t\t\t\treturn true;\n+\t\t}\n+\t\treturn false;\n+\t}\n+\n+\t/**\n+\t * @param uri\n+\t *            remote repository URI.\n+\t * @return push result for this repository (URI) or null if operation ended\n+\t *         with unsuccessful connection for this URI.\n+\t */\n+\tpublic PushResult getPushResult(final URIish uri) {\n+\t\treturn urisEntries.get(uri).getResult();\n+\t}\n+\n+\t/**\n+\t * @param uri\n+\t *            remote repository URI.\n+\t * @return error message for this repository (URI) or null if operation\n+\t *         ended with successful connection for this URI.\n+\t */\n+\tpublic String getErrorMessage(final URIish uri) {\n+\t\treturn urisEntries.get(uri).getErrorMessage();\n+\t}\n+\n+\t/**\n+\t * @return string being list of failed URIs with their error messages.\n+\t */\n+\tpublic String getErrorStringForAllURis() {\n+\t\tfinal StringBuilder sb = new StringBuilder();\n+\t\tboolean first = true;\n+\t\tfor (final URIish uri : getURIs()) {\n+\t\t\tif (first)\n+\t\t\t\tfirst = false;\n+\t\t\telse\n+\t\t\t\tsb.append(\", \");\n+\t\t\tsb.append(uri);\n+\t\t\tsb.append(\" (\");\n+\t\t\tsb.append(getErrorMessage(uri));\n+\t\t\tsb.append(\")\");\n+\t\t}\n+\t\treturn sb.toString();\n+\t}\n+\n+\t/**\n+\t * Derive push operation specification from this push operation result.\n+\t * <p>\n+\t * Specification is created basing on URIs of remote repositories in this\n+\t * result that completed without connection errors, and remote ref updates\n+\t * from push results.\n+\t * <p>\n+\t * This method is targeted to provide support for 2-stage push, where first\n+\t * operation is dry run for user confirmation and second one is a real\n+\t * operation.\n+\t * \n+\t * @param requireUnchanged\n+\t *            if true, newly created copies of remote ref updates have\n+\t *            expected old object id set to previously advertised ref value\n+\t *            (remote ref won't be updated if it change in the mean time),\n+\t *            if false, newly create copies of remote ref updates have\n+\t *            expected object id set up as in this result source\n+\t *            specification.\n+\t * @return derived specification for another push operation.\n+\t * @throws IOException\n+\t *             when some previously locally available source ref is not\n+\t *             available anymore, or some error occurred during creation\n+\t *             locally tracking ref update.\n+\t * \n+\t */\n+\tpublic PushOperationSpecification deriveSpecification(\n+\t\t\tfinal boolean requireUnchanged) throws IOException {\n+\t\tfinal PushOperationSpecification spec = new PushOperationSpecification();\n+\t\tfor (final URIish uri : getURIs()) {\n+\t\t\tfinal PushResult pr = getPushResult(uri);\n+\t\t\tif (pr == null)\n+\t\t\t\tcontinue;\n+\n+\t\t\tfinal Collection<RemoteRefUpdate> oldUpdates = pr\n+\t\t\t\t\t.getRemoteUpdates();\n+\t\t\tfinal ArrayList<RemoteRefUpdate> newUpdates = new ArrayList<RemoteRefUpdate>(\n+\t\t\t\t\toldUpdates.size());\n+\t\t\tfor (final RemoteRefUpdate rru : oldUpdates) {\n+\t\t\t\tfinal ObjectId expectedOldObjectId;\n+\t\t\t\tif (requireUnchanged) {\n+\t\t\t\t\tfinal Ref advertisedRef = getPushResult(uri)\n+\t\t\t\t\t\t\t.getAdvertisedRef(rru.getRemoteName());\n+\t\t\t\t\tif (advertisedRef == null)\n+\t\t\t\t\t\texpectedOldObjectId = ObjectId.zeroId();\n+\t\t\t\t\telse\n+\t\t\t\t\t\texpectedOldObjectId = advertisedRef.getObjectId();\n+\t\t\t\t} else\n+\t\t\t\t\texpectedOldObjectId = rru.getExpectedOldObjectId();\n+\t\t\t\tfinal RemoteRefUpdate newRru = new RemoteRefUpdate(rru,\n+\t\t\t\t\t\texpectedOldObjectId);\n+\t\t\t\tnewUpdates.add(newRru);\n+\t\t\t}\n+\t\t\tspec.addURIRefUpdates(uri, newUpdates);\n+\t\t}\n+\t\treturn spec;\n+\t}\n+\n+\t/**\n+\t * This implementation returns true if all following conditions are met:\n+\t * <ul>\n+\t * <li>both objects result have the same set successfully connected\n+\t * repositories (URIs) - unsuccessful connections are discarded, AND <li>\n+\t * remote ref updates must match for each successful connection in sense of\n+\t * equal remoteName, equal status and equal newObjectId value.</li>\n+\t * </ul>\n+\t * \n+\t * @see Object#equals(Object)\n+\t * @param obj\n+\t *            other push operation result to compare to.\n+\t * @return true if object is equal to this one in terms of conditions\n+\t *         described above, false otherwise.\n+\t */\n+\t@Override\n+\tpublic boolean equals(final Object obj) {\n+\t\tif (!(obj instanceof PushOperationResult))\n+\t\t\treturn false;\n+\n+\t\tfinal PushOperationResult other = (PushOperationResult) obj;\n+\n+\t\t// Check successful connections/URIs two-ways:\n+\t\tfinal Set<URIish> otherURIs = other.getURIs();\n+\t\tfor (final URIish uri : getURIs()) {\n+\t\t\tif (isSuccessfulConnection(uri)\n+\t\t\t\t\t&& (!otherURIs.contains(uri) || !other\n+\t\t\t\t\t\t\t.isSuccessfulConnection(uri)))\n+\t\t\t\treturn false;\n+\t\t}\n+\t\tfor (final URIish uri : other.getURIs()) {\n+\t\t\tif (other.isSuccessfulConnection(uri)\n+\t\t\t\t\t&& (!urisEntries.containsKey(uri) || !isSuccessfulConnection(uri)))\n+\t\t\t\treturn false;\n+\t\t}\n+\n+\t\tfor (final URIish uri : getURIs()) {\n+\t\t\tif (!isSuccessfulConnection(uri))\n+\t\t\t\tcontinue;\n+\n+\t\t\tfinal PushResult otherPushResult = other.getPushResult(uri);\n+\t\t\tfor (final RemoteRefUpdate rru : getPushResult(uri)\n+\t\t\t\t\t.getRemoteUpdates()) {\n+\t\t\t\tfinal RemoteRefUpdate otherRru = otherPushResult\n+\t\t\t\t\t\t.getRemoteUpdate(rru.getRemoteName());\n+\t\t\t\tif (otherRru == null)\n+\t\t\t\t\treturn false;\n+\t\t\t\tif (otherRru.getStatus() != rru.getStatus()\n+\t\t\t\t\t\t|| otherRru.getNewObjectId() != rru.getNewObjectId())\n+\t\t\t\t\treturn false;\n+\t\t\t}\n+\t\t}\n+\t\treturn true;\n+\t}\n+\n+\tprivate static class Entry {\n+\t\tprivate String errorMessage;\n+\n+\t\tprivate PushResult result;\n+\n+\t\tEntry(final PushResult result) {\n+\t\t\tthis.result = result;\n+\t\t}\n+\n+\t\tEntry(final String errorMessage) {\n+\t\t\tthis.errorMessage = errorMessage;\n+\t\t}\n+\n+\t\tboolean isSuccessfulConnection() {\n+\t\t\treturn result != null;\n+\t\t}\n+\n+\t\tString getErrorMessage() {\n+\t\t\treturn errorMessage;\n+\t\t}\n+\n+\t\tPushResult getResult() {\n+\t\t\treturn result;\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperationSpecification.java b/org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperationSpecification.java\nnew file mode 100644\nindex 0000000..0e3f3b4\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/PushOperationSpecification.java\n@@ -0,0 +1,82 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.core.op;\n+\n+import java.util.Collection;\n+import java.util.Collections;\n+import java.util.LinkedHashMap;\n+import java.util.Set;\n+\n+import org.spearce.jgit.transport.RemoteRefUpdate;\n+import org.spearce.jgit.transport.URIish;\n+\n+/**\n+ * Data class storing push operation update specifications for each remote\n+ * repository.\n+ * <p>\n+ * One instance is dedicated for one push operation: either push to one URI or\n+ * to many URIs.\n+ * \n+ * @see PushOperation\n+ */\n+public class PushOperationSpecification {\n+\tprivate LinkedHashMap<URIish, Collection<RemoteRefUpdate>> urisRefUpdates;\n+\n+\t/**\n+\t * Create empty instance of specification.\n+\t * <p>\n+\t * URIs and ref updates should be configured\n+\t * {@link #addURIRefUpdates(URIish, Collection)} method.\n+\t */\n+\tpublic PushOperationSpecification() {\n+\t\tthis.urisRefUpdates = new LinkedHashMap<URIish, Collection<RemoteRefUpdate>>();\n+\t}\n+\n+\t/**\n+\t * Add remote repository URI with ref updates specification.\n+\t * <p>\n+\t * Ref updates are not in constructor - pay attention to not share them\n+\t * between different URIs ref updates or push operations.\n+\t * <p>\n+\t * Note that refUpdates can differ between URIs <b>only</b> by expected old\n+\t * object id field: {@link RemoteRefUpdate#getExpectedOldObjectId()}.\n+\t * \n+\t * @param uri\n+\t *            remote repository URI.\n+\t * @param refUpdates\n+\t *            collection of remote ref updates specifications.\n+\t */\n+\tpublic void addURIRefUpdates(final URIish uri,\n+\t\t\tCollection<RemoteRefUpdate> refUpdates) {\n+\t\turisRefUpdates.put(uri, refUpdates);\n+\t}\n+\n+\t/**\n+\t * @return set of remote repositories URIis. Set is ordered in addition\n+\t *         sequence.\n+\t */\n+\tpublic Set<URIish> getURIs() {\n+\t\treturn Collections.unmodifiableSet(urisRefUpdates.keySet());\n+\t}\n+\n+\t/**\n+\t * @return number of remote repositories URI for this push operation.\n+\t */\n+\tpublic int getURIsNumber() {\n+\t\treturn urisRefUpdates.keySet().size();\n+\t}\n+\n+\t/**\n+\t * @param uri\n+\t *            remote repository URI.\n+\t * @return remote ref updates as specified by user for this URI.\n+\t */\n+\tpublic Collection<RemoteRefUpdate> getRefUpdates(final URIish uri) {\n+\t\treturn Collections.unmodifiableCollection(urisRefUpdates.get(uri));\n+\t}\n+}\n-- \n1.5.6.3\n"},{"id":"87494","messageId":"1219005852-21496-32-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219005852-21496-31-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 31/31] Push GUI","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-17T20:44:12Z","receivedAt":"2008-08-17T20:44:12Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Push operation in GUI is implemented as wizard consisting of 3 pages:\n-repository selection\n-ref specifications selection\n-confirmation page with dry run results (optional - can be skipped)\n\nWizard finish-action starts Job with PushOperation. Dialog with results\nis shown when job ends. It can be configured to be displayed only if\nresults change since confirmation.\n\nWith confirmation page, it is possible to perform 2-stage push, allowing\nuser to preview updates and mark them to be performed only if\nadvertised refs don't change.\n\nEventually, results Dialog can be replaced with Result/Transport view\nsome day, as this idea arose recently.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n org.spearce.egit.ui/plugin.properties              |    3 +\n org.spearce.egit.ui/plugin.xml                     |   15 +\n .../src/org/spearce/egit/ui/UIText.java            |  129 ++++++++\n .../egit/ui/internal/actions/PushAction.java       |   47 +++\n .../egit/ui/internal/push/ConfirmationPage.java    |  211 +++++++++++++\n .../egit/ui/internal/push/PushResultTable.java     |  327 ++++++++++++++++++++\n .../spearce/egit/ui/internal/push/PushWizard.java  |  250 +++++++++++++++\n .../ui/internal/push/RefUpdateContentProvider.java |   62 ++++\n .../egit/ui/internal/push/RefUpdateElement.java    |   67 ++++\n .../egit/ui/internal/push/ResultDialog.java        |   65 ++++\n .../src/org/spearce/egit/ui/uitext.properties      |   47 +++\n 11 files changed, 1223 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/PushAction.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/ConfirmationPage.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/PushResultTable.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/PushWizard.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/RefUpdateContentProvider.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/RefUpdateElement.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/ResultDialog.java\n\ndiff --git a/org.spearce.egit.ui/plugin.properties b/org.spearce.egit.ui/plugin.properties\nindex ce3b058..8ac3474 100644\n--- a/org.spearce.egit.ui/plugin.properties\n+++ b/org.spearce.egit.ui/plugin.properties\n@@ -41,6 +41,9 @@ ResetAction_tooltip=Reset the current branch to the same or another commit\n BranchAction_label=&Branch...\n BranchAction_tooltip=Switch to another branch\n \n+PushAction_label=&Push To...\n+PushAction_tooltip=Push to another repository\n+\n GitActions_label=Git\n GitMenu_label=&Git\n \ndiff --git a/org.spearce.egit.ui/plugin.xml b/org.spearce.egit.ui/plugin.xml\nindex 0b59a6e..ca93f5a 100644\n--- a/org.spearce.egit.ui/plugin.xml\n+++ b/org.spearce.egit.ui/plugin.xml\n@@ -40,6 +40,12 @@\n                id=\"org.spearce.egit.ui.internal.actions.Disconnect\">\n          </action>\n          <action\n+               class=\"org.spearce.egit.ui.internal.actions.PushAction\"\n+               id=\"org.spearce.egit.ui.internal.actions.PushAction\"\n+               label=\"%PushAction_label\"\n+               menubarPath=\"team.main/projectGroup\"\n+               tooltip=\"%PushAction_tooltip\"/>         \n+         <action\n                class=\"org.spearce.egit.ui.internal.actions.ResetAction\"\n                id=\"org.spearce.egit.ui.internal.actions.ResetAction\"\n                label=\"%ResetAction_label\"\n@@ -253,6 +259,15 @@\n         </separator>\n \t    </menu>\n \t\t<action\n+\t\t       class=\"org.spearce.egit.ui.internal.actions.PushAction\"\n+\t\t       id=\"org.spearce.egit.ui.actionpush\"\n+\t\t       label=\"%PushAction_label\"\n+\t\t       style=\"push\"\n+\t\t       menubarPath=\"org.spearce.egit.ui.gitmenu/repo\"\n+\t\t       toolbarPath=\"org.spearce.egit.ui\"\n+\t\t       tooltip=\"%PushAction_tooltip\">\n+\t\t</action>\n+\t\t<action\n \t\t       class=\"org.spearce.egit.ui.internal.actions.BranchAction\"\n \t\t       disabledIcon=\"icons/toolbar/checkoutd.png\"\n \t\t       icon=\"icons/toolbar/checkoute.png\"\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex ff2b541..cc785f7 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -464,6 +464,135 @@ public class UIText extends NLS {\n \tpublic static String HistoryPreferencePage_title;\n \n \t/** */\n+\tpublic static String PushWizard_cantConnectToAny;\n+\n+\t/** */\n+\tpublic static String PushWizard_cantPrepareUpdatesMessage;\n+\n+\t/** */\n+\tpublic static String PushWizard_cantPrepareUpdatesTitle;\n+\n+\t/** */\n+\tpublic static String PushWizard_cantSaveMessage;\n+\n+\t/** */\n+\tpublic static String PushWizard_cantSaveTitle;\n+\n+\t/** */\n+\tpublic static String PushWizard_jobName;\n+\n+\t/** */\n+\tpublic static String PushWizard_missingRefsMessage;\n+\n+\t/** */\n+\tpublic static String PushWizard_missingRefsTitle;\n+\n+\t/** */\n+\tpublic static String PushWizard_unexpectedError;\n+\n+\t/** */\n+\tpublic static String PushWizard_windowTitleDefault;\n+\n+\t/** */\n+\tpublic static String PushWizard_windowTitleWithDestination;\n+\n+\t/** */\n+\tpublic static String ConfirmationPage_cantConnectToAny;\n+\n+\t/** */\n+\tpublic static String ConfirmationPage_description;\n+\n+\t/** */\n+\tpublic static String ConfirmationPage_errorCantResolveSpecs;\n+\n+\t/** */\n+\tpublic static String ConfirmationPage_errorInterrupted;\n+\n+\t/** */\n+\tpublic static String ConfirmationPage_errorRefsChangedNoMatch;\n+\n+\t/** */\n+\tpublic static String ConfirmationPage_errorUnexpected;\n+\n+\t/** */\n+\tpublic static String ConfirmationPage_requireUnchangedButton;\n+\n+\t/** */\n+\tpublic static String ConfirmationPage_showOnlyIfChanged;\n+\n+\t/** */\n+\tpublic static String ConfirmationPage_title;\n+\n+\t/** */\n+\tpublic static String PushResultTable_columnStatusRepo;\n+\n+\t/** */\n+\tpublic static String PushResultTable_columnDst;\n+\n+\t/** */\n+\tpublic static String PushResultTable_columnSrc;\n+\n+\t/** */\n+\tpublic static String PushResultTable_columnMode;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusUnexpected;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusConnectionFailed;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusDetailChanged;\n+\n+\t/** */\n+\tpublic static String PushResultTable_refNonExisting;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusDetailDeleted;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusDetailNonFastForward;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusDetailNoDelete;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusDetailNonExisting;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusDetailForcedUpdate;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusDetailFastForward;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusRemoteRejected;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusRejected;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusNoMatch;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusUpToDate;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusOkDeleted;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusOkNewBranch;\n+\n+\t/** */\n+\tpublic static String PushResultTable_statusOkNewTag;\n+\n+\t/** */\n+\tpublic static String ResultDialog_title;\n+\n+\t/** */\n+\tpublic static String ResultDialog_label;\n+\n+\t/** */\n \tpublic static String WindowCachePreferencePage_title;\n \n \t/** */\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/PushAction.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/PushAction.java\nnew file mode 100644\nindex 0000000..61833d0\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/PushAction.java\n@@ -0,0 +1,47 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.actions;\n+\n+import java.net.URISyntaxException;\n+\n+import org.eclipse.jface.action.IAction;\n+import org.eclipse.jface.dialogs.MessageDialog;\n+import org.eclipse.jface.wizard.WizardDialog;\n+import org.spearce.egit.ui.internal.push.PushWizard;\n+import org.spearce.jgit.lib.Repository;\n+\n+/**\n+ * Action for choosing specifications for push, and pushing out to another\n+ * repository.\n+ */\n+public class PushAction extends RepositoryAction {\n+\n+\t@Override\n+\tpublic void run(IAction action) {\n+\t\tfinal Repository repository = getRepository(true);\n+\t\tif (repository == null)\n+\t\t\treturn;\n+\n+\t\tfinal PushWizard pushWizard;\n+\t\ttry {\n+\t\t\tpushWizard = new PushWizard(repository);\n+\t\t} catch (URISyntaxException x) {\n+\t\t\tMessageDialog.openError(getShell(), \"Corrupted configuration\",\n+\t\t\t\t\t\"Remote repositories URLs configuration is corrupted: \"\n+\t\t\t\t\t\t\t+ x.getMessage());\n+\t\t\treturn;\n+\t\t}\n+\t\tfinal WizardDialog dialog = new WizardDialog(getShell(), pushWizard);\n+\t\tdialog.open();\n+\t}\n+\n+\t@Override\n+\tpublic boolean isEnabled() {\n+\t\treturn getRepository(false) != null;\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/ConfirmationPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/ConfirmationPage.java\nnew file mode 100644\nindex 0000000..38109d2\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/ConfirmationPage.java\n@@ -0,0 +1,211 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.push;\n+\n+import java.io.IOException;\n+import java.lang.reflect.InvocationTargetException;\n+import java.util.ArrayList;\n+import java.util.Collection;\n+import java.util.List;\n+\n+import org.eclipse.jface.wizard.WizardPage;\n+import org.eclipse.osgi.util.NLS;\n+import org.eclipse.swt.SWT;\n+import org.eclipse.swt.layout.GridData;\n+import org.eclipse.swt.layout.GridLayout;\n+import org.eclipse.swt.widgets.Button;\n+import org.eclipse.swt.widgets.Composite;\n+import org.eclipse.swt.widgets.Control;\n+import org.spearce.egit.core.op.PushOperation;\n+import org.spearce.egit.core.op.PushOperationResult;\n+import org.spearce.egit.core.op.PushOperationSpecification;\n+import org.spearce.egit.ui.UIText;\n+import org.spearce.egit.ui.internal.components.RefSpecPage;\n+import org.spearce.egit.ui.internal.components.RepositorySelection;\n+import org.spearce.egit.ui.internal.components.RepositorySelectionPage;\n+import org.spearce.egit.ui.internal.components.SelectionChangeListener;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.transport.RefSpec;\n+import org.spearce.jgit.transport.RemoteRefUpdate;\n+import org.spearce.jgit.transport.Transport;\n+import org.spearce.jgit.transport.URIish;\n+\n+class ConfirmationPage extends WizardPage {\n+\tstatic Collection<RemoteRefUpdate> copyUpdates(\n+\t\t\tfinal Collection<RemoteRefUpdate> refUpdates) throws IOException {\n+\t\tfinal Collection<RemoteRefUpdate> copy = new ArrayList<RemoteRefUpdate>(\n+\t\t\t\trefUpdates.size());\n+\t\tfor (final RemoteRefUpdate rru : refUpdates)\n+\t\t\tcopy.add(new RemoteRefUpdate(rru, null));\n+\t\treturn copy;\n+\t}\n+\n+\tprivate final Repository local;\n+\n+\tprivate final RepositorySelectionPage repoPage;\n+\n+\tprivate final RefSpecPage refSpecPage;\n+\n+\tprivate RepositorySelection displayedRepoSelection;\n+\n+\tprivate List<RefSpec> displayedRefSpecs;\n+\n+\tprivate PushOperationResult confirmedResult;\n+\n+\tprivate PushResultTable resultPanel;\n+\n+\tprivate Button requireUnchangedButton;\n+\n+\tprivate Button showOnlyIfChanged;\n+\n+\tpublic ConfirmationPage(final Repository local,\n+\t\t\tfinal RepositorySelectionPage repoPage,\n+\t\t\tfinal RefSpecPage refSpecPage) {\n+\t\tsuper(ConfirmationPage.class.getName());\n+\t\tthis.local = local;\n+\t\tthis.repoPage = repoPage;\n+\t\tthis.refSpecPage = refSpecPage;\n+\n+\t\tsetTitle(UIText.ConfirmationPage_title);\n+\t\tsetDescription(UIText.ConfirmationPage_description);\n+\n+\t\tfinal SelectionChangeListener listener = new SelectionChangeListener() {\n+\t\t\tpublic void selectionChanged() {\n+\t\t\t\tcheckPreviousPagesSelections();\n+\t\t\t}\n+\t\t};\n+\t\trepoPage.addSelectionListener(listener);\n+\t\trefSpecPage.addSelectionListener(listener);\n+\t}\n+\n+\tpublic void createControl(final Composite parent) {\n+\t\tfinal Composite panel = new Composite(parent, SWT.NONE);\n+\t\tpanel.setLayout(new GridLayout());\n+\n+\t\tresultPanel = new PushResultTable(panel);\n+\t\tfinal Control tableControl = resultPanel.getControl();\n+\t\ttableControl\n+\t\t\t\t.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));\n+\n+\t\trequireUnchangedButton = new Button(panel, SWT.CHECK);\n+\t\trequireUnchangedButton\n+\t\t\t\t.setText(UIText.ConfirmationPage_requireUnchangedButton);\n+\n+\t\tshowOnlyIfChanged = new Button(panel, SWT.CHECK);\n+\t\tshowOnlyIfChanged.setText(UIText.ConfirmationPage_showOnlyIfChanged);\n+\n+\t\tsetControl(panel);\n+\t}\n+\n+\t@Override\n+\tpublic void setVisible(final boolean visible) {\n+\t\tif (visible)\n+\t\t\trevalidate();\n+\t\tsuper.setVisible(visible);\n+\t}\n+\n+\tboolean isConfirmed() {\n+\t\treturn confirmedResult != null;\n+\t}\n+\n+\tPushOperationResult getConfirmedResult() {\n+\t\treturn confirmedResult;\n+\t}\n+\n+\tboolean isRequireUnchangedSelected() {\n+\t\treturn requireUnchangedButton.getSelection();\n+\t}\n+\n+\tboolean isShowOnlyIfChangedSelected() {\n+\t\treturn showOnlyIfChanged.getSelection();\n+\t}\n+\n+\tprivate void checkPreviousPagesSelections() {\n+\t\tif (!repoPage.selectionEquals(displayedRepoSelection)\n+\t\t\t\t|| !refSpecPage.specsSelectionEquals(displayedRefSpecs)) {\n+\t\t\t// Allow user to finish by skipping confirmation...\n+\t\t\tsetPageComplete(true);\n+\t\t} else {\n+\t\t\t// ... but if user doesn't skip confirmation, allow only when no\n+\t\t\t// critical errors occurred\n+\t\t\tsetPageComplete(confirmedResult != null);\n+\t\t}\n+\t}\n+\n+\tprivate void revalidate() {\n+\t\t// always update this page\n+\t\tresultPanel.setData(local, new PushOperationResult());\n+\t\tconfirmedResult = null;\n+\t\tdisplayedRepoSelection = repoPage.getSelection();\n+\t\tdisplayedRefSpecs = refSpecPage.getRefSpecs();\n+\t\tsetErrorMessage(null);\n+\t\tsetPageComplete(false);\n+\t\tgetControl().getDisplay().asyncExec(new Runnable() {\n+\t\t\tpublic void run() {\n+\t\t\t\trevalidateImpl();\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\tprivate void revalidateImpl() {\n+\t\tif (getControl().isDisposed() || !isCurrentPage())\n+\t\t\treturn;\n+\t\t\n+\t\tfinal List<RefSpec> fetchSpecs;\n+\t\tif (displayedRepoSelection.isConfigSelected())\n+\t\t\tfetchSpecs = displayedRepoSelection.getConfig().getFetchRefSpecs();\n+\t\telse\n+\t\t\tfetchSpecs = null;\n+\n+\t\tfinal PushOperation operation;\n+\t\ttry {\n+\t\t\tfinal Collection<RemoteRefUpdate> updates = Transport\n+\t\t\t\t\t.findRemoteRefUpdatesFor(local, displayedRefSpecs,\n+\t\t\t\t\t\t\tfetchSpecs);\n+\t\t\tif (updates.isEmpty()) {\n+\t\t\t\t// It can happen only when local refs changed in the mean time.\n+\t\t\t\tsetErrorMessage(UIText.ConfirmationPage_errorRefsChangedNoMatch);\n+\t\t\t\tsetPageComplete(false);\n+\t\t\t\treturn;\n+\t\t\t}\n+\n+\t\t\tfinal PushOperationSpecification spec = new PushOperationSpecification();\n+\t\t\tfor (final URIish uri : displayedRepoSelection.getAllURIs())\n+\t\t\t\tspec.addURIRefUpdates(uri, copyUpdates(updates));\n+\n+\t\t\toperation = new PushOperation(local, spec, true,\n+\t\t\t\t\tdisplayedRepoSelection.getConfig());\n+\t\t\tgetContainer().run(true, true, operation);\n+\t\t} catch (final IOException e) {\n+\t\t\tsetErrorMessage(NLS.bind(\n+\t\t\t\t\tUIText.ConfirmationPage_errorCantResolveSpecs, e\n+\t\t\t\t\t\t\t.getMessage()));\n+\t\t\treturn;\n+\t\t} catch (final InvocationTargetException e) {\n+\t\t\tsetErrorMessage(NLS.bind(UIText.ConfirmationPage_errorUnexpected, e\n+\t\t\t\t\t.getCause().getMessage()));\n+\t\t\treturn;\n+\t\t} catch (final InterruptedException e) {\n+\t\t\tsetErrorMessage(UIText.ConfirmationPage_errorInterrupted);\n+\t\t\tsetPageComplete(true);\n+\t\t\tdisplayedRefSpecs = null;\n+\t\t\tdisplayedRepoSelection = null;\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tfinal PushOperationResult result = operation.getOperationResult();\n+\t\tresultPanel.setData(local, result);\n+\t\tif (result.isSuccessfulConnectionForAnyURI()) {\n+\t\t\tsetPageComplete(true);\n+\t\t\tconfirmedResult = result;\n+\t\t} else {\n+\t\t\tsetErrorMessage(NLS.bind(UIText.ConfirmationPage_cantConnectToAny,\n+\t\t\t\t\tresult.getErrorStringForAllURis()));\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/PushResultTable.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/PushResultTable.java\nnew file mode 100644\nindex 0000000..5daf153\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/PushResultTable.java\n@@ -0,0 +1,327 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.push;\n+\n+import org.eclipse.jface.layout.TableColumnLayout;\n+import org.eclipse.jface.resource.ColorRegistry;\n+import org.eclipse.jface.resource.ImageRegistry;\n+import org.eclipse.jface.viewers.ColumnLabelProvider;\n+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;\n+import org.eclipse.jface.viewers.ColumnWeightData;\n+import org.eclipse.jface.viewers.IElementComparer;\n+import org.eclipse.jface.viewers.TableViewer;\n+import org.eclipse.jface.viewers.TableViewerColumn;\n+import org.eclipse.osgi.util.NLS;\n+import org.eclipse.swt.SWT;\n+import org.eclipse.swt.events.DisposeEvent;\n+import org.eclipse.swt.events.DisposeListener;\n+import org.eclipse.swt.graphics.Color;\n+import org.eclipse.swt.graphics.Image;\n+import org.eclipse.swt.graphics.RGB;\n+import org.eclipse.swt.layout.GridData;\n+import org.eclipse.swt.layout.GridLayout;\n+import org.eclipse.swt.widgets.Composite;\n+import org.eclipse.swt.widgets.Control;\n+import org.eclipse.swt.widgets.Table;\n+import org.eclipse.swt.widgets.TableColumn;\n+import org.spearce.egit.core.op.PushOperationResult;\n+import org.spearce.egit.ui.UIIcons;\n+import org.spearce.egit.ui.UIText;\n+import org.spearce.egit.ui.internal.components.CenteredImageLabelProvider;\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.transport.RemoteRefUpdate;\n+import org.spearce.jgit.transport.URIish;\n+import org.spearce.jgit.transport.RemoteRefUpdate.Status;\n+\n+/**\n+ * Table displaying push operation results.\n+ */\n+class PushResultTable {\n+\tprivate static final int TABLE_PREFERRED_WIDTH = 650;\n+\n+\tprivate static final int TABLE_PREFERRED_HEIGHT = 300;\n+\n+\tprivate static final int COLUMN_STATUS_WEIGHT = 40;\n+\n+\tprivate static final int COLUMN_DST_WEIGHT = 40;\n+\n+\tprivate static final int COLUMN_SRC_WEIGHT = 40;\n+\n+\tprivate static final int COLUMN_MODE_WEIGHT = 15;\n+\n+\tprivate static final String IMAGE_DELETE = \"MODE_DELETE\";\n+\n+\tprivate static final String IMAGE_ADD = \"MODE_ADD\";\n+\n+\tprivate static final String COLOR_REJECTED_KEY = \"REJECTED\";\n+\n+\tprivate static final RGB COLOR_REJECTED = new RGB(255, 0, 0);\n+\n+\tprivate static final String COLOR_UPDATED_KEY = \"UPDATED\";\n+\n+\tprivate static final RGB COLOR_UPDATED = new RGB(0, 255, 0);\n+\n+\tprivate static final String COLOR_UP_TO_DATE_KEY = \"UP_TO_DATE\";\n+\n+\tprivate static final RGB COLOR_UP_TO_DATE = new RGB(245, 245, 245);\n+\n+\tprivate final TableViewer tableViewer;\n+\n+\tprivate final Composite tablePanel;\n+\n+\tprivate final ImageRegistry imageRegistry;\n+\n+\tprivate final ColorRegistry colorRegistry;\n+\n+\tPushResultTable(final Composite parent) {\n+\t\ttablePanel = new Composite(parent, SWT.NONE);\n+\t\ttablePanel.setLayout(new GridLayout());\n+\t\tfinal GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);\n+\t\tlayoutData.heightHint = TABLE_PREFERRED_HEIGHT;\n+\t\tlayoutData.widthHint = TABLE_PREFERRED_WIDTH;\n+\t\ttableViewer = new TableViewer(tablePanel);\n+\t\tColumnViewerToolTipSupport.enableFor(tableViewer);\n+\t\tfinal Table table = tableViewer.getTable();\n+\t\ttable.setLinesVisible(true);\n+\t\ttable.setHeaderVisible(true);\n+\n+\t\timageRegistry = new ImageRegistry(table.getDisplay());\n+\t\timageRegistry.put(IMAGE_ADD, UIIcons.ELCL16_ADD);\n+\t\timageRegistry.put(IMAGE_DELETE, UIIcons.ELCL16_DELETE);\n+\n+\t\tcolorRegistry = new ColorRegistry(table.getDisplay());\n+\t\tcolorRegistry.put(COLOR_REJECTED_KEY, COLOR_REJECTED);\n+\t\tcolorRegistry.put(COLOR_UPDATED_KEY, COLOR_UPDATED);\n+\t\tcolorRegistry.put(COLOR_UP_TO_DATE_KEY, COLOR_UP_TO_DATE);\n+\n+\t\ttablePanel.addDisposeListener(new DisposeListener() {\n+\t\t\tpublic void widgetDisposed(DisposeEvent e) {\n+\t\t\t\timageRegistry.dispose();\n+\t\t\t}\n+\t\t});\n+\n+\t\ttableViewer.setComparer(new IElementComparer() {\n+\t\t\t// we need this to keep refresh() working while having custom\n+\t\t\t// equals() in PushOperationResult\n+\t\t\tpublic boolean equals(Object a, Object b) {\n+\t\t\t\treturn a == b;\n+\t\t\t}\n+\n+\t\t\tpublic int hashCode(Object element) {\n+\t\t\t\treturn element.hashCode();\n+\t\t\t}\n+\t\t});\n+\t\ttableViewer.setContentProvider(new RefUpdateContentProvider());\n+\t\ttableViewer.setInput(new PushOperationResult());\n+\t}\n+\n+\tvoid setData(final Repository localDb, final PushOperationResult result) {\n+\t\t// We have to recreate columns.\n+\t\tfor (final TableColumn tc : tableViewer.getTable().getColumns())\n+\t\t\ttc.dispose();\n+\t\t// Set empty result for a while.\n+\t\ttableViewer.setInput(new PushOperationResult());\n+\n+\t\t// Layout should be recreated to work properly.\n+\t\tfinal TableColumnLayout layout = new TableColumnLayout();\n+\t\ttablePanel.setLayout(layout);\n+\n+\t\tfinal TableViewerColumn modeViewer = createColumn(layout,\n+\t\t\t\tUIText.PushResultTable_columnMode, COLUMN_MODE_WEIGHT,\n+\t\t\t\tSWT.CENTER);\n+\t\tmodeViewer.setLabelProvider(new CenteredImageLabelProvider() {\n+\t\t\t@Override\n+\t\t\tpublic Image getImage(Object element) {\n+\t\t\t\tif (((RefUpdateElement) element).isDelete())\n+\t\t\t\t\treturn imageRegistry.get(IMAGE_DELETE);\n+\t\t\t\treturn imageRegistry.get(IMAGE_ADD);\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic String getToolTipText(Object element) {\n+\t\t\t\tif (((RefUpdateElement) element).isDelete())\n+\t\t\t\t\treturn UIText.RefSpecPanel_modeDeleteDescription;\n+\t\t\t\treturn UIText.RefSpecPanel_modeUpdateDescription;\n+\t\t\t}\n+\t\t});\n+\n+\t\tfinal TableViewerColumn srcViewer = createColumn(layout,\n+\t\t\t\tUIText.PushResultTable_columnSrc, COLUMN_SRC_WEIGHT, SWT.LEFT);\n+\t\tsrcViewer.setLabelProvider(new ColumnLabelProvider() {\n+\t\t\t@Override\n+\t\t\tpublic String getText(Object element) {\n+\t\t\t\treturn ((RefUpdateElement) element).getSrcRefName();\n+\t\t\t}\n+\t\t});\n+\n+\t\tfinal TableViewerColumn dstViewer = createColumn(layout,\n+\t\t\t\tUIText.PushResultTable_columnDst, COLUMN_DST_WEIGHT, SWT.LEFT);\n+\t\tdstViewer.setLabelProvider(new ColumnLabelProvider() {\n+\t\t\t@Override\n+\t\t\tpublic String getText(Object element) {\n+\t\t\t\treturn ((RefUpdateElement) element).getDstRefName();\n+\t\t\t}\n+\t\t});\n+\n+\t\tint i = 0;\n+\t\tfor (final URIish uri : result.getURIs()) {\n+\t\t\tfinal TableViewerColumn statusViewer = createColumn(layout, NLS\n+\t\t\t\t\t.bind(UIText.PushResultTable_columnStatusRepo, Integer\n+\t\t\t\t\t\t\t.toString(++i)), COLUMN_STATUS_WEIGHT, SWT.CENTER);\n+\t\t\tstatusViewer.getColumn().setToolTipText(uri.toString());\n+\t\t\tstatusViewer.setLabelProvider(new UpdateStatusLabelProvider(\n+\t\t\t\t\tlocalDb, uri));\n+\t\t}\n+\t\ttableViewer.setInput(result);\n+\t\ttablePanel.layout();\n+\t}\n+\n+\tControl getControl() {\n+\t\treturn tablePanel;\n+\t}\n+\n+\tprivate TableViewerColumn createColumn(\n+\t\t\tfinal TableColumnLayout columnLayout, final String text,\n+\t\t\tfinal int weight, final int style) {\n+\t\tfinal TableViewerColumn viewerColumn = new TableViewerColumn(\n+\t\t\t\ttableViewer, style);\n+\t\tfinal TableColumn column = viewerColumn.getColumn();\n+\t\tcolumn.setText(text);\n+\t\tcolumnLayout.setColumnData(column, new ColumnWeightData(weight));\n+\t\treturn viewerColumn;\n+\t}\n+\n+\tprivate class UpdateStatusLabelProvider extends ColumnLabelProvider {\n+\t\tprivate Repository localDb;\n+\n+\t\tprivate final URIish uri;\n+\n+\t\tUpdateStatusLabelProvider(final Repository localDb, final URIish uri) {\n+\t\t\tthis.localDb = localDb;\n+\t\t\tthis.uri = uri;\n+\t\t}\n+\n+\t\t@Override\n+\t\tpublic String getText(Object element) {\n+\t\t\tfinal RefUpdateElement rue = (RefUpdateElement) element;\n+\t\t\tif (!rue.isSuccessfulConnection(uri))\n+\t\t\t\treturn UIText.PushResultTable_statusConnectionFailed;\n+\n+\t\t\tfinal RemoteRefUpdate rru = rue.getRemoteRefUpdate(uri);\n+\t\t\tswitch (rru.getStatus()) {\n+\t\t\tcase OK:\n+\t\t\t\tif (rru.isDelete())\n+\t\t\t\t\treturn UIText.PushResultTable_statusOkDeleted;\n+\n+\t\t\t\tfinal Ref oldRef = rue.getAdvertisedRemoteRef(uri);\n+\t\t\t\tif (oldRef == null) {\n+\t\t\t\t\tif (rue.getDstRefName().startsWith(\n+\t\t\t\t\t\t\tConstants.TAGS_PREFIX + \"/\"))\n+\t\t\t\t\t\treturn UIText.PushResultTable_statusOkNewTag;\n+\t\t\t\t\treturn UIText.PushResultTable_statusOkNewBranch;\n+\t\t\t\t}\n+\n+\t\t\t\treturn oldRef.getObjectId().abbreviate(localDb)\n+\t\t\t\t\t\t+ (rru.isFastForward() ? \"..\" : \"...\")\n+\t\t\t\t\t\t+ rru.getNewObjectId().abbreviate(localDb);\n+\t\t\tcase UP_TO_DATE:\n+\t\t\t\treturn UIText.PushResultTable_statusUpToDate;\n+\t\t\tcase NON_EXISTING:\n+\t\t\t\treturn UIText.PushResultTable_statusNoMatch;\n+\t\t\tcase REJECTED_NODELETE:\n+\t\t\tcase REJECTED_NONFASTFORWARD:\n+\t\t\tcase REJECTED_REMOTE_CHANGED:\n+\t\t\t\treturn UIText.PushResultTable_statusRejected;\n+\t\t\tcase REJECTED_OTHER_REASON:\n+\t\t\t\treturn UIText.PushResultTable_statusRemoteRejected;\n+\t\t\tdefault:\n+\t\t\t\tthrow new IllegalArgumentException(NLS.bind(\n+\t\t\t\t\t\tUIText.PushResultTable_statusUnexpected, rru\n+\t\t\t\t\t\t\t\t.getStatus()));\n+\t\t\t}\n+\t\t}\n+\n+\t\t@Override\n+\t\tpublic Color getBackground(Object element) {\n+\t\t\tfinal RefUpdateElement rue = (RefUpdateElement) element;\n+\t\t\tif (!rue.isSuccessfulConnection(uri))\n+\t\t\t\treturn colorRegistry.get(COLOR_REJECTED_KEY);\n+\n+\t\t\tfinal Status status = rue.getRemoteRefUpdate(uri).getStatus();\n+\t\t\tswitch (status) {\n+\t\t\tcase OK:\n+\t\t\t\treturn colorRegistry.get(COLOR_UPDATED_KEY);\n+\t\t\tcase UP_TO_DATE:\n+\t\t\tcase NON_EXISTING:\n+\t\t\t\treturn colorRegistry.get(COLOR_UP_TO_DATE_KEY);\n+\t\t\tcase REJECTED_NODELETE:\n+\t\t\tcase REJECTED_NONFASTFORWARD:\n+\t\t\tcase REJECTED_REMOTE_CHANGED:\n+\t\t\tcase REJECTED_OTHER_REASON:\n+\t\t\t\treturn colorRegistry.get(COLOR_REJECTED_KEY);\n+\t\t\tdefault:\n+\t\t\t\tthrow new IllegalArgumentException(NLS.bind(\n+\t\t\t\t\t\tUIText.PushResultTable_statusUnexpected, status));\n+\t\t\t}\n+\t\t}\n+\n+\t\t@Override\n+\t\tpublic String getToolTipText(Object element) {\n+\t\t\tfinal RefUpdateElement rue = (RefUpdateElement) element;\n+\t\t\tif (!rue.isSuccessfulConnection(uri))\n+\t\t\t\treturn rue.getErrorMessage(uri);\n+\n+\t\t\tfinal RemoteRefUpdate rru = rue.getRemoteRefUpdate(uri);\n+\t\t\tfinal Ref oldRef = rue.getAdvertisedRemoteRef(uri);\n+\t\t\tswitch (rru.getStatus()) {\n+\t\t\tcase OK:\n+\t\t\t\tif (rru.isDelete())\n+\t\t\t\t\treturn NLS.bind(UIText.PushResultTable_statusDetailDeleted,\n+\t\t\t\t\t\t\toldRef.getObjectId().abbreviate(localDb));\n+\t\t\t\tif (oldRef == null)\n+\t\t\t\t\treturn null;\n+\t\t\t\tif (rru.isFastForward())\n+\t\t\t\t\treturn UIText.PushResultTable_statusDetailFastForward;\n+\t\t\t\treturn UIText.PushResultTable_statusDetailForcedUpdate;\n+\t\t\tcase UP_TO_DATE:\n+\t\t\t\treturn null;\n+\t\t\tcase NON_EXISTING:\n+\t\t\t\treturn UIText.PushResultTable_statusDetailNonExisting;\n+\t\t\tcase REJECTED_NODELETE:\n+\t\t\t\treturn UIText.PushResultTable_statusDetailNoDelete;\n+\t\t\tcase REJECTED_NONFASTFORWARD:\n+\t\t\t\treturn UIText.PushResultTable_statusDetailNonFastForward;\n+\t\t\tcase REJECTED_REMOTE_CHANGED:\n+\t\t\t\tfinal Ref remoteRef = oldRef;\n+\t\t\t\tfinal String currentValue;\n+\t\t\t\tif (remoteRef == null)\n+\t\t\t\t\tcurrentValue = UIText.PushResultTable_refNonExisting;\n+\t\t\t\telse\n+\t\t\t\t\tcurrentValue = remoteRef.getObjectId().abbreviate(localDb);\n+\t\t\t\tfinal ObjectId expectedOldObjectId = rru\n+\t\t\t\t\t\t.getExpectedOldObjectId();\n+\t\t\t\tfinal String expectedValue;\n+\t\t\t\tif (expectedOldObjectId.equals(ObjectId.zeroId()))\n+\t\t\t\t\texpectedValue = UIText.PushResultTable_refNonExisting;\n+\t\t\t\telse\n+\t\t\t\t\texpectedValue = expectedOldObjectId.abbreviate(localDb);\n+\t\t\t\treturn NLS.bind(UIText.PushResultTable_statusDetailChanged,\n+\t\t\t\t\t\tcurrentValue, expectedValue);\n+\t\t\tcase REJECTED_OTHER_REASON:\n+\t\t\t\treturn rru.getMessage();\n+\t\t\tdefault:\n+\t\t\t\tthrow new IllegalArgumentException(NLS.bind(\n+\t\t\t\t\t\tUIText.PushResultTable_statusUnexpected, rru\n+\t\t\t\t\t\t\t\t.getStatus()));\n+\t\t\t}\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/PushWizard.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/PushWizard.java\nnew file mode 100644\nindex 0000000..44060df\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/PushWizard.java\n@@ -0,0 +1,250 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.push;\n+\n+import java.io.IOException;\n+import java.lang.reflect.InvocationTargetException;\n+import java.net.URISyntaxException;\n+import java.util.Collection;\n+import java.util.List;\n+\n+import org.eclipse.core.runtime.IProgressMonitor;\n+import org.eclipse.core.runtime.IStatus;\n+import org.eclipse.core.runtime.Status;\n+import org.eclipse.core.runtime.jobs.Job;\n+import org.eclipse.jface.dialogs.Dialog;\n+import org.eclipse.jface.dialogs.ErrorDialog;\n+import org.eclipse.jface.wizard.IWizardPage;\n+import org.eclipse.jface.wizard.Wizard;\n+import org.eclipse.osgi.util.NLS;\n+import org.eclipse.swt.widgets.Shell;\n+import org.eclipse.ui.PlatformUI;\n+import org.spearce.egit.core.op.PushOperation;\n+import org.spearce.egit.core.op.PushOperationResult;\n+import org.spearce.egit.core.op.PushOperationSpecification;\n+import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIIcons;\n+import org.spearce.egit.ui.UIText;\n+import org.spearce.egit.ui.internal.components.RefSpecPage;\n+import org.spearce.egit.ui.internal.components.RepositorySelection;\n+import org.spearce.egit.ui.internal.components.RepositorySelectionPage;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.lib.RepositoryConfig;\n+import org.spearce.jgit.transport.RefSpec;\n+import org.spearce.jgit.transport.RemoteConfig;\n+import org.spearce.jgit.transport.RemoteRefUpdate;\n+import org.spearce.jgit.transport.Transport;\n+import org.spearce.jgit.transport.URIish;\n+\n+/**\n+ * Wizard allowing user to specify all needed data to push to another repository\n+ * - including selection of remote repository and refs specifications.\n+ * <p>\n+ * Push operation is performed upon successful completion of this wizard.\n+ */\n+public class PushWizard extends Wizard {\n+\tprivate static String getURIsString(final Collection<URIish> uris) {\n+\t\tfinal StringBuilder sb = new StringBuilder();\n+\t\tboolean first = true;\n+\t\tfor (final URIish uri : uris) {\n+\t\t\tif (first)\n+\t\t\t\tfirst = false;\n+\t\t\telse\n+\t\t\t\tsb.append(\", \"); //$NON-NLS-1$\n+\t\t\tsb.append(uri);\n+\t\t}\n+\t\treturn sb.toString();\n+\t}\n+\n+\tprivate Repository localDb;\n+\n+\tprivate final RepositorySelectionPage repoPage;\n+\n+\tprivate final RefSpecPage refSpecPage;\n+\n+\tprivate ConfirmationPage confirmPage;\n+\n+\t/**\n+\t * Create push wizard for specified local repository.\n+\t * \n+\t * @param localDb\n+\t *            repository to push from.\n+\t * @throws URISyntaxException\n+\t *             when configuration of this repository contains illegal URIs.\n+\t */\n+\tpublic PushWizard(final Repository localDb) throws URISyntaxException {\n+\t\tthis.localDb = localDb;\n+\t\tfinal List<RemoteConfig> remotes = RemoteConfig\n+\t\t\t\t.getAllRemoteConfigs(localDb.getConfig());\n+\t\trepoPage = new RepositorySelectionPage(false, remotes);\n+\t\trefSpecPage = new RefSpecPage(localDb, true, repoPage);\n+\t\tconfirmPage = new ConfirmationPage(localDb, repoPage, refSpecPage);\n+\t\t// TODO use/create another cool icon\n+\t\tsetDefaultPageImageDescriptor(UIIcons.WIZBAN_IMPORT_REPO);\n+\t\tsetNeedsProgressMonitor(true);\n+\t}\n+\n+\t@Override\n+\tpublic void addPages() {\n+\t\taddPage(repoPage);\n+\t\taddPage(refSpecPage);\n+\t\taddPage(confirmPage);\n+\t}\n+\n+\t@Override\n+\tpublic boolean performFinish() {\n+\t\tif (repoPage.getSelection().isConfigSelected()\n+\t\t\t\t&& refSpecPage.isSaveRequested()) {\n+\t\t\tsaveRefSpecs();\n+\t\t}\n+\n+\t\tfinal PushOperation operation = createPushOperation();\n+\t\tif (operation == null)\n+\t\t\treturn false;\n+\t\tfinal PushOperationResult resultToCompare;\n+\t\tif (confirmPage.isShowOnlyIfChangedSelected())\n+\t\t\tresultToCompare = confirmPage.getConfirmedResult();\n+\t\telse\n+\t\t\tresultToCompare = null;\n+\t\tfinal Job job = new PushJob(operation, resultToCompare,\n+\t\t\t\tgetDestinationString());\n+\n+\t\tjob.setUser(true);\n+\t\tjob.schedule();\n+\n+\t\treturn true;\n+\t}\n+\n+\t@Override\n+\tpublic String getWindowTitle() {\n+\t\tfinal IWizardPage currentPage = getContainer().getCurrentPage();\n+\t\tif (currentPage == repoPage || currentPage == null)\n+\t\t\treturn UIText.PushWizard_windowTitleDefault;\n+\t\tfinal String destination = getDestinationString();\n+\t\treturn NLS.bind(UIText.PushWizard_windowTitleWithDestination,\n+\t\t\t\tdestination);\n+\t}\n+\n+\tprivate void saveRefSpecs() {\n+\t\tfinal RemoteConfig rc = repoPage.getSelection().getConfig();\n+\t\trc.setPushRefSpecs(refSpecPage.getRefSpecs());\n+\t\tfinal RepositoryConfig config = localDb.getConfig();\n+\t\trc.update(config);\n+\t\ttry {\n+\t\t\tconfig.save();\n+\t\t} catch (final IOException e) {\n+\t\t\tErrorDialog.openError(getShell(), UIText.PushWizard_cantSaveTitle,\n+\t\t\t\t\tUIText.PushWizard_cantSaveMessage, new Status(\n+\t\t\t\t\t\t\tIStatus.WARNING, Activator.getPluginId(), e\n+\t\t\t\t\t\t\t\t\t.getMessage(), e));\n+\t\t\t// Continue, it's not critical.\n+\t\t}\n+\t}\n+\n+\tprivate PushOperation createPushOperation() {\n+\t\ttry {\n+\t\t\tfinal PushOperationSpecification spec;\n+\t\t\tfinal RemoteConfig config = repoPage.getSelection().getConfig();\n+\t\t\tif (confirmPage.isConfirmed()) {\n+\t\t\t\tfinal PushOperationResult confirmedResult = confirmPage\n+\t\t\t\t\t\t.getConfirmedResult();\n+\t\t\t\tspec = confirmedResult.deriveSpecification(confirmPage\n+\t\t\t\t\t\t.isRequireUnchangedSelected());\n+\t\t\t} else {\n+\t\t\t\tfinal Collection<RefSpec> fetchSpecs;\n+\t\t\t\tif (config != null)\n+\t\t\t\t\tfetchSpecs = config.getFetchRefSpecs();\n+\t\t\t\telse\n+\t\t\t\t\tfetchSpecs = null;\n+\n+\t\t\t\tfinal Collection<RemoteRefUpdate> updates = Transport\n+\t\t\t\t\t\t.findRemoteRefUpdatesFor(localDb, refSpecPage\n+\t\t\t\t\t\t\t\t.getRefSpecs(), fetchSpecs);\n+\t\t\t\tif (updates.isEmpty()) {\n+\t\t\t\t\tErrorDialog.openError(getShell(),\n+\t\t\t\t\t\t\tUIText.PushWizard_missingRefsTitle, null,\n+\t\t\t\t\t\t\tnew Status(IStatus.ERROR, Activator.getPluginId(),\n+\t\t\t\t\t\t\t\t\tUIText.PushWizard_missingRefsMessage));\n+\t\t\t\t\treturn null;\n+\t\t\t\t}\n+\n+\t\t\t\tspec = new PushOperationSpecification();\n+\t\t\t\tfor (final URIish uri : repoPage.getSelection().getAllURIs())\n+\t\t\t\t\tspec.addURIRefUpdates(uri, ConfirmationPage\n+\t\t\t\t\t\t\t.copyUpdates(updates));\n+\t\t\t}\n+\t\t\treturn new PushOperation(localDb, spec, false, config);\n+\t\t} catch (final IOException e) {\n+\t\t\tErrorDialog.openError(getShell(),\n+\t\t\t\t\tUIText.PushWizard_cantPrepareUpdatesTitle,\n+\t\t\t\t\tUIText.PushWizard_cantPrepareUpdatesMessage, new Status(\n+\t\t\t\t\t\t\tIStatus.ERROR, Activator.getPluginId(), e\n+\t\t\t\t\t\t\t\t\t.getMessage(), e));\n+\t\t\treturn null;\n+\t\t}\n+\t}\n+\n+\tprivate String getDestinationString() {\n+\t\tfinal RepositorySelection repoSelection = repoPage.getSelection();\n+\t\tfinal String destination;\n+\t\tif (repoSelection.isConfigSelected())\n+\t\t\tdestination = repoSelection.getConfigName();\n+\t\telse\n+\t\t\tdestination = repoSelection.getURI().toString();\n+\t\treturn destination;\n+\t}\n+\n+\tprivate class PushJob extends Job {\n+\t\tprivate final PushOperation operation;\n+\n+\t\tprivate final PushOperationResult resultToCompare;\n+\n+\t\tprivate final String destinationString;\n+\n+\t\tpublic PushJob(final PushOperation operation,\n+\t\t\t\tfinal PushOperationResult resultToCompare,\n+\t\t\t\tfinal String destinationString) {\n+\t\t\tsuper(NLS.bind(UIText.PushWizard_jobName, getURIsString(operation\n+\t\t\t\t\t.getSpecification().getURIs())));\n+\t\t\tthis.operation = operation;\n+\t\t\tthis.resultToCompare = resultToCompare;\n+\t\t\tthis.destinationString = destinationString;\n+\t\t}\n+\n+\t\t@Override\n+\t\tprotected IStatus run(final IProgressMonitor monitor) {\n+\t\t\ttry {\n+\t\t\t\toperation.run(monitor);\n+\t\t\t} catch (final InvocationTargetException e) {\n+\t\t\t\treturn new Status(IStatus.ERROR, Activator.getPluginId(),\n+\t\t\t\t\t\tUIText.PushWizard_unexpectedError, e.getCause());\n+\t\t\t}\n+\n+\t\t\tfinal PushOperationResult result = operation.getOperationResult();\n+\t\t\tif (!result.isSuccessfulConnectionForAnyURI()) {\n+\t\t\t\treturn new Status(IStatus.ERROR, Activator.getPluginId(), NLS\n+\t\t\t\t\t\t.bind(UIText.PushWizard_cantConnectToAny, result\n+\t\t\t\t\t\t\t\t.getErrorStringForAllURis()));\n+\t\t\t}\n+\n+\t\t\tif (resultToCompare == null || !result.equals(resultToCompare)) {\n+\t\t\t\tPlatformUI.getWorkbench().getDisplay().asyncExec(\n+\t\t\t\t\t\tnew Runnable() {\n+\t\t\t\t\t\t\tpublic void run() {\n+\t\t\t\t\t\t\t\tfinal Shell shell = PlatformUI.getWorkbench()\n+\t\t\t\t\t\t\t\t\t\t.getActiveWorkbenchWindow().getShell();\n+\t\t\t\t\t\t\t\tfinal Dialog dialog = new ResultDialog(shell,\n+\t\t\t\t\t\t\t\t\t\tlocalDb, result, destinationString);\n+\t\t\t\t\t\t\t\tdialog.open();\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t});\n+\t\t\t}\n+\t\t\treturn Status.OK_STATUS;\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/RefUpdateContentProvider.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/RefUpdateContentProvider.java\nnew file mode 100644\nindex 0000000..ccc5340\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/RefUpdateContentProvider.java\n@@ -0,0 +1,62 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.push;\n+\n+import java.util.SortedMap;\n+import java.util.TreeMap;\n+import java.util.Map.Entry;\n+\n+import org.eclipse.jface.viewers.IStructuredContentProvider;\n+import org.eclipse.jface.viewers.Viewer;\n+import org.spearce.egit.core.op.PushOperationResult;\n+import org.spearce.jgit.transport.RemoteRefUpdate;\n+import org.spearce.jgit.transport.URIish;\n+\n+/**\n+ * Content provided for push result table viewer.\n+ * <p>\n+ * Input of this provided must be {@link PushOperationResult} instance, while\n+ * returned elements are instances of {@link RefUpdateElement}.\n+ * \n+ * @see PushOperationResult\n+ * @see RefUpdateElement\n+ */\n+class RefUpdateContentProvider implements IStructuredContentProvider {\n+\tpublic Object[] getElements(final Object inputElement) {\n+\t\tfinal PushOperationResult result = (PushOperationResult) inputElement;\n+\n+\t\tfinal SortedMap<String, String> dstToSrc = new TreeMap<String, String>();\n+\t\tfor (final URIish uri : result.getURIs()) {\n+\t\t\tif (result.isSuccessfulConnection(uri)) {\n+\t\t\t\tfor (final RemoteRefUpdate rru : result.getPushResult(uri)\n+\t\t\t\t\t\t.getRemoteUpdates())\n+\t\t\t\t\tdstToSrc.put(rru.getRemoteName(), rru.getSrcRef());\n+\t\t\t\t// Assuming that each repository received the same ref updates,\n+\t\t\t\t// we need only one to get these ref names.\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\n+\t\t// Transforming PushOperationResult model to row-wise one.\n+\t\tfinal RefUpdateElement elements[] = new RefUpdateElement[dstToSrc\n+\t\t\t\t.size()];\n+\t\tint i = 0;\n+\t\tfor (final Entry<String, String> entry : dstToSrc.entrySet())\n+\t\t\telements[i++] = new RefUpdateElement(result, entry.getValue(),\n+\t\t\t\t\tentry.getKey());\n+\t\treturn elements;\n+\t}\n+\n+\tpublic void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\n+\t\t// nothing to do\n+\t}\n+\n+\tpublic void dispose() {\n+\t\t// nothing to dispose\n+\t}\n+}\n\\ No newline at end of file\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/RefUpdateElement.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/RefUpdateElement.java\nnew file mode 100644\nindex 0000000..7b9bce0\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/RefUpdateElement.java\n@@ -0,0 +1,67 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.push;\n+\n+import org.spearce.egit.core.op.PushOperationResult;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.transport.RemoteRefUpdate;\n+import org.spearce.jgit.transport.URIish;\n+\n+/**\n+ * Data class representing row (element) of table with push results.\n+ * <p>\n+ * Each row is associated with one ref update, while each column is associated\n+ * with one URI (remote repository).\n+ * \n+ * @see PushOperationResult\n+ * @see RefUpdateContentProvider\n+ */\n+class RefUpdateElement {\n+\tprivate final String srcRefName;\n+\n+\tprivate final String dstRefName;\n+\n+\tprivate final PushOperationResult result;\n+\n+\tRefUpdateElement(final PushOperationResult result, final String srcRef,\n+\t\t\tfinal String dstRef) {\n+\t\tthis.result = result;\n+\t\tthis.srcRefName = srcRef;\n+\t\tthis.dstRefName = dstRef;\n+\t}\n+\n+\tString getSrcRefName() {\n+\t\treturn srcRefName;\n+\t}\n+\n+\tString getDstRefName() {\n+\t\treturn dstRefName;\n+\t}\n+\n+\tboolean isDelete() {\n+\t\t// Assuming that we never use ObjectId.zeroId() in GUI.\n+\t\t// (no need to compare to it).\n+\t\treturn srcRefName == null;\n+\t}\n+\t\n+\tboolean isSuccessfulConnection(final URIish uri) {\n+\t\treturn result.isSuccessfulConnection(uri);\n+\t}\n+\n+\tString getErrorMessage(final URIish uri) {\n+\t\treturn result.getErrorMessage(uri);\n+\t}\n+\n+\tRemoteRefUpdate getRemoteRefUpdate(final URIish uri) {\n+\t\treturn result.getPushResult(uri).getRemoteUpdate(dstRefName);\n+\t}\n+\n+\tRef getAdvertisedRemoteRef(final URIish uri) {\n+\t\treturn result.getPushResult(uri).getAdvertisedRef(dstRefName);\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/ResultDialog.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/ResultDialog.java\nnew file mode 100644\nindex 0000000..920494f\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/push/ResultDialog.java\n@@ -0,0 +1,65 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.push;\n+\n+import org.eclipse.jface.dialogs.Dialog;\n+import org.eclipse.jface.dialogs.IDialogConstants;\n+import org.eclipse.osgi.util.NLS;\n+import org.eclipse.swt.SWT;\n+import org.eclipse.swt.layout.GridData;\n+import org.eclipse.swt.widgets.Composite;\n+import org.eclipse.swt.widgets.Control;\n+import org.eclipse.swt.widgets.Label;\n+import org.eclipse.swt.widgets.Shell;\n+import org.spearce.egit.core.op.PushOperationResult;\n+import org.spearce.egit.ui.UIText;\n+import org.spearce.jgit.lib.Repository;\n+\n+class ResultDialog extends Dialog {\n+\tprivate final Repository localDb;\n+\n+\tprivate final PushOperationResult result;\n+\n+\tprivate final String destinationString;\n+\n+\tResultDialog(final Shell parentShell, final Repository localDb,\n+\t\t\tfinal PushOperationResult result, final String destinationString) {\n+\t\tsuper(parentShell);\n+\t\tsetShellStyle(getShellStyle() | SWT.RESIZE);\n+\t\tthis.localDb = localDb;\n+\t\tthis.result = result;\n+\t\tthis.destinationString = destinationString;\n+\t}\n+\n+\t@Override\n+\tprotected void createButtonsForButtonBar(final Composite parent) {\n+\t\tcreateButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,\n+\t\t\t\ttrue);\n+\t}\n+\n+\t@Override\n+\tprotected Control createDialogArea(final Composite parent) {\n+\t\tfinal Composite composite = (Composite) super.createDialogArea(parent);\n+\n+\t\tfinal Label label = new Label(composite, SWT.NONE);\n+\t\tlabel.setText(NLS.bind(UIText.ResultDialog_label, destinationString));\n+\t\tlabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));\n+\t\tfinal PushResultTable table = new PushResultTable(composite);\n+\t\ttable.setData(localDb, result);\n+\t\tfinal Control tableControl = table.getControl();\n+\t\tfinal GridData tableLayout = new GridData(SWT.FILL, SWT.FILL, true,\n+\t\t\t\ttrue);\n+\t\ttableLayout.widthHint = 650;\n+\t\ttableLayout.heightHint = 300;\n+\t\ttableControl.setLayoutData(tableLayout);\n+\n+\t\tgetShell().setText(\n+\t\t\t\tNLS.bind(UIText.ResultDialog_title, destinationString));\n+\t\treturn composite;\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex f2be3c1..4b0aea6 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -179,6 +179,53 @@ HistoryPage_findbar_notFound=String not found\n \n HistoryPreferencePage_title=Git\n \n+PushWizard_cantConnectToAny=Can't connect to any repository: {0}\n+PushWizard_cantPrepareUpdatesMessage=Can't resolve ref specifications locally (local refs changed?) or create tracking ref update.\n+PushWizard_cantPrepareUpdatesTitle=Preparing Ref Updates Error\n+PushWizard_cantSaveMessage=Couldn't save specified specifications in configuration file.\n+PushWizard_cantSaveTitle=Configuration Storage Warning\n+PushWizard_jobName=Pushing to...{0}\n+PushWizard_missingRefsMessage=Ref specifications don't match any source ref (local refs changed?).\n+PushWizard_missingRefsTitle=Missing Refs Error\n+PushWizard_unexpectedError=Unexpected error occurred.\n+PushWizard_windowTitleDefault=Push To Another Repositories\n+PushWizard_windowTitleWithDestination=Push To: {0}\n+\n+ConfirmationPage_cantConnectToAny=Can't connect to any URI: {0}\n+ConfirmationPage_description=Confirm following expected push result.\n+ConfirmationPage_errorCantResolveSpecs=Can't resolve ref specifications locally or create tracking ref update: {0} \n+ConfirmationPage_errorInterrupted=Operation was interrupted.\n+ConfirmationPage_errorRefsChangedNoMatch=Local refs changed, ref specifications don't match any source ref.\n+ConfirmationPage_errorUnexpected=Unexpected error occurred: {0}\n+ConfirmationPage_requireUnchangedButton=Push only if remote refs don't change in the mean time\n+ConfirmationPage_showOnlyIfChanged=Show final report dialog only when it differs from this confirmation report\n+ConfirmationPage_title=Push Confirmation\n+\n+PushResultTable_columnStatusRepo=Status: Repo #{0}\n+PushResultTable_columnDst=Destination Ref\n+PushResultTable_columnSrc=Source Ref\n+PushResultTable_columnMode=Mode\n+PushResultTable_statusUnexpected=Unexpected update status: {0} \n+PushResultTable_statusConnectionFailed=[connection failed]\n+PushResultTable_statusDetailChanged=remote ref object changed,\\nnow it's: {0},\\nnot expected: {1}\n+PushResultTable_refNonExisting=(non existing)\n+PushResultTable_statusDetailDeleted=old value: {0} \n+PushResultTable_statusDetailNonFastForward=non-fast forward\n+PushResultTable_statusDetailNoDelete=remote side does not support deleting refs\n+PushResultTable_statusDetailNonExisting=remote ref already does not exist\n+PushResultTable_statusDetailForcedUpdate=forced update (non-fast forward)\n+PushResultTable_statusDetailFastForward=fast forward\n+PushResultTable_statusRemoteRejected=[remote rejected]\n+PushResultTable_statusRejected=[rejected]\n+PushResultTable_statusNoMatch=[no match]\n+PushResultTable_statusUpToDate=[up to date]\n+PushResultTable_statusOkDeleted=[deleted]\n+PushResultTable_statusOkNewBranch=[new branch]\n+PushResultTable_statusOkNewTag=[new tag]\n+\t\n+ResultDialog_title=Push Results: {0}\n+ResultDialog_label=Pushed to {0}.\n+\n WindowCachePreferencePage_title=Git Window Cache\n WindowCachePreferencePage_packedGitWindowSize=Window size:\n WindowCachePreferencePage_packedGitLimit=Window cache limit:\n-- \n1.5.6.3\n"},{"id":"87712","messageId":"20080819162816.GF20947@spearce.org","threadId":"15059","inReplyTo":"1219005852-21496-9-git-send-email-marek.zawirski@gmail.com","subject":"Re: [EGIT PATCH 08/31] Add dryRun option to Transport and console push","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-19T16:28:16Z","receivedAt":"2008-08-19T16:28:16Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Marek Zawirski <marek.zawirski@gmail.com> wrote:\n> Implementation of C Git --dry-run behavior for push operation.\n> It allows investigating possible push result, while not performing real\n> push operation - not updating remote refs.\n \n> diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n> index a952309..f5b24c6 100644\n> --- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n> +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n> @@ -85,6 +85,9 @@ class Push extends TextBuiltin {\n>  \n>  \t@Option(name = \"--receive-pack\", metaVar = \"path\")\n>  \tprivate String receivePack;\n> +\t\n> +\t@Option(name = \"--dry-run\")\n> +\tprivate boolean dryRun = Transport.DEFAULT_DRY_RUN;\n\nHaving a boolean constant DEFAULT_DRY_RUN = false is sort of\noverkill in my opinion.  Most people would assume a boolean with\nno value assigned and not marked final will default to false.\nSetting something like dryRun to false by default is reasonable,\nas you usually want it to actually execute.\n\nWhat's worse though is if one day someone changes\nTransport.DEFAULT_DRY_RUN = true in the library.  When that happens\nthe push command line tool will never function as there is no\n\"--no-dry-run\" to set dryRun = false.\n\nSo this assignment is likely not a good idea.\n\n-- \nShawn.\n"},{"id":"87713","messageId":"20080819164518.GG20947@spearce.org","threadId":"15059","inReplyTo":"1219005852-21496-13-git-send-email-marek.zawirski@gmail.com","subject":"Re: [EGIT PATCH 12/31] Add another RemoteRefUpdate constructor, useful for 2-stage push","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-19T16:45:18Z","receivedAt":"2008-08-19T16:45:18Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Marek Zawirski <marek.zawirski@gmail.com> wrote:\n> New constructor base on existing RemoteRefUpdate instance, providing\n> deep copy of object, but allowing change of expectedOldObjectId.\n \n> @@ -133,6 +133,8 @@ public class RemoteRefUpdate {\n>  \n>  \tprivate String message;\n>  \n> +\tprivate final Repository db;\n> +\n\nI think we should document that this is the *local* (source)\nrepository and not the remote (dest) repository.\n\n-- \nShawn.\n"},{"id":"87722","messageId":"20080819175931.GH20947@spearce.org","threadId":"15059","inReplyTo":"1219005852-21496-1-git-send-email-marek.zawirski@gmail.com","subject":"Re: [EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-19T17:59:31Z","receivedAt":"2008-08-19T17:59:31Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Marek Zawirski <marek.zawirski@gmail.com> wrote:\n> Robin, Tor, I know that you were already contributing some graphics to\n> egit. If someone of you would like to do some icon for push/fetch with\n> pleasure, you are welcome. Otherwise I'll have to do some crappy icon\n> instead ;) Another matter are checkboxes screenshots. I'm not sure\n> about legality status of including them. Any ideas if/how we co use\n> them or some another set that we can for sure?\n\nSo my office-mate suggests that the output of a program (in this\ncase the checkbox icon) isn't covered by the same copyright as the\nprogram that created it.  So we may be OK.  Or we just use some\nother sort of icon for the checkbox.  Maybe Tor would be able to\ncome up with something useful here?\n\nAnyway, most of this series looks pretty good to me.  I found a\nfew other annoying bugs in JGit and EGit as a result of testing\nthis series, but they are unrelated to the series and have been\nthere all along.  So I may try to fix them in the coming weeks.\n \n>  55 files changed, 6471 insertions(+), 981 deletions(-)\n\nHeh.  Been busy, hmm?  ;-)\n\n-- \nShawn.\n"},{"id":"87725","messageId":"200808192025.00001.robin.rosenberg.lists@dewire.com","threadId":"15059","inReplyTo":"1219005852-21496-29-git-send-email-marek.zawirski@gmail.com","subject":"Re: [EGIT PATCH 28/31] Checkbox images/screenshots","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-08-19T18:24:59Z","receivedAt":"2008-08-19T18:24:59Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"söndagen den 17 augusti 2008 22.44.09 skrev Marek Zawirski:\n> Screenshots of checkboxes in various states, made at QT with Plastik\n> style. These images may be used as workaround in cases when we can\n> only display images and handle buttons pressed, but can't display Button\n> directly.\n> \n> I'm somewhat uncertain about license issues regarding these images.\n> Recently, I realized that they are LGPL licensed perhaps. Is it possible\n> to license that small stuff? If so, we may have to replace them by some\n> other images or create ours.\n\n\"Stealing\" artwork is not good, though there are bordercases like this. It\nis also the wrong look on my box which uses check marks instead of X.\n\nAre all options exhauested wrt to using the native L&F, commented out in \npatch 31. For example toucher caching of generated images so mac users\nwould only see less flicker. I see no problems with GTK when using the\nout-commented portion. Windows (XP) seems fine too. A really ugly, codewise,\nwould be to detect OSX and use custom images only for it until we find a\nfix.\n\n-- robin\n"},{"id":"87742","messageId":"200808192121.30372.robin.rosenberg.lists@dewire.com","threadId":"15059","inReplyTo":"20080819175931.GH20947@spearce.org","subject":"Re: [EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-08-19T19:21:30Z","receivedAt":"2008-08-19T19:21:30Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"tisdagen den 19 augusti 2008 19.59.31 skrev Shawn O. Pearce:\n> Marek Zawirski <marek.zawirski@gmail.com> wrote:\n> > Robin, Tor, I know that you were already contributing some graphics to\n> > egit. If someone of you would like to do some icon for push/fetch with\n> > pleasure, you are welcome. Otherwise I'll have to do some crappy icon\n> > instead ;) Another matter are checkboxes screenshots. I'm not sure\n> > about legality status of including them. Any ideas if/how we co use\n> > them or some another set that we can for sure?\n> \n> So my office-mate suggests that the output of a program (in this\n> case the checkbox icon) isn't covered by the same copyright as the\n> program that created it.  So we may be OK. \nIt's not that simple. That obviously may be the case, but I'm certain\nit does not apply to individual well defined pieces of artwork (or text for\nthat matter) included in the output. \n\n> Or we just use some \n> other sort of icon for the checkbox.  Maybe Tor would be able to\n> come up with something useful here?\nMost annoying is that we'd need one (four) for each LAF supported.\n\n> \n> Anyway, most of this series looks pretty good to me.  I found a\n> few other annoying bugs in JGit and EGit as a result of testing\n> this series, but they are unrelated to the series and have been\n> there all along.  So I may try to fix them in the coming weeks.\n>  \n> >  55 files changed, 6471 insertions(+), 981 deletions(-)\n> \n> Heh.  Been busy, hmm?  ;-)\n\nThe weather in and around the Baltic Sea has been horrible for much\nof August so far. Maybe that helped him. :)\n\n-- robin\n"},{"id":"87784","messageId":"48AB84A2.7010905@gmail.com","threadId":"15059","inReplyTo":"200808192121.30372.robin.rosenberg.lists@dewire.com","subject":"Re: [EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-20T02:42:42Z","receivedAt":"2008-08-20T02:42:42Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Robin Rosenberg wrote:\n> tisdagen den 19 augusti 2008 19.59.31 skrev Shawn O. Pearce:\n>> Marek Zawirski <marek.zawirski@gmail.com> wrote:\n>>> Robin, Tor, I know that you were already contributing some graphics to\n>>> egit. If someone of you would like to do some icon for push/fetch with\n>>> pleasure, you are welcome. Otherwise I'll have to do some crappy icon\n>>> instead ;) Another matter are checkboxes screenshots. I'm not sure\n>>> about legality status of including them. Any ideas if/how we co use\n>>> them or some another set that we can for sure?\n>> So my office-mate suggests that the output of a program (in this\n>> case the checkbox icon) isn't covered by the same copyright as the\n>> program that created it.  So we may be OK. \n> It's not that simple. That obviously may be the case, but I'm certain\n> it does not apply to individual well defined pieces of artwork (or text for\n> that matter) included in the output. \n> \n>> Or we just use some \n>> other sort of icon for the checkbox.  Maybe Tor would be able to\n>> come up with something useful here?\n> Most annoying is that we'd need one (four) for each LAF supported.\n\nWell, consider that they are even more of them. GTK+ has configurable \nthemes (in my case it is delegation to QT with Plastik theme, where \nscreenshot was made). I don't know about other platforms, but always \nhaving hyper-UI Mac OS X and Windows Vista may also have some \ncustomization options.\nNo matter how icons we would prepare, always there will be somebody who \ncan say that these icons are somehow inconsistent with his/her LAF.\n\nSo I think, it'd better to have this mentioned ugly code workaround just \nfor Mac OS (only there flashing window is so annoying). Less people \nsuffering.\n\nOr as Shawn said, we can try to make our own funny icon for Force Update \n(red \"F\"-enabled and gray \"F\"-disabled?) that is equally (in)consistent \nfor everybody.\n\n>> Anyway, most of this series looks pretty good to me.  I found a\n>> few other annoying bugs in JGit and EGit as a result of testing\n>> this series, but they are unrelated to the series and have been\n>> there all along.  So I may try to fix them in the coming weeks.\n\nNice testing then, as I have spend hours on testing UI tricks and \nhaven't noticed more bugs;) Beside of some indexing problems in old API.\n\n>>>  55 files changed, 6471 insertions(+), 981 deletions(-)\n>> Heh.  Been busy, hmm?  ;-)\n> \n> The weather in and around the Baltic Sea has been horrible for much\n> of August so far. Maybe that helped him. :)\n\nI swear guys, I wasn't sailing for over 1 month;)\nActually, I'm now leaving for few days, so I'm sending out just fixes \nfor issues pointed out by Shawn and found by me in the mean time. I'll \nsquash these patches with existing commits in push branch, as it is \nprobably worth nothing to keep this in history.\n\nFetch UI and dynamic team menu entries for push and fetch (for \nconfigured remotes) are underway, I'll come back to them after coming \nback home.\n\nBTW, as another developers are getting involved in jgit/egit coding, \nmaybe we could use (update) some wiki page for marking who is working on \nsome topic currently? Now it's not obvious for me, and as we're not so \nnumerous it would be pity to waste our time and do some redundant stuff \none day.\n\n-- \nMarek Zawirski [zawir]\nmarek.zawirski@gmail.com\n"},{"id":"87786","messageId":"1219201060-4307-1-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"48AB84A2.7010905@gmail.com","subject":"[EGIT PATCH 1/6] Remove DEFAULT_DRY_RUN constant from Transport","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-20T02:57:35Z","receivedAt":"2008-08-20T02:57:35Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"This constant was pointless as changing it would brake code in the same\nway as changing hardcoded value instead.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/jgit/pgm/Push.java             |    2 +-\n .../src/org/spearce/jgit/transport/Transport.java  |    7 +------\n 2 files changed, 2 insertions(+), 7 deletions(-)\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\nindex b61071c..d36bf4f 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n@@ -87,7 +87,7 @@ void nothin(final boolean ignored) {\n \tprivate String receivePack;\n \t\n \t@Option(name = \"--dry-run\")\n-\tprivate boolean dryRun = Transport.DEFAULT_DRY_RUN;\n+\tprivate boolean dryRun;\n \n \tprivate boolean shownURI;\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\nindex 939347e..ca68ca6 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Transport.java\n@@ -313,11 +313,6 @@ private static String findTrackingRefName(final String remoteName,\n \tpublic static final RefSpec REFSPEC_PUSH_ALL = new RefSpec(\n \t\t\t\"refs/heads/*:refs/heads/*\");\n \n-\t/**\n-\t * Default setting for {@link #dryRun} option.\n-\t */\n-\tpublic static final boolean DEFAULT_DRY_RUN = false;\n-\n \t/** The repository this transport fetches into, or pushes out of. */\n \tprotected final Repository local;\n \n@@ -354,7 +349,7 @@ private static String findTrackingRefName(final String remoteName,\n \tprivate boolean pushThin = DEFAULT_PUSH_THIN;\n \n \t/** Should push just check for operation result, not really push. */\n-\tprivate boolean dryRun = DEFAULT_DRY_RUN;\n+\tprivate boolean dryRun;\n \n \t/**\n \t * Create a new transport instance.\n-- \n1.5.6.3\n"},{"id":"87788","messageId":"1219201060-4307-2-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219201060-4307-1-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 2/6] Emphasize that db is a local one in RemoteRefUpdate","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-20T02:57:36Z","receivedAt":"2008-08-20T02:57:36Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../spearce/jgit/transport/RemoteRefUpdate.java    |   20 ++++++++++----------\n 1 files changed, 10 insertions(+), 10 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java\nindex 42588c1..66fe6a1 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java\n@@ -133,15 +133,15 @@\n \n \tprivate String message;\n \n-\tprivate final Repository db;\n+\tprivate final Repository localDb;\n \n \t/**\n \t * Construct remote ref update request by providing an update specification.\n \t * Object is created with default {@link Status#NOT_ATTEMPTED} status and no\n \t * message.\n-\t *\n-\t * @param db\n-\t *            repository to push from.\n+\t * \n+\t * @param localDb\n+\t *            local repository to push from.\n \t * @param srcRef\n \t *            source revision - any string resolvable by\n \t *            {@link Repository#resolve(String)}. This resolves to the new\n@@ -173,26 +173,26 @@\n \t * @throws IllegalArgumentException\n \t *             if some required parameter was null\n \t */\n-\tpublic RemoteRefUpdate(final Repository db, final String srcRef,\n+\tpublic RemoteRefUpdate(final Repository localDb, final String srcRef,\n \t\t\tfinal String remoteName, final boolean forceUpdate,\n \t\t\tfinal String localName, final ObjectId expectedOldObjectId)\n \t\t\tthrows IOException {\n \t\tif (remoteName == null)\n \t\t\tthrow new IllegalArgumentException(\"Remote name can't be null.\");\n \t\tthis.srcRef = srcRef;\n-\t\tthis.newObjectId = (srcRef == null ? ObjectId.zeroId() : db\n+\t\tthis.newObjectId = (srcRef == null ? ObjectId.zeroId() : localDb\n \t\t\t\t.resolve(srcRef));\n \t\tif (newObjectId == null)\n \t\t\tthrow new IOException(\"Source ref \" + srcRef\n \t\t\t\t\t+ \" doesn't resolve to any object.\");\n \t\tthis.remoteName = remoteName;\n \t\tthis.forceUpdate = forceUpdate;\n-\t\tif (localName != null && db != null)\n-\t\t\ttrackingRefUpdate = new TrackingRefUpdate(db, localName,\n+\t\tif (localName != null && localDb != null)\n+\t\t\ttrackingRefUpdate = new TrackingRefUpdate(localDb, localName,\n \t\t\t\t\tremoteName, forceUpdate, newObjectId, \"push\");\n \t\telse\n \t\t\ttrackingRefUpdate = null;\n-\t\tthis.db = db;\n+\t\tthis.localDb = localDb;\n \t\tthis.expectedOldObjectId = expectedOldObjectId;\n \t\tthis.status = Status.NOT_ATTEMPTED;\n \t}\n@@ -215,7 +215,7 @@ public RemoteRefUpdate(final Repository db, final String srcRef,\n \t */\n \tpublic RemoteRefUpdate(final RemoteRefUpdate base,\n \t\t\tfinal ObjectId newExpectedOldObjectId) throws IOException {\n-\t\tthis(base.db, base.srcRef, base.remoteName, base.forceUpdate,\n+\t\tthis(base.localDb, base.srcRef, base.remoteName, base.forceUpdate,\n \t\t\t\t(base.trackingRefUpdate == null ? null : base.trackingRefUpdate\n \t\t\t\t\t\t.getLocalName()), newExpectedOldObjectId);\n \t}\n-- \n1.5.6.3\n"},{"id":"87787","messageId":"1219201060-4307-3-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219201060-4307-2-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 3/6] Handle URIs parsing errors in PushAction better","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-20T02:57:37Z","receivedAt":"2008-08-20T02:57:37Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"ErrorDialog is used instead of MessageDialog (more information), strings\nare externalized.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |    6 ++++++\n .../egit/ui/internal/actions/PushAction.java       |   13 +++++++++----\n .../src/org/spearce/egit/ui/uitext.properties      |    3 +++\n 3 files changed, 18 insertions(+), 4 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex cc785f7..b45d2e9 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -464,6 +464,12 @@\n \tpublic static String HistoryPreferencePage_title;\n \n \t/** */\n+\tpublic static String PushAction_wrongURIDescription;\n+\n+\t/** */\n+\tpublic static String PushAction_wrongURITitle;\n+\n+\t/** */\n \tpublic static String PushWizard_cantConnectToAny;\n \n \t/** */\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/PushAction.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/PushAction.java\nindex 61833d0..b4af3b5 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/PushAction.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/PushAction.java\n@@ -9,9 +9,13 @@\n \n import java.net.URISyntaxException;\n \n+import org.eclipse.core.runtime.IStatus;\n+import org.eclipse.core.runtime.Status;\n import org.eclipse.jface.action.IAction;\n-import org.eclipse.jface.dialogs.MessageDialog;\n+import org.eclipse.jface.dialogs.ErrorDialog;\n import org.eclipse.jface.wizard.WizardDialog;\n+import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIText;\n import org.spearce.egit.ui.internal.push.PushWizard;\n import org.spearce.jgit.lib.Repository;\n \n@@ -31,9 +35,10 @@ public void run(IAction action) {\n \t\ttry {\n \t\t\tpushWizard = new PushWizard(repository);\n \t\t} catch (URISyntaxException x) {\n-\t\t\tMessageDialog.openError(getShell(), \"Corrupted configuration\",\n-\t\t\t\t\t\"Remote repositories URLs configuration is corrupted: \"\n-\t\t\t\t\t\t\t+ x.getMessage());\n+\t\t\tErrorDialog.openError(getShell(), UIText.PushAction_wrongURITitle,\n+\t\t\t\t\tUIText.PushAction_wrongURIDescription, new Status(\n+\t\t\t\t\t\t\tIStatus.ERROR, Activator.getPluginId(), x\n+\t\t\t\t\t\t\t\t\t.getMessage(), x));\n \t\t\treturn;\n \t\t}\n \t\tfinal WizardDialog dialog = new WizardDialog(getShell(), pushWizard);\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex 4b0aea6..83be622 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -179,6 +179,9 @@ HistoryPage_findbar_notFound=String not found\n \n HistoryPreferencePage_title=Git\n \n+PushAction_wrongURIDescription=Remote repositories URIs configuration is corrupted.\n+PushAction_wrongURITitle=Corrupted Configuration\n+\n PushWizard_cantConnectToAny=Can't connect to any repository: {0}\n PushWizard_cantPrepareUpdatesMessage=Can't resolve ref specifications locally (local refs changed?) or create tracking ref update.\n PushWizard_cantPrepareUpdatesTitle=Preparing Ref Updates Error\n-- \n1.5.6.3\n"},{"id":"87789","messageId":"1219201060-4307-4-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219201060-4307-3-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 4/6] Fix proposal provider for fetch in RefSpecPanel","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-20T02:57:38Z","receivedAt":"2008-08-20T02:57:38Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Proposal provider for destination fields in fetch was inappropriately\nproposing illegal local expression like HEAD^, while we should propose\nthere only refs or wildcard expressions.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../egit/ui/internal/components/RefSpecPanel.java  |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPanel.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPanel.java\nindex caef4d2..1621434 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPanel.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPanel.java\n@@ -323,7 +323,7 @@ private static void setControlDecoration(final ControlDecoration control,\n \t */\n \tpublic RefSpecPanel(final Composite parent, final boolean pushSpecs) {\n \t\tthis.pushSpecs = pushSpecs;\n-\t\tthis.localProposalProvider = new RefContentProposalProvider(true);\n+\t\tthis.localProposalProvider = new RefContentProposalProvider(pushSpecs);\n \t\tthis.remoteProposalProvider = new RefContentProposalProvider(false);\n \t\tthis.imageRegistry = new ImageRegistry(parent.getDisplay());\n \n-- \n1.5.6.3\n"},{"id":"87791","messageId":"1219201060-4307-5-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219201060-4307-4-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 5/6] Fix disappearing \"save configuration\" label in RefSpecPage","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-20T02:57:39Z","receivedAt":"2008-08-20T02:57:39Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"If we call only pack() after controls were layed out, label may disappear\non dialog resize. So we probably need to relayout whole panel when label\ncontent changes.\n\nSigned-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../egit/ui/internal/components/RefSpecPage.java   |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java\nindex 4471e24..b526cf3 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java\n@@ -209,7 +209,7 @@ private void revalidateImpl(final RepositorySelection newRepoSelection) {\n \t\t\tsaveButton.setVisible(true);\n \t\t\tsaveButton.setText(NLS.bind(UIText.RefSpecPage_saveSpecifications,\n \t\t\t\t\tremoteName));\n-\t\t\tsaveButton.pack();\n+\t\t\tsaveButton.getParent().layout();\n \t\t}\n \t\tcheckPage();\n \t}\n-- \n1.5.6.3\n"},{"id":"87790","messageId":"1219201060-4307-6-git-send-email-marek.zawirski@gmail.com","threadId":"15059","inReplyTo":"1219201060-4307-5-git-send-email-marek.zawirski@gmail.com","subject":"[EGIT PATCH 6/6] Fix RefSpecPage formatting","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-20T02:57:40Z","receivedAt":"2008-08-20T02:57:40Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>\n---\n .../egit/ui/internal/components/RefSpecPage.java   |    3 ++-\n 1 files changed, 2 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java\nindex b526cf3..70856ca 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/components/RefSpecPage.java\n@@ -191,7 +191,8 @@ private void revalidateImpl(final RepositorySelection newRepoSelection) {\n \t\t} catch (InvocationTargetException e) {\n \t\t\tfinal Throwable cause = e.getCause();\n \t\t\ttransportError(cause.getMessage());\n-\t\t\tErrorDialog.openError(getShell(), UIText.RefSpecPage_errorTransportDialogTitle,\n+\t\t\tErrorDialog.openError(getShell(),\n+\t\t\t\t\tUIText.RefSpecPage_errorTransportDialogTitle,\n \t\t\t\t\tUIText.RefSpecPage_errorTransportDialogMessage, new Status(\n \t\t\t\t\t\t\tIStatus.ERROR, Activator.getPluginId(), 0, cause\n \t\t\t\t\t\t\t\t\t.getMessage(), cause));\n-- \n1.5.6.3\n"},{"id":"87822","messageId":"20080820141326.GA3483@spearce.org","threadId":"15059","inReplyTo":"48AB84A2.7010905@gmail.com","subject":"Re: [EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-20T14:13:26Z","receivedAt":"2008-08-20T14:13:26Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Marek Zawirski <marek.zawirski@gmail.com> wrote:\n> Actually, I'm now leaving for few days, so I'm sending out just fixes  \n> for issues pointed out by Shawn and found by me in the mean time. I'll  \n> squash these patches with existing commits in push branch, as it is  \n> probably worth nothing to keep this in history.\n\nThanks.  These fixes look good to me, and they address my immediate\nconcerns with the series.\n\n> BTW, as another developers are getting involved in jgit/egit coding,  \n> maybe we could use (update) some wiki page for marking who is working on  \n> some topic currently? Now it's not obvious for me, and as we're not so  \n> numerous it would be pity to waste our time and do some redundant stuff  \n> one day.\n\nI've thought about starting a code.google.com project just to use\nthe issue tracking system there.  I'm using an internal tool to\nkeep of issues for myself, but that's not fair to the end-users or\nother contributors...\n\n-- \nShawn.\n"},{"id":"87823","messageId":"20080820152305.GJ10544@machine.or.cz","threadId":"15059","inReplyTo":"20080820141326.GA3483@spearce.org","subject":"Git-aware Issue Tracking?","fromName":"Petr Baudis","fromEmail":"pasky@suse.cz","sentAt":"2008-08-20T15:23:05Z","receivedAt":"2008-08-20T15:23:05Z","isPatch":false,"sender":{"key":"pasky@ucw.cz","avatar":"https://avatars.githubusercontent.com/u/18439?v=4"},"body":"On Wed, Aug 20, 2008 at 07:13:26AM -0700, Shawn O. Pearce wrote:\n> I've thought about starting a code.google.com project just to use\n> the issue tracking system there.  I'm using an internal tool to\n> keep of issues for myself, but that's not fair to the end-users or\n> other contributors...\n\nI have been thinking about issue tracking for some of my projects too,\nbut I'm wondering, does anyone have a comprehensive picture of the state\nof the Git-supporting issue tracking tools, especially those that keep\nthe tracked issues in a Git repository as well?\n\n\thttp://git.or.cz/gitwiki/InterfacesFrontendsAndTools#head-73b23f376ebd0222d1e4b08f09158172aa34c24f\n\nhas three, but two of them are in Ruby, which is rather discouraging.\nBut Cil (in Perl) is already \"self-hosting\", so it might be well usable?\n\n-- \n\t\t\t\tPetr \"Pasky\" Baudis\nThe next generation of interesting software will be done\non the Macintosh, not the IBM PC.  -- Bill Gates\n"},{"id":"87826","messageId":"20080820164409.GH3483@spearce.org","threadId":"15059","inReplyTo":"20080820152305.GJ10544@machine.or.cz","subject":"Re: Git-aware Issue Tracking?","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-20T16:44:09Z","receivedAt":"2008-08-20T16:44:09Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Petr Baudis <pasky@suse.cz> wrote:\n> On Wed, Aug 20, 2008 at 07:13:26AM -0700, Shawn O. Pearce wrote:\n> > I've thought about starting a code.google.com project just to use\n> > the issue tracking system there.\n> \n> I have been thinking about issue tracking for some of my projects too,\n> but I'm wondering, does anyone have a comprehensive picture of the state\n> of the Git-supporting issue tracking tools, especially those that keep\n> the tracked issues in a Git repository as well?\n> \n> \thttp://git.or.cz/gitwiki/InterfacesFrontendsAndTools#head-73b23f376ebd0222d1e4b08f09158172aa34c24f\n> \n> has three, but two of them are in Ruby, which is rather discouraging.\n> But Cil (in Perl) is already \"self-hosting\", so it might be well usable?\n\nCil is interesting.  I'm concerned about keeping the state in tree\nwith the repository though in a distributed development team.\n\nIf I mark the status of an issue in a branch that isn't ready\nfor mainline how do I share that status update with everyone else?\nI have to put it into a branch somewhere, no big deal.  repo.or.cz is\npretty good at publishing things.\n\nBut do that now for 5 developers working on 10 or 20 different\nbranches at once.  We'll have status updates all over the place\nand Marek's desire to see what we are each working on (to reduce\nwasted effort and perhaps help each other out more) still isn't met.\n\nThis is the number one reason a DIT (distributed issue tracker)\nisn't available.  Nobody has solved the hard technical problem of\nmaking it easy to distribute the state changes, yet still provide\na reasonably current global view of the issue status.\n\nPerhaps running Cil in its own egit-cil.git repository would get\nus what we neeed.  I looked at the code and its pretty clean,\nbut I didn't see how merges of the .cil database work.\n\n-- \nShawn.\n"},{"id":"87846","messageId":"m3fxozsepa.fsf@localhost.localdomain","threadId":"15059","inReplyTo":"20080820152305.GJ10544@machine.or.cz","subject":"Re: Git-aware Issue Tracking?","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2008-08-20T18:52:24Z","receivedAt":"2008-08-20T18:52:24Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Petr Baudis <pasky@suse.cz> writes:\n\n> On Wed, Aug 20, 2008 at 07:13:26AM -0700, Shawn O. Pearce wrote:\n> > I've thought about starting a code.google.com project just to use\n> > the issue tracking system there.  I'm using an internal tool to\n> > keep of issues for myself, but that's not fair to the end-users or\n> > other contributors...\n> \n> I have been thinking about issue tracking for some of my projects too,\n> but I'm wondering, does anyone have a comprehensive picture of the state\n> of the Git-supporting issue tracking tools, especially those that keep\n> the tracked issues in a Git repository as well?\n> \n> \thttp://git.or.cz/gitwiki/InterfacesFrontendsAndTools#head-73b23f376ebd0222d1e4b08f09158172aa34c24f\n> \n> has three, but two of them are in Ruby, which is rather discouraging.\n> But Cil (in Perl) is already \"self-hosting\", so it might be well usable?\n\nThere is also Bugs Everywhere, written in Python, which supposedly has\n(some form of) Git support:\n  http://git.or.cz/gitwiki/InterfacesFrontendsAndToolsWishlist#be\n\nThere was also 'grit' by Pierre Habouzit, also in Python, which got\nabandoned and removed (also from wiki):\nhttp://git.or.cz/gitwiki/InterfacesFrontendsAndTools?action=diff&rev2=203&rev1=202\nIf I remember correcly Pierre promised to write down what he learned\nabout distributed bug tracking from his work on grit[*1*], when he had\na bit of free time, but I don't remember him doing it...\n\nSee also \"[RFC] git integrated bugtracking\" thread on git mailing list\n(http://thread.gmane.org/gmane.comp.version-control.git/48981) where\ngrit started, and later \"[RFC] Idea for Git Bugtracking Tool\"\n(http://thread.gmane.org/gmane.comp.version-control.git/76411), where\nyou can find different distributed bug trackers, not all of them\nthought supporting Git.\n\nHTH.\n\nFootnotes:\n==========\n[*1*] http://thread.gmane.org/gmane.comp.version-control.git/76411/focus=76565\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"87865","messageId":"618c07250808201322r7422116bw2b6bb106412b50dc@mail.gmail.com","threadId":"15059","inReplyTo":"20080820152305.GJ10544@machine.or.cz","subject":"Re: Git-aware Issue Tracking?","fromName":"Mike Dalessio","fromEmail":"mike.dalessio@gmail.com","sentAt":"2008-08-20T20:22:29Z","receivedAt":"2008-08-20T20:22:29Z","isPatch":false,"sender":{"key":"mike.dalessio@gmail.com","avatar":null},"body":"On Wed, Aug 20, 2008 at 11:23 AM, Petr Baudis <pasky@suse.cz> wrote:\n> I have been thinking about issue tracking for some of my projects too,\n> but I'm wondering, does anyone have a comprehensive picture of the state\n> of the Git-supporting issue tracking tools, especially those that keep\n> the tracked issues in a Git repository as well?\n\nI haven't played with it, but this looks like it's along the lines of\nwhat you're discussing:\n\n     http://github.com/jwiegley/git-issues/tree/master\n\nIt apparently uses an unchecked-out branch in the project to manage its data,\nwhich is stored in a hash directory structure similar to .git/objects.\n"},{"id":"87868","messageId":"200808202225.43331.robin.rosenberg.lists@dewire.com","threadId":"15059","inReplyTo":"20080820164409.GH3483@spearce.org","subject":"Re: Git-aware Issue Tracking?","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-08-20T20:25:40Z","receivedAt":"2008-08-20T20:25:40Z","isPatch":false,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"onsdagen den 20 augusti 2008 18.44.09 skrev Shawn O. Pearce:\n> Petr Baudis <pasky@suse.cz> wrote:\n> > On Wed, Aug 20, 2008 at 07:13:26AM -0700, Shawn O. Pearce wrote:\n> > > I've thought about starting a code.google.com project just to use\n> > > the issue tracking system there.\n> > \n> > I have been thinking about issue tracking for some of my projects too,\n> > but I'm wondering, does anyone have a comprehensive picture of the state\n> > of the Git-supporting issue tracking tools, especially those that keep\n> > the tracked issues in a Git repository as well?\n> > \n> > \thttp://git.or.cz/gitwiki/InterfacesFrontendsAndTools#head-73b23f376ebd0222d1e4b08f09158172aa34c24f\n> > \n> > has three, but two of them are in Ruby, which is rather discouraging.\n> > But Cil (in Perl) is already \"self-hosting\", so it might be well usable?\n> \n> Cil is interesting.  I'm concerned about keeping the state in tree\n> with the repository though in a distributed development team.\n> \n> If I mark the status of an issue in a branch that isn't ready\n> for mainline how do I share that status update with everyone else?\n> I have to put it into a branch somewhere, no big deal.  repo.or.cz is\n> pretty good at publishing things.\n> \n> But do that now for 5 developers working on 10 or 20 different\n> branches at once.  We'll have status updates all over the place\n> and Marek's desire to see what we are each working on (to reduce\n> wasted effort and perhaps help each other out more) still isn't met.\n> \n> This is the number one reason a DIT (distributed issue tracker)\n> isn't available.  Nobody has solved the hard technical problem of\n> making it easy to distribute the state changes, yet still provide\n> a reasonably current global view of the issue status.\n\nActually his name is Mik Kersten, the main figure behind Mylyn.\nMylyn is a plugin for Eclipse that actually does this. It connects to \nbugzilla, trac and som other trackers and caches changes locally\nuntil it connect to the website and syncrhronize. It does a lot more,\nthan that. The objectives partially overlap what Git does. Having\nMylin and Git work together would be very sweet indeed.\n\n> Perhaps running Cil in its own egit-cil.git repository would get\n> us what we neeed.  I looked at the code and its pretty clean,\n> but I didn't see how merges of the .cil database work.\n\n-- robin\n"},{"id":"87951","messageId":"20080821083042.GA6517@artemis.corp","threadId":"15059","inReplyTo":"m3fxozsepa.fsf@localhost.localdomain","subject":"Re: Git-aware Issue Tracking?","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2008-08-21T08:30:42Z","receivedAt":"2008-08-21T08:30:42Z","isPatch":false,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"On Wed, Aug 20, 2008 at 06:52:24PM +0000, Jakub Narebski wrote:\n> Petr Baudis <pasky@suse.cz> writes:\n> \n> > On Wed, Aug 20, 2008 at 07:13:26AM -0700, Shawn O. Pearce wrote:\n> > > I've thought about starting a code.google.com project just to use\n> > > the issue tracking system there.  I'm using an internal tool to\n> > > keep of issues for myself, but that's not fair to the end-users or\n> > > other contributors...\n> > \n> > I have been thinking about issue tracking for some of my projects too,\n> > but I'm wondering, does anyone have a comprehensive picture of the state\n> > of the Git-supporting issue tracking tools, especially those that keep\n> > the tracked issues in a Git repository as well?\n> > \n> > \thttp://git.or.cz/gitwiki/InterfacesFrontendsAndTools#head-73b23f376ebd0222d1e4b08f09158172aa34c24f\n> > \n> > has three, but two of them are in Ruby, which is rather discouraging.\n> > But Cil (in Perl) is already \"self-hosting\", so it might be well usable?\n> \n> There is also Bugs Everywhere, written in Python, which supposedly has\n> (some form of) Git support:\n>   http://git.or.cz/gitwiki/InterfacesFrontendsAndToolsWishlist#be\n> \n> There was also 'grit' by Pierre Habouzit, also in Python, which got\n> abandoned and removed (also from wiki):\n> http://git.or.cz/gitwiki/InterfacesFrontendsAndTools?action=diff&rev2=203&rev1=202\n> If I remember correcly Pierre promised to write down what he learned\n> about distributed bug tracking from his work on grit[*1*], when he had\n> a bit of free time, but I don't remember him doing it...\n\n  Actually there is a list, bugs-dist@kitenet.net or sth similar where I\ndid it. One should look at the archives of that list. Though it's\nsomehow dead again.\n\n-- \n·O·  Pierre Habouzit\n··O                                                madcoder@debian.org\nOOO                                                http://www.madism.org\n"},{"id":"87956","messageId":"46a038f90808210147pa3087a5k5bd5804e36bdcc13@mail.gmail.com","threadId":"15059","inReplyTo":"20080820164409.GH3483@spearce.org","subject":"Re: Git-aware Issue Tracking?","fromName":"Martin Langhoff","fromEmail":"martin.langhoff@gmail.com","sentAt":"2008-08-21T08:47:12Z","receivedAt":"2008-08-21T08:47:12Z","isPatch":false,"sender":{"key":"martin.langhoff@gmail.com","avatar":"https://gravatar.com/avatar/1e3f311b6c4c15836501901ca58f8c0b0667246488084ba524d8bc9867e22fd9?d=mp&s=160"},"body":"On Thu, Aug 21, 2008 at 4:44 AM, Shawn O. Pearce <spearce@spearce.org> wrote:\n> Cil is interesting.  I'm concerned about keeping the state in tree\n> with the repository though in a distributed development team.\n\nI'm not a Cil user, but I work with the original dev, and a few people\naround here use it. Perhaps Andy can help answer questions about it...\n\ncheers,\n\n\n\nm\n-- \n martin.langhoff@gmail.com\n martin@laptop.org -- School Server Architect\n - ask interesting questions\n - don't get distracted with shiny stuff - working code first\n - http://wiki.laptop.org/go/User:Martinlanghoff\n"},{"id":"87958","messageId":"7bfdc29a0808210155h48598bev5b9901b14d2d7cf1@mail.gmail.com","threadId":"15059","inReplyTo":"20080820152305.GJ10544@machine.or.cz","subject":"Re: Git-aware Issue Tracking?","fromName":"Imran M Yousuf","fromEmail":"imyousuf@gmail.com","sentAt":"2008-08-21T08:55:42Z","receivedAt":"2008-08-21T08:55:42Z","isPatch":false,"sender":{"key":"imyousuf@gmail.com","avatar":"https://gravatar.com/avatar/fda3c870262849d03c7b9c4d288842e128d6d80769fa7bc2d22731b7597928be?d=mp&s=160"},"body":"On Wed, Aug 20, 2008 at 9:23 PM, Petr Baudis <pasky@suse.cz> wrote:\n> On Wed, Aug 20, 2008 at 07:13:26AM -0700, Shawn O. Pearce wrote:\n>> I've thought about starting a code.google.com project just to use\n>> the issue tracking system there.  I'm using an internal tool to\n>> keep of issues for myself, but that's not fair to the end-users or\n>> other contributors...\n>\n> I have been thinking about issue tracking for some of my projects too,\n> but I'm wondering, does anyone have a comprehensive picture of the state\n> of the Git-supporting issue tracking tools, especially those that keep\n> the tracked issues in a Git repository as well?\n>\n>        http://git.or.cz/gitwiki/InterfacesFrontendsAndTools#head-73b23f376ebd0222d1e4b08f09158172aa34c24f\n>\n> has three, but two of them are in Ruby, which is rather discouraging.\n> But Cil (in Perl) is already \"self-hosting\", so it might be well usable?\n\nI would also like to see FishEye support git. They have a issue for it\nwhere they have asked interested personnel to place their comment or\nvote.\n\nhttp://jira.atlassian.com/browse/FE-337\n\nIntegration with FishEye would mean integration with JIRA as well.\nIMO, that would be cool!\n\nBest regards,\n\nImran\n\n>\n> --\n>                                Petr \"Pasky\" Baudis\n> The next generation of interesting software will be done\n> on the Macintosh, not the IBM PC.  -- Bill Gates\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n>\n\n\n\n-- \nImran M Yousuf\nEmail: imran@smartitengineering.com\nBlog: http://imyousuf-tech.blogs.smartitengineering.com/\nMobile: +880-1711402557\n"},{"id":"87960","messageId":"200808211123.26372.jnareb@gmail.com","threadId":"15059","inReplyTo":"20080821083042.GA6517@artemis.corp","subject":"Re: Git-aware Issue Tracking?","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2008-08-21T09:23:25Z","receivedAt":"2008-08-21T09:23:25Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Thu, 21 Aug 2008, Pierre Habouzit wrote:\n\n> Actually there is a list, bugs-dist@kitenet.net or sth similar where\n> I did it. One should look at the archives of that list. Though it's\n> somehow dead again.\n\nArchives are at http://kitenet.net/pipermail/dist-bugs/ for what its\nworth (only 39 messages there).\n\n-- \nJakub Narebski\nPoland\n"},{"id":"88028","messageId":"200808212212.11184.robin.rosenberg.lists@dewire.com","threadId":"15059","inReplyTo":"48AB84A2.7010905@gmail.com","subject":"Re: [EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-08-21T20:12:10Z","receivedAt":"2008-08-21T20:12:10Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"onsdagen den 20 augusti 2008 04.42.42 skrev Marek Zawirski:\n> Actually, I'm now leaving for few days, so I'm sending out just fixes \n> for issues pointed out by Shawn and found by me in the mean time. I'll \n> squash these patches with existing commits in push branch, as it is \n> probably worth nothing to keep this in history.\n\nAs I'm too eager to get this out (seems to work), I can squash it and\nrearrange the checkbox-related code a little like stealing the icons\nfrom the Eclipse CVS and grouping the checkbox classes with the\nicons plus a tweak for selecting how the images for the checkbox\nare created.\n\n-- robin\n"},{"id":"88031","messageId":"20080821201636.GZ3483@spearce.org","threadId":"15059","inReplyTo":"200808212212.11184.robin.rosenberg.lists@dewire.com","subject":"Re: [EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-21T20:16:36Z","receivedAt":"2008-08-21T20:16:36Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:\n> onsdagen den 20 augusti 2008 04.42.42 skrev Marek Zawirski:\n> > Actually, I'm now leaving for few days, so I'm sending out just fixes \n> > for issues pointed out by Shawn and found by me in the mean time. I'll \n> > squash these patches with existing commits in push branch, as it is \n> > probably worth nothing to keep this in history.\n> \n> As I'm too eager to get this out (seems to work), I can squash it and\n> rearrange the checkbox-related code a little like stealing the icons\n> from the Eclipse CVS and grouping the checkbox classes with the\n> icons plus a tweak for selecting how the images for the checkbox\n> are created.\n\nYea.  Go for it.  Reusing icons from Eclipse CVS is a good way\naround the icon issue.\n\n-- \nShawn.\n"},{"id":"88056","messageId":"200808220125.31990.robin.rosenberg.lists@dewire.com","threadId":"15059","inReplyTo":"20080821201636.GZ3483@spearce.org","subject":"Re: [EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2008-08-21T23:25:31Z","receivedAt":"2008-08-21T23:25:31Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"torsdagen den 21 augusti 2008 22.16.36 skrev Shawn O. Pearce:\n> Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:\n> > onsdagen den 20 augusti 2008 04.42.42 skrev Marek Zawirski:\n> > > Actually, I'm now leaving for few days, so I'm sending out just fixes \n> > > for issues pointed out by Shawn and found by me in the mean time. I'll \n> > > squash these patches with existing commits in push branch, as it is \n> > > probably worth nothing to keep this in history.\n> > \n> > As I'm too eager to get this out (seems to work), I can squash it and\n> > rearrange the checkbox-related code a little like stealing the icons\n> > from the Eclipse CVS and grouping the checkbox classes with the\n> > icons plus a tweak for selecting how the images for the checkbox\n> > are created.\n> \n> Yea.  Go for it.  Reusing icons from Eclipse CVS is a good way\n> around the icon issue.\n\nI pushed the cleanups to pu using the Eclipse plugin(!) built from that version. \nYou may want to take a quick look before we declare it master. \n\nNice work Marek!\n\n-- robin\n"},{"id":"88077","messageId":"20080822030525.GA30182@spearce.org","threadId":"15059","inReplyTo":"200808220125.31990.robin.rosenberg.lists@dewire.com","subject":"Re: [EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-22T03:05:25Z","receivedAt":"2008-08-22T03:05:25Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:\n> \n> I pushed the cleanups to pu using the Eclipse plugin(!) built from that version. \n> You may want to take a quick look before we declare it master. \n\nYay!  I'll take a quick look at the series tomorrow, and if\nit looks good to me too I'll merge it over to master.  But I'm\nnot going to spend much time on EGit tomorrow if I can help it.\nI need to focus on some work related stuff, and the Git in HTTP\nRPC protocol specification.\n \n-- \nShawn.\n"},{"id":"88457","messageId":"48B2BAD1.5030909@gmail.com","threadId":"15059","inReplyTo":"20080820141326.GA3483@spearce.org","subject":"Re: [EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-25T13:59:45Z","receivedAt":"2008-08-25T13:59:45Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Shawn O. Pearce wrote:\n>> BTW, as another developers are getting involved in jgit/egit coding,  \n>> maybe we could use (update) some wiki page for marking who is working on  \n>> some topic currently? Now it's not obvious for me, and as we're not so  \n>> numerous it would be pity to waste our time and do some redundant stuff  \n>> one day.\n> \n> I've thought about starting a code.google.com project just to use\n> the issue tracking system there.  I'm using an internal tool to\n> keep of issues for myself, but that's not fair to the end-users or\n> other contributors...\n\nThat seems to be nice idea.\nThe only downside I see is that using code.coogle.com with Mylyn may be \nhard[1]. As if we start to use issues system, it may be nice opportunity \nto start using Mylyn - if anybody want to?\n\n[1] http://www.jroller.com/alexRuiz/entry/using_mylyn_with_google_code\n\n-- \nMarek Zawirski [zawir]\nmarek.zawirski@gmail.com\n"},{"id":"88459","messageId":"48B2BE1E.2020503@gmail.com","threadId":"15059","inReplyTo":"200808220125.31990.robin.rosenberg.lists@dewire.com","subject":"Re: [EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Marek Zawirski","fromEmail":"marek.zawirski@gmail.com","sentAt":"2008-08-25T14:13:50Z","receivedAt":"2008-08-25T14:13:50Z","isPatch":true,"sender":{"key":"marek.zawirski@gmail.com","avatar":null},"body":"Robin Rosenberg wrote:\n> torsdagen den 21 augusti 2008 22.16.36 skrev Shawn O. Pearce:\n>> Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:\n>>> onsdagen den 20 augusti 2008 04.42.42 skrev Marek Zawirski:\n>>>> Actually, I'm now leaving for few days, so I'm sending out just fixes \n>>>> for issues pointed out by Shawn and found by me in the mean time. I'll \n>>>> squash these patches with existing commits in push branch, as it is \n>>>> probably worth nothing to keep this in history.\n>>> As I'm too eager to get this out (seems to work), I can squash it and\n>>> rearrange the checkbox-related code a little like stealing the icons\n>>> from the Eclipse CVS and grouping the checkbox classes with the\n>>> icons plus a tweak for selecting how the images for the checkbox\n>>> are created.\n>> Yea.  Go for it.  Reusing icons from Eclipse CVS is a good way\n>> around the icon issue.\n\nI'm back and looked at this solution - it is probably the best \nworkaround for now, thanks!\n\n> I pushed the cleanups to pu using the Eclipse plugin(!) built from that version. \n> You may want to take a quick look before we declare it master. \n> \n> Nice work Marek!\n\nNice to hear that:)\n\n-- \nMarek Zawirski [zawir]\nmarek.zawirski@gmail.com\n"},{"id":"88458","messageId":"20080825141506.GB25654@spearce.org","threadId":"15059","inReplyTo":"48B2BE1E.2020503@gmail.com","subject":"Re: [EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-25T14:15:06Z","receivedAt":"2008-08-25T14:15:06Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Marek Zawirski <marek.zawirski@gmail.com> wrote:\n> Robin Rosenberg wrote:\n>\n>> I pushed the cleanups to pu using the Eclipse plugin(!) built from that \n>> version. You may want to take a quick look before we declare it master. \n>> \n>>\n>> Nice work Marek!\n>\n> Nice to hear that:)\n\nI used the plugin to push itself to master.  Your changes are in\nthe main tree.  Yay!\n\n-- \nShawn.\n"},{"id":"88461","messageId":"20080825142417.GC25654@spearce.org","threadId":"15059","inReplyTo":"48B2BAD1.5030909@gmail.com","subject":"Re: [EGIT PATCH 00/31] Push GUI, GUI improvements, various jgit stuff","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-25T14:24:17Z","receivedAt":"2008-08-25T14:24:17Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Marek Zawirski <marek.zawirski@gmail.com> wrote:\n> Shawn O. Pearce wrote:\n>>\n>> I've thought about starting a code.google.com project just to use\n>> the issue tracking system there.\n>\n> That seems to be nice idea.\n> The only downside I see is that using code.coogle.com with Mylyn may be  \n> hard[1]. As if we start to use issues system, it may be nice opportunity  \n> to start using Mylyn - if anybody want to?\n>\n> [1] http://www.jroller.com/alexRuiz/entry/using_mylyn_with_google_code\n\nA comment there suggests its gotten slightly better:\n\nPosted by Christopher Barber on July 17, 2008 at 02:26 PM PDT:\n> There was a regression introduced in the 3.0 version of mylyn,\n> but it has been fixed in 3.0.1, which was just released.\n> \n> Also, since Google Code now supports csv output, you are probably\n> better off changing your Query URL to:\n> \n> ${serverUrl}/csv?can=1&colspec=ID+Status+Type+Owner+Summary\n> \n> and your Query Pattern to:\n> \n> \"({Id}[0-9]+?)\",\"({Status}.+?)\",\"({Type}.+?)\",\"({Owner}.+?)\",\"({Description}.+?)\"\\n\n> \n> This should provide a faster sync, since it minimizes the amount\n> of bytes downloaded.\n\nI may give it a try later today.\n\nI also know some of the folks behind code.google.com.  I can poke\nthem to see if Mylyn support is going to be coming along in the\nnear future.  Google uses Eclipse a lot internally, and we have\na number of open source projects using code.google.com as their\nhosting/issue tracking, and Eclipse as their IDE.  We probably\nshould be making the issue tracking product more accessible to IDEs.\n\n-- \nShawn.\n"}]}