diff options
author | Jens Baumgart | 2010-11-30 10:13:28 +0000 |
---|---|---|
committer | Matthias Sohn | 2010-11-30 14:35:45 +0000 |
commit | cfb8c6e0d57e09fa6013c2c27bb679df12455bc8 (patch) | |
tree | d488eacddceb3a4031a51867d0ea7ba99cd549c6 /org.eclipse.egit.ui/src/org | |
parent | e9f2af26088f25a43db8c890b4be8ea641ff546b (diff) | |
download | egit-cfb8c6e0d57e09fa6013c2c27bb679df12455bc8.tar.gz egit-cfb8c6e0d57e09fa6013c2c27bb679df12455bc8.tar.xz egit-cfb8c6e0d57e09fa6013c2c27bb679df12455bc8.zip |
Implement basic authentification
Implementation of basic authentification with user and password.
User and password entered in the clone wizard are put in the Eclipse
secure store. A credentials provider was implemented that fetches
credentials from the secure store and asks the user if credentials are
not available.
Change-Id: I5239c6595e39d2a855318649fccc8fe8ac5e5fb8
Signed-off-by: Jens Baumgart <jens.baumgart@sap.com>
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Diffstat (limited to 'org.eclipse.egit.ui/src/org')
9 files changed, 393 insertions, 2 deletions
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java index 711cf07180..6b6c255da1 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java @@ -36,6 +36,7 @@ import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.egit.core.RepositoryUtil; import org.eclipse.egit.core.project.RepositoryMapping; +import org.eclipse.egit.ui.credentials.EGitCredentialsProvider; import org.eclipse.egit.ui.internal.trace.GitTraceLocation; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; @@ -44,6 +45,7 @@ import org.eclipse.jgit.events.IndexChangedListener; import org.eclipse.jgit.events.ListenerHandle; import org.eclipse.jgit.events.RepositoryEvent; import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.SshSessionFactory; import org.eclipse.jsch.core.IJSchService; import org.eclipse.osgi.service.debug.DebugOptions; @@ -187,6 +189,11 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener setupRepoChangeScanner(); setupRepoIndexRefresh(); setupFocusHandling(); + setupCredentialsProvider(); + } + + private void setupCredentialsProvider() { + CredentialsProvider.setDefault(new EGitCredentialsProvider()); } static boolean isActive() { diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java index 6bb222176d..8d12bf351e 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java @@ -169,6 +169,9 @@ public class UIText extends NLS { public static String GenerateHistoryJob_errorComputingHistory; /** */ + public static String EGitCredentialsProvider_errorReadingCredentials; + + /** */ public static String EgitUiUtils_CouldNotOpenEditorMessage; /** */ @@ -217,6 +220,9 @@ public class UIText extends NLS { public static String GitCloneWizard_errorCannotCreate; /** */ + public static String GitCloneWizard_writeToSecureStoreFailed; + + /** */ public static String GitDecoratorPreferencePage_bindingRepositoryNameFlag; /** */ @@ -595,6 +601,9 @@ public class UIText extends NLS { public static String RepositorySearchDialog_SomeDirectoriesHiddenMessage; /** */ + public static String ChangeCredentialsCommand_writingToSecureStoreFailed; + + /** */ public static String CheckoutHandler_SelectBranchMessage; /** */ @@ -2507,6 +2516,21 @@ public class UIText extends NLS { public static String LocalFileRevision_currentVersionTag; /** */ + public static String LoginDialog_changeCredentials; + + /** */ + public static String LoginDialog_login; + + /** */ + public static String LoginDialog_password; + + /** */ + public static String LoginDialog_repository; + + /** */ + public static String LoginDialog_user; + + /** */ public static String NewRemoteWizard_CheckingUriTaskName; /** */ diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/credentials/EGitCredentialsProvider.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/credentials/EGitCredentialsProvider.java new file mode 100644 index 0000000000..87078965a6 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/credentials/EGitCredentialsProvider.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (C) 2010, Jens Baumgart <jens.baumgart@sap.com> + * Copyright (C) 2010, Edwin Kempin <edwin.kempin@sap.com> + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.egit.ui.credentials; + +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.egit.core.Activator; +import org.eclipse.egit.core.securestorage.UserPasswordCredentials; +import org.eclipse.egit.ui.UIText; +import org.eclipse.equinox.security.storage.StorageException; +import org.eclipse.jgit.errors.UnsupportedCredentialItem; +import org.eclipse.jgit.transport.CredentialItem; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.URIish; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PlatformUI; + +/** + * This class implements a {@link CredentialsProvider} for EGit. The provider + * tries to retrieve the credentials (user, password) for a given URI from the + * secure store. A login popup is shown if no credentials are available. + */ +public class EGitCredentialsProvider extends CredentialsProvider { + + @Override + public boolean isInteractive() { + return true; + } + + @Override + public boolean supports(CredentialItem... items) { + for (CredentialItem i : items) { + if (i instanceof CredentialItem.Username) + continue; + else if (i instanceof CredentialItem.Password) + continue; + else + return false; + } + return true; + } + + @Override + public boolean get(final URIish uri, CredentialItem... items) + throws UnsupportedCredentialItem { + CredentialItem.Username userItem = null; + CredentialItem.Password passwordItem = null; + + for (CredentialItem item : items) { + if (item instanceof CredentialItem.Username) + userItem = (CredentialItem.Username) item; + else if (item instanceof CredentialItem.Password) + passwordItem = (CredentialItem.Password) item; + else + throw new UnsupportedCredentialItem(uri, item.getPromptText()); + } + + UserPasswordCredentials credentials = getCredentialsFromSecureStore(uri); + + if (credentials == null) { + credentials = getCredentialsFromUser(uri); + if (credentials == null) + return false; + } + if (userItem != null) + userItem.setValue(credentials.getUser()); + if (passwordItem != null) + passwordItem.setValue(credentials.getPassword().toCharArray()); + return true; + } + + private UserPasswordCredentials getCredentialsFromUser(final URIish uri) { + final AtomicReference<UserPasswordCredentials> aRef = new AtomicReference<UserPasswordCredentials>( + null); + PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() { + public void run() { + Shell shell = PlatformUI.getWorkbench() + .getActiveWorkbenchWindow().getShell(); + aRef.set(LoginDialog.login(shell, uri)); + } + }); + return aRef.get(); + } + + private UserPasswordCredentials getCredentialsFromSecureStore(final URIish uri) { + UserPasswordCredentials credentials = null; + try { + credentials = Activator.getDefault().getSecureStore() + .getCredentials(uri); + } catch (StorageException e) { + Activator.logError( + UIText.EGitCredentialsProvider_errorReadingCredentials, e); + } + return credentials; + } + +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/credentials/LoginDialog.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/credentials/LoginDialog.java new file mode 100644 index 0000000000..c82ed2a657 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/credentials/LoginDialog.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (C) 2010, Jens Baumgart <jens.baumgart@sap.com> + * Copyright (C) 2010, Edwin Kempin <edwin.kempin@sap.com> + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.egit.ui.credentials; + +import org.eclipse.egit.core.securestorage.UserPasswordCredentials; +import org.eclipse.egit.ui.UIText; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jgit.transport.URIish; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * This class implements a login dialog asking for user and password for a given + * URI. + */ +public class LoginDialog extends Dialog { + + private Text user; + + private Text password; + + private UserPasswordCredentials credentials; + + private final URIish uri; + + private boolean isUserSet; + + private boolean changeCredentials = false; + + private LoginDialog(Shell shell, URIish uri) { + super(shell); + this.uri = uri; + isUserSet = uri.getUser() != null && uri.getUser().length() > 0; + } + + @Override + protected Control createDialogArea(Composite parent) { + final Composite composite = (Composite) super.createDialogArea(parent); + composite.setLayout(new GridLayout(2, false)); + getShell().setText( + changeCredentials ? UIText.LoginDialog_changeCredentials + : UIText.LoginDialog_login); + + Label uriLabel = new Label(composite, SWT.NONE); + uriLabel.setText(UIText.LoginDialog_repository); + Text uriText = new Text(composite, SWT.READ_ONLY); + uriText.setText(uri.toString()); + + Label userLabel = new Label(composite, SWT.NONE); + userLabel.setText(UIText.LoginDialog_user); + if (isUserSet) { + user = new Text(composite, SWT.BORDER | SWT.READ_ONLY); + user.setText(uri.getUser()); + } else + user = new Text(composite, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).applyTo(user); + + Label passwordLabel = new Label(composite, SWT.NONE); + passwordLabel.setText(UIText.LoginDialog_password); + password = new Text(composite, SWT.PASSWORD | SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).applyTo(password); + + if (isUserSet) + password.setFocus(); + else + user.setFocus(); + + return composite; + } + + /** + * The method shows a login dialog for a given URI. The user field is taken + * from the URI if a user is present in the URI. In this case the user is + * not editable. + * + * @param parent + * @param uri + * @return credentials, <code>null</code> if the user canceled the dialog. + */ + public static UserPasswordCredentials login(Shell parent, URIish uri) { + LoginDialog dialog = new LoginDialog(parent, uri); + if (dialog.open() == OK) + return dialog.credentials; + return null; + } + + /** + * The method shows a change credentials dialog for a given URI. The user + * field is taken from the URI if a user is present in the URI. In this case + * the user is not editable. + * + * @param parent + * @param uri + * @return credentials, <code>null</code> if the user canceled the dialog. + */ + public static UserPasswordCredentials changeCredentials(Shell parent, + URIish uri) { + LoginDialog dialog = new LoginDialog(parent, uri); + dialog.changeCredentials = true; + if (dialog.open() == OK) + return dialog.credentials; + return null; + } + + @Override + protected void okPressed() { + if (user.getText().length() > 0) + credentials = new UserPasswordCredentials(user.getText(), + password.getText()); + super.okPressed(); + } + +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/GitCloneWizard.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/GitCloneWizard.java index bbc4e8eb42..82f5879d03 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/GitCloneWizard.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/GitCloneWizard.java @@ -14,6 +14,7 @@ package org.eclipse.egit.ui.internal.clone; import java.io.File; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.Collections; @@ -24,11 +25,13 @@ import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.egit.core.RepositoryUtil; import org.eclipse.egit.core.op.CloneOperation; +import org.eclipse.egit.core.securestorage.UserPasswordCredentials; import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.UIIcons; import org.eclipse.egit.ui.UIPreferences; import org.eclipse.egit.ui.UIText; import org.eclipse.egit.ui.internal.components.RepositorySelectionPage; +import org.eclipse.equinox.security.storage.StorageException; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.operation.IRunnableWithProgress; @@ -72,11 +75,12 @@ public class GitCloneWizard extends Wizard { @Override public void setVisible(boolean visible) { - if (visible) + if (visible) { setSelection(cloneSource.getSelection()); + setCredentials(cloneSource.getCredentials()); + } super.setVisible(visible); } - }; cloneDestination = new CloneDestinationPage() { @Override @@ -145,12 +149,37 @@ public class GitCloneWizard extends Wizard { @Override public boolean performFinish() { try { + if (!storeCredentials()) + return false; return performClone(); } finally { setWindowTitle(UIText.GitCloneWizard_title); } } + private boolean storeCredentials() { + UserPasswordCredentials credentials = cloneSource.getCredentials(); + if (credentials != null) { + URIish uri = cloneSource.getSelection().getURI(); + try { + org.eclipse.egit.core.Activator.getDefault().getSecureStore().putCredentials(uri, credentials); + } catch (StorageException e) { + Activator + .handleError( + UIText.GitCloneWizard_writeToSecureStoreFailed, + e, true); + return false; + } catch (IOException e) { + Activator + .handleError( + UIText.GitCloneWizard_writeToSecureStoreFailed, + e, true); + return false; + } + } + return true; + } + boolean performClone() { final URIish uri = cloneSource.getSelection().getURI(); setWindowTitle(NLS.bind(UIText.GitCloneWizard_jobName, uri.toString())); diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/SourceBranchPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/SourceBranchPage.java index f0016b3d95..d88eac4b97 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/SourceBranchPage.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/clone/SourceBranchPage.java @@ -25,6 +25,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.egit.core.op.ListRemoteOperation; +import org.eclipse.egit.core.securestorage.UserPasswordCredentials; import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.UIPreferences; import org.eclipse.egit.ui.UIText; @@ -45,6 +46,7 @@ import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.storage.file.FileRepository; import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; @@ -76,6 +78,8 @@ class SourceBranchPage extends WizardPage { private CheckboxTableViewer refsViewer; + private UserPasswordCredentials credentials; + SourceBranchPage() { super(SourceBranchPage.class.getName()); setTitle(UIText.SourceBranchPage_title); @@ -164,6 +168,10 @@ class SourceBranchPage extends WizardPage { revalidate(selection); } + public void setCredentials(UserPasswordCredentials credentials) { + this.credentials = credentials; + } + /** * Check internal state for page completion status. This method should be * called only when all necessary data from previous form is available. @@ -233,6 +241,10 @@ class SourceBranchPage extends WizardPage { int timeout = Activator.getDefault().getPreferenceStore().getInt( UIPreferences.REMOTE_CONNECTION_TIMEOUT); listRemoteOp = new ListRemoteOperation(db, uri, timeout); + if (credentials != null) + listRemoteOp + .setCredentialsProvider(new UsernamePasswordCredentialsProvider( + credentials.getUser(), credentials.getPassword())); getContainer().run(true, true, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RepositorySelectionPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RepositorySelectionPage.java index e4ce426f89..c44eb58d7f 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RepositorySelectionPage.java +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RepositorySelectionPage.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.TreeMap; import java.util.regex.Pattern; +import org.eclipse.egit.core.securestorage.UserPasswordCredentials; import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.UIText; import org.eclipse.egit.ui.UIUtils; @@ -108,6 +109,10 @@ public class RepositorySelectionPage extends WizardPage { private IPreviousValueProposalHandler uriProposalHandler; + private String user; + + private String password; + /** * Transport protocol abstraction * @@ -539,12 +544,19 @@ public class RepositorySelectionPage extends WizardPage { userText.addModifyListener(new ModifyListener() { public void modifyText(final ModifyEvent e) { setURI(uri.setUser(nullString(userText.getText()))); + user = userText.getText(); } }); newLabel(g, UIText.RepositorySelectionPage_promptPassword + ":"); //$NON-NLS-1$ passText = new Text(g, SWT.BORDER | SWT.PASSWORD); passText.setLayoutData(createFieldGridData()); + passText.addModifyListener(new ModifyListener() { + public void modifyText(final ModifyEvent e) { + setURI(uri.setPass(null)); + password = passText.getText(); + } + }); return g; } @@ -856,6 +868,16 @@ public class RepositorySelectionPage extends WizardPage { uriProposalHandler.updateProposals(); } + /** + * @return credentials + */ + public UserPasswordCredentials getCredentials() { + if ((user == null || user.length() == 0) + && (password == null || password.length() == 0)) + return null; + return new UserPasswordCredentials(user, password); + } + private void setEnabledRecursively(final Control control, final boolean enable) { control.setEnabled(enable); diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/ChangeCredentialsCommand.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/ChangeCredentialsCommand.java new file mode 100644 index 0000000000..e6ac1b5bb5 --- /dev/null +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/ChangeCredentialsCommand.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (C) 2010, Jens Baumgart <jens.baumgart@sap.com> + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ +package org.eclipse.egit.ui.internal.repository.tree.command; + +import java.io.IOException; +import java.net.URISyntaxException; + +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.egit.core.Activator; +import org.eclipse.egit.core.securestorage.UserPasswordCredentials; +import org.eclipse.egit.ui.UIText; +import org.eclipse.egit.ui.credentials.LoginDialog; +import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode; +import org.eclipse.equinox.security.storage.StorageException; +import org.eclipse.jgit.transport.URIish; + +/** + * Change credentials command + */ +public class ChangeCredentialsCommand extends + RepositoriesViewCommandHandler<RepositoryTreeNode<String>> { + + /** + * Execute the command + */ + public Object execute(ExecutionEvent event) throws ExecutionException { + RepositoryTreeNode<String> node = getSelectedNodes(event).get(0); + URIish uri; + try { + uri = new URIish(node.getObject()); + } catch (URISyntaxException e) { + Activator.error(e.getMessage(), e); + return null; + } + UserPasswordCredentials credentials = LoginDialog.changeCredentials(getShell(event), uri); + if (credentials == null) + return null; + try { + Activator.getDefault().getSecureStore() + .putCredentials(uri, credentials); + } catch (StorageException e) { + Activator.error( + UIText.ChangeCredentialsCommand_writingToSecureStoreFailed, + e); + } catch (IOException e) { + Activator.error( + UIText.ChangeCredentialsCommand_writingToSecureStoreFailed, + e); + } + return null; + } +} diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties index 159c0e86fc..f747e6158d 100644 --- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties +++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties @@ -60,6 +60,7 @@ GenerateHistoryJob_BuildingListMessage=Building commit list for "{0}" ... GenerateHistoryJob_CancelMessage=Reading commit list was canceled for "{0}" GenerateHistoryJob_errorComputingHistory=Cannot compute Git history. +EGitCredentialsProvider_errorReadingCredentials=Failed reading credentials from secure store EgitUiUtils_CouldNotOpenEditorMessage=Could not open editor of type {0} ExistingOrNewPage_CreateButton=&Create Repository ExistingOrNewPage_title=Configure Git Repository @@ -77,6 +78,7 @@ GitCloneWizard_title=Clone Git Repository GitCloneWizard_jobName=Cloning from {0} GitCloneWizard_failed=Git repository clone failed. GitCloneWizard_errorCannotCreate=Cannot create directory {0}. +GitCloneWizard_writeToSecureStoreFailed=Writing to secure store failed GitDecoratorPreferencePage_bindingRepositoryNameFlag=Name and state of the Repository (the default state will not be shown) GitDecoratorPreferencePage_iconsShowDirty=Dirty resources GitDocument_errorLoadCommit=Could not load commit {0} for {1} corresponding to {2} in {3} @@ -157,6 +159,7 @@ GitProjectPropertyPage_ValueEmptyRepository=None (empty repository) GitProjectPropertyPage_ValueUnbornBranch=None (unborn branch) GitProjectsImportPage_NoProjectsMessage=No projects found +ChangeCredentialsCommand_writingToSecureStoreFailed=Writing to secure store failed CheckoutHandler_SelectBranchMessage=There is more than one branch for this commit. Please select the branch you want to check out. CheckoutHandler_SelectBranchTitle=Select a branch for checkout CompareWithIndexAction_FileNotInIndex={0} not in index @@ -886,6 +889,11 @@ GitTraceConfigurationDialog_ShellTitle=Git Trace Configuration GitTraceConfigurationDialog_TraceFileLocationLabel=Trace File &Location: LocalFileRevision_CurrentVersion=*({0}) LocalFileRevision_currentVersionTag=<current version> +LoginDialog_changeCredentials=Change Credentials +LoginDialog_login=Login +LoginDialog_password=Password +LoginDialog_repository=Repository +LoginDialog_user=User NewRemoteWizard_CheckingUriTaskName=Checking URI {0} NewRepositoryWizard_WizardTitle=Create a Git Repository |