Commits

Dusan Hornik committed e4ff3f7 Merge
  • Participants
  • Parent commits eb4a184, 428f55e
  • Branches jira4.x

Comments (0)

Files changed (61)

.hgtags

File contents unchanged.

pom.xml

File contents unchanged.

src/main/java/com/atlassian/jira/plugins/bitbucket/DefaultProgress.java

File contents unchanged.

src/main/java/com/atlassian/jira/plugins/bitbucket/GlobalRepositoryManager.java

 package com.atlassian.jira.plugins.bitbucket;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
 import com.atlassian.jira.plugins.bitbucket.activeobjects.v2.IssueMapping;
 import com.atlassian.jira.plugins.bitbucket.activeobjects.v2.ProjectMapping;
 import com.atlassian.jira.plugins.bitbucket.api.Changeset;
 import com.atlassian.jira.plugins.bitbucket.spi.UrlInfo;
 import com.atlassian.jira.plugins.bitbucket.streams.GlobalFilter;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-
 /**
  * Aggregated Repository Manager that handles all Repository Managers based on the repository url
  */
     }
 
     @Override
-    public SourceControlRepository addRepository(String repositoryType, String projectKey, String url, String username, String password, String adminUsername, String adminPassword, String accessToken)
+    public SourceControlRepository addRepository(String repositoryType, String projectKey, String url, String adminUsername, String adminPassword, String accessToken)
     {
         for (RepositoryManager repositoryManager : repositoryManagers)
         {
             if (repositoryManager.getRepositoryType().equals(repositoryType))
             {
-                return repositoryManager.addRepository(repositoryType, projectKey, url, username, password, adminUsername, adminPassword, accessToken);
+                return repositoryManager.addRepository(repositoryType, projectKey, url, adminUsername, adminPassword, accessToken);
             }
         }
         throw new IllegalArgumentException("No repository manager found for given repository type [" + repositoryType + "]");
     }
 
     @Override
-    public UrlInfo getUrlInfo(String repositoryUrl)
+    public UrlInfo getUrlInfo(String repositoryUrl, String projectKey)
     {
         // TODO - multithread this for better user experience
         for (RepositoryManager repositoryManager : repositoryManagers)
         {
-            UrlInfo urlInfo = repositoryManager.getUrlInfo(repositoryUrl);
+            UrlInfo urlInfo = repositoryManager.getUrlInfo(repositoryUrl, projectKey);
             if (urlInfo != null)
             {
                 return urlInfo;
         throw new UnsupportedOperationException("This implementation should never be called.");
     }
 
+    @Override
+    public Date getLastCommitDate(SourceControlRepository repo)
+    {
+        return getManagerByRepository(repo).getLastCommitDate(repo);
+    }
+
+    @Override
+    public void setLastCommitDate(SourceControlRepository repo, Date date)
+    {
+        getManagerByRepository(repo).setLastCommitDate(repo, date);
+    }
+    
 }

src/main/java/com/atlassian/jira/plugins/bitbucket/activeobjects/v2/IssueMapping.java

 @Table("IssueMappingV2")
 public interface IssueMapping extends Entity {
 
-    public static final String COLUMN_REPOSITORY_ID = "REPOSITORY_ID";
-    public static final String COLUMN_ISSUE_ID = "ISSUE_ID";
-    public static final String COLUMN_NODE = "NODE";
-    public static final String COLUMN_RAW_AUTHOR = "RAW_AUTHOR";
-    public static final String COLUMN_AUTHOR = "AUTHOR";
-    public static final String COLUMN_DATE = "DATE";
-    public static final String COLUMN_RAW_NODE = "RAW_NODE";
-    public static final String COLUMN_BRANCH = "BRANCH";
-    public static final String COLUMN_MESSAGE = "MESSAGE";
-    public static final String COLUMN_PARENTS_DATA = "PARENTS_DATA";
-    public static final String COLUMN_FILES_DATA = "FILES_DATA";
-    public static final String COLUMN_VERSION = "VERSION";
+    public static final String REPOSITORY_ID = "REPOSITORY_ID";
+    public static final String ISSUE_ID = "ISSUE_ID";
+    public static final String NODE = "NODE";
+    public static final String RAW_AUTHOR = "RAW_AUTHOR";
+    public static final String AUTHOR = "AUTHOR";
+    public static final String DATE = "DATE";
+    public static final String RAW_NODE = "RAW_NODE";
+    public static final String BRANCH = "BRANCH";
+    public static final String MESSAGE = "MESSAGE";
+    public static final String PARENTS_DATA = "PARENTS_DATA";
+    public static final String FILES_DATA = "FILES_DATA";
+    public static final String VERSION = "VERSION";
     /**
      * Rows at the table can contain data loaded by previous versions of this plugin. Some column data maybe missing 
      * because previous versions of plugin was not loading them. To get the updated version of changeset we need 

src/main/java/com/atlassian/jira/plugins/bitbucket/activeobjects/v2/ProjectMapping.java

 import net.java.ao.Entity;
 import net.java.ao.schema.Table;
 
+import java.util.Date;
+
 @Table("ProjectMappingV2")
 public interface ProjectMapping extends Entity
 {
+    public static final String ACCESS_TOKEN = "ACCESS_TOKEN";
+    public static final String ADMIN_PASSWORD = "ADMIN_PASSWORD";
+    public static final String ADMIN_USERNAME = "ADMIN_USERNAME";
+    public static final String REPOSITORY_TYPE = "REPOSITORY_TYPE";
+    public static final String PROJECT_KEY = "PROJECT_KEY";
+    public static final String REPOSITORY_URL = "REPOSITORY_URL";
+    public static final String REPOSITORY_NAME = "REPOSITORY_NAME";
+    
     String getRepositoryName();
     String getRepositoryType();
     String getRepositoryUrl();
     String getProjectKey();
-    String getUsername();
-    String getPassword();
     String getAdminPassword();
     String getAdminUsername();
     String getAccessToken();
+    Date getLastCommitDate();
 
     void setRepositoryName(String repositoryName);
     void setRepositoryType(String repositoryType);
     void setRepositoryUrl(String repositoryUrl);
     void setProjectKey(String projectKey);
-    void setUsername(String username);
-    void setPassword(String password);
     void setAdminPassword(String pasword);
     void setAdminUsername(String username);
     void setAccessToken(String accessToken);
+    void setLastCommitDate(Date lastSyncDate);
+
 }

src/main/java/com/atlassian/jira/plugins/bitbucket/api/RepositoryPersister.java

 import com.atlassian.jira.plugins.bitbucket.activeobjects.v2.ProjectMapping;
 import com.atlassian.jira.plugins.bitbucket.streams.GlobalFilter;
 
-import java.util.List;
-import java.util.Set;
 
 /**
  * Maps bitbucket repositories and commits to jira projects and issues.
      *
      * @return
      */
-    ProjectMapping addRepository(String repositoryName, String projectKey, String repositoryUrl, String username, String password, String adminUsername, String adminPassword, String repositoryType, String accessToken);
+    ProjectMapping addRepository(String repositoryName, String projectKey, String repositoryUrl, String adminUsername, String adminPassword, String repositoryType, String accessToken);
 
     /**
      * Remove the mapping of the bibucket repository from the specified jira project
      * @return changeset by node
      */
     public IssueMapping getIssueMapping(String node);
+
+    public ProjectMapping[] findRepositories(String projectKey, String repositoryUrl);
+
 }

src/main/java/com/atlassian/jira/plugins/bitbucket/api/SourceControlRepository.java

      */
     String getRepositoryType();
 
-	/**
-	 * @return username to use for authenticating
-	 */
-	String getUsername();
-
-	/**
-	 * @return password to use for authenticating
-	 */
-	String getPassword();
-
+    
 	/**
 	 * Admin username - used when (un)installing postcommit hook
 	 * @return

src/main/java/com/atlassian/jira/plugins/bitbucket/api/impl/DefaultAuthenticationFactory.java

 package com.atlassian.jira.plugins.bitbucket.api.impl;
 
-import org.apache.commons.lang.StringUtils;
-
 import com.atlassian.jira.plugins.bitbucket.api.Authentication;
 import com.atlassian.jira.plugins.bitbucket.api.AuthenticationFactory;
 import com.atlassian.jira.plugins.bitbucket.api.SourceControlRepository;
+import org.apache.commons.lang.StringUtils;
 
 public class DefaultAuthenticationFactory implements AuthenticationFactory
 {
 	    }
 
 	    // basic
-	    if (StringUtils.isNotBlank(repository.getUsername()))
+	    if (StringUtils.isNotBlank(repository.getAdminUsername()))
 	    {
-	        return new BasicAuthentication(repository.getUsername(), repository.getPassword());
+	        return new BasicAuthentication(repository.getAdminUsername(), repository.getAdminPassword());
 	    }
 	        
 	    // none

src/main/java/com/atlassian/jira/plugins/bitbucket/api/impl/DefaultRepositoryPersister.java

  */
 public class DefaultRepositoryPersister implements RepositoryPersister
 {
-    private final Logger logger = LoggerFactory.getLogger(DefaultRepositoryPersister.class);
-
+    private final Logger log = LoggerFactory.getLogger(DefaultRepositoryPersister.class);
     private final ActiveObjects activeObjects;
 
     public DefaultRepositoryPersister(ActiveObjects activeObjects)
             @Override
             public List<ProjectMapping> doInTransaction()
             {
-                ProjectMapping[] mappings = activeObjects.find(ProjectMapping.class, "PROJECT_KEY = ? AND REPOSITORY_TYPE = ?", projectKey, repositoryType);
+                ProjectMapping[] mappings = activeObjects.find(ProjectMapping.class, ProjectMapping.PROJECT_KEY + " = ? AND "
+                    + ProjectMapping.REPOSITORY_TYPE + " = ?", projectKey, repositoryType);
                 return Lists.newArrayList(mappings);
             }
         });
     }
 
     @Override
-    public ProjectMapping addRepository(String repositoryName, String repositoryType, String projectKey, String repositoryUrl, String username, String password, String adminUsername, String adminPassword, String accessToken)
+    public ProjectMapping addRepository(String repositoryName, String repositoryType, String projectKey, String repositoryUrl, 
+        String adminUsername, String adminPassword, String accessToken)
     {
-
-        final ProjectMapping[] projectMappings = activeObjects.find(ProjectMapping.class, "REPOSITORY_URL = ? and PROJECT_KEY = ?", repositoryUrl, projectKey);
-        if (projectMappings.length > 0)
+        if (findRepositories(projectKey, repositoryUrl).length > 0)
         {
             throw new SourceControlException("Repository [" + repositoryUrl + "] is already linked to project [" + projectKey + "]");
         }
         final Map<String, Object> map = new HashMap<String, Object>();
-        // TODO make constants in ProjectMappings.java
-        map.put("REPOSITORY_NAME", repositoryName);
-        map.put("REPOSITORY_URL", repositoryUrl);
-        map.put("PROJECT_KEY", projectKey);
-        map.put("REPOSITORY_TYPE", repositoryType);
-        if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password))
-        {
-            map.put("USERNAME", username);
-            map.put("PASSWORD", password);
-        }
+        map.put(ProjectMapping.REPOSITORY_NAME, repositoryName);
+        map.put(ProjectMapping.REPOSITORY_URL, repositoryUrl);
+        map.put(ProjectMapping.PROJECT_KEY, projectKey);
+        map.put(ProjectMapping.REPOSITORY_TYPE, repositoryType);
         if (StringUtils.isNotBlank(adminUsername) && StringUtils.isNotBlank(adminPassword))
         {
-            map.put("ADMIN_USERNAME", adminUsername);
-            map.put("ADMIN_PASSWORD", adminPassword);
+            map.put(ProjectMapping.ADMIN_USERNAME, adminUsername);
+            map.put(ProjectMapping.ADMIN_PASSWORD, adminPassword);
         }
         if (StringUtils.isNotBlank(accessToken))
         {
-            map.put("ACCESS_TOKEN", accessToken);
+            map.put(ProjectMapping.ACCESS_TOKEN, accessToken);
         }
         return activeObjects.executeInTransaction(new TransactionCallback<ProjectMapping>()
         {
     }
 
     @Override
+    public ProjectMapping[] findRepositories(String projectKey, String repositoryUrl)
+    {
+        return activeObjects.find(ProjectMapping.class, ProjectMapping.REPOSITORY_URL + " = ? and "
+            + ProjectMapping.PROJECT_KEY + " = ?", repositoryUrl, projectKey);
+    }
+
+    @Override
     public void removeRepository(final int id)
     {
         activeObjects.executeInTransaction(new TransactionCallback<Object>()
             public Object doInTransaction()
             {
                 final ProjectMapping projectMapping = activeObjects.get(ProjectMapping.class, id);
-                final IssueMapping[] issueMappings = activeObjects.find(IssueMapping.class, "REPOSITORY_ID = ?", id);
+                final IssueMapping[] issueMappings = activeObjects.find(IssueMapping.class, IssueMapping.REPOSITORY_ID+" = ?", id);
 
-                logger.debug("deleting project mapping [ {} ]", String.valueOf(id));
-                logger.debug("deleting [ {} ] issue mappings [ {} ]", new String[]{String.valueOf(issueMappings.length), String.valueOf(id)});
+                log.debug("deleting project mapping [ {} ]", String.valueOf(id));
+                log.debug("deleting [ {} ] issue mappings [ {} ]", new String[]{String.valueOf(issueMappings.length), String.valueOf(id)});
 
                 activeObjects.delete(projectMapping);
                 activeObjects.delete(issueMappings);
             @Override
             public List<IssueMapping> doInTransaction()
             {
-                String baseWhereClause = "ISSUE_ID = '" + issueId + "'";
+                String baseWhereClause = IssueMapping.ISSUE_ID + " = '" + issueId + "'";
                 String repositoryIdsFilteringWhereClause = getRepositoryIdsFilteringWhereClause(repositoryType);
                 Query query = Query.select().where(baseWhereClause + repositoryIdsFilteringWhereClause);
 
      */
     private Set<Integer> getProjectMappingsForRepositoryType(final String repositoryType)
     {
-        ProjectMapping[] myProjectMappings = activeObjects.find(ProjectMapping.class, "REPOSITORY_TYPE = ?", repositoryType);
+        ProjectMapping[] myProjectMappings = activeObjects.find(ProjectMapping.class, ProjectMapping.REPOSITORY_TYPE + " = ?",
+            repositoryType);
 
         final Set<Integer> projectMappingsIds = Sets.newHashSet();
         for (ProjectMapping myProjectMapping : myProjectMappings)
             @Override
             public Object doInTransaction()
             {
-                logger.debug("create issue mapping [ {} ] [ {} - {} ] ", new String[]{issueId, String.valueOf(repositoryId), node});
+                log.debug("create issue mapping [ {} ] [ {} - {} ] ", new String[]{issueId, String.valueOf(repositoryId), node});
                 // delete existing
-                IssueMapping[] mappings = activeObjects.find(IssueMapping.class, "ISSUE_ID = ? and NODE = ?", issueId, node);
+                IssueMapping[] mappings = activeObjects.find(IssueMapping.class, IssueMapping.ISSUE_ID + " = ? and "
+                    + IssueMapping.NODE + " = ?", issueId, node);
                 if (ArrayUtils.isNotEmpty(mappings))
                 {
                     activeObjects.delete(mappings);
                 }
                 // add new
                 Map<String, Object> map = Maps.newHashMap();
-                map.put(IssueMapping.COLUMN_REPOSITORY_ID, repositoryId);
-                map.put(IssueMapping.COLUMN_ISSUE_ID, issueId);
-                map.put(IssueMapping.COLUMN_NODE, node);
-                map.put(IssueMapping.COLUMN_RAW_AUTHOR, changeset.getRawAuthor());
-                map.put(IssueMapping.COLUMN_AUTHOR, changeset.getAuthor());
-                map.put(IssueMapping.COLUMN_DATE, changeset.getTimestamp());
-                map.put(IssueMapping.COLUMN_RAW_NODE, changeset.getRawNode());
-                map.put(IssueMapping.COLUMN_BRANCH, changeset.getBranch());
-                map.put(IssueMapping.COLUMN_MESSAGE, changeset.getMessage());
+                map.put(IssueMapping.REPOSITORY_ID, repositoryId);
+                map.put(IssueMapping.ISSUE_ID, issueId);
+                map.put(IssueMapping.NODE, node);
+                map.put(IssueMapping.RAW_AUTHOR, changeset.getRawAuthor());
+                map.put(IssueMapping.AUTHOR, changeset.getAuthor());
+                map.put(IssueMapping.DATE, changeset.getTimestamp());
+                map.put(IssueMapping.RAW_NODE, changeset.getRawNode());
+                map.put(IssueMapping.BRANCH, changeset.getBranch());
+                map.put(IssueMapping.MESSAGE, changeset.getMessage());
 
                 JSONArray parentsJson = new JSONArray();
                 for (String parent : changeset.getParents())
                 {
                     parentsJson.put(parent);
                 }
-                map.put(IssueMapping.COLUMN_PARENTS_DATA, parentsJson.toString());
+                map.put(IssueMapping.PARENTS_DATA, parentsJson.toString());
 
                 JSONObject filesDataJson = new JSONObject();
                 JSONArray filesJson = new JSONArray();
                         filesJson.put(fileJson);
                     }
                     filesDataJson.put("files", filesJson);
-
-                    map.put(IssueMapping.COLUMN_FILES_DATA, filesDataJson.toString());
-
-                    map.put(IssueMapping.COLUMN_VERSION, IssueMapping.LATEST_VERSION);
+                    map.put(IssueMapping.FILES_DATA, filesDataJson.toString());
+                    map.put(IssueMapping.VERSION, IssueMapping.LATEST_VERSION);
                 } catch (JSONException e)
                 {
-                    logger.error("Creating files JSON failed!", e);
+                    log.error("Creating files JSON failed!", e);
                 }
 
                 return activeObjects.create(IssueMapping.class, map);
             {
                 String baseWhereClause = new GlobalFilterQueryWhereClauseBuilder(gf).build();
                 String repositoryIdsFilteringWhereClause = getRepositoryIdsFilteringWhereClause(repositoryType);
-                Query query = Query.select().where(baseWhereClause + repositoryIdsFilteringWhereClause).limit(count).order("DATE DESC");
+                Query query = Query.select().where(baseWhereClause + repositoryIdsFilteringWhereClause).limit(count).order(IssueMapping.DATE + " DESC");
                 IssueMapping[] mappings = activeObjects.find(IssueMapping.class, query);
                 return Arrays.asList(mappings);
             }
         Set<Integer> ids = getProjectMappingsForRepositoryType(repositoryType);
         if (CollectionUtils.isNotEmpty(ids))
         {
-            sb.append(" AND REPOSITORY_ID in (");
+            sb.append(" AND " + IssueMapping.REPOSITORY_ID + " in (");
             sb.append(StringUtils.join(ids, ","));
             sb.append(")");
         } else

src/main/java/com/atlassian/jira/plugins/bitbucket/api/impl/DefaultSourceControlRepository.java

 package com.atlassian.jira.plugins.bitbucket.api.impl;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.builder.EqualsBuilder;
 import org.apache.commons.lang.builder.HashCodeBuilder;
 
 public class DefaultSourceControlRepository implements SourceControlRepository
 {
 	private final int id;
-	private final String username;
-	private final String password;
 	private final String repositoryName;
     private final RepositoryUri repositoryUri;
     private final String projectKey;
     private final String repositoryType;
     private final String accessToken;
 
-    public DefaultSourceControlRepository(int id, String repositoryName, String repositoryType, RepositoryUri repositoryUri, String projectKey, String username, String password,
+    public DefaultSourceControlRepository(int id, String repositoryName, String repositoryType, RepositoryUri repositoryUri, String projectKey,
 			String adminUsername, String adminPassword, String accessToken)
 	{
 		this.id = id;
         this.repositoryName = repositoryName;
         this.repositoryUri = repositoryUri;
         this.projectKey = projectKey;
-		this.username = username;
-		this.password = password;
 		this.adminUsername = adminUsername;
 		this.adminPassword = adminPassword;
         this.repositoryType = repositoryType;
 	@Override
     public String getRepositoryName()
     {
+	    if (StringUtils.isBlank(repositoryName))
+	    {
+	        return repositoryUri.getSlug();
+	    }
         return repositoryName;
     }
 
 	
 
 	@Override
-    public String getUsername()
-	{
-		return username;
-	}
-	
-	@Override
-    public String getPassword()
-	{
-		return password;
-	}
-
-	@Override
     public String getAdminUsername()
 	{
 		return adminUsername;
 		if (this==obj) return true;
 		if (this.getClass()!=obj.getClass()) return false;
 		DefaultSourceControlRepository that = (DefaultSourceControlRepository) obj;
-		return new EqualsBuilder().append(id, that.id).append(repositoryUri, that.repositoryUri).append(repositoryName, that.repositoryName)
-			.append(projectKey, that.projectKey).append(username, that.username)
-			.append(password, that.password).append(adminUsername, that.adminUsername)
-			.append(adminPassword, that.adminPassword).append(accessToken, that.accessToken).isEquals();
-	}
+        return new EqualsBuilder().append(id, that.id).append(repositoryUri, that.repositoryUri).append(repositoryName, that.repositoryName)
+            .append(projectKey, that.projectKey)
+            .append(adminUsername, that.adminUsername)
+            .append(adminPassword, that.adminPassword).append(accessToken, that.accessToken)
+            .isEquals();
+    }
 	
 	@Override
     public int hashCode()
     {
         return new HashCodeBuilder(17, 37).append(id).append(repositoryUri).append(repositoryName).append(projectKey)
-            .append(username).append(password).append(adminUsername).append(adminPassword).append(accessToken)
+            .append(adminUsername).append(adminPassword).append(accessToken)
             .toHashCode();
     }
 }

src/main/java/com/atlassian/jira/plugins/bitbucket/rest/Repository.java

 package com.atlassian.jira.plugins.bitbucket.rest;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlAttribute;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.*;
 
 @XmlRootElement(name = "repository")
 @XmlAccessorType(XmlAccessType.FIELD)
     private SyncProgress sync;
 
     @XmlAttribute
-    private String username;
-
-    @XmlAttribute
-    private String password;
-    
-    @XmlAttribute
     private String adminUsername;
     
     @XmlAttribute
 	@XmlAttribute
     private String accessToken;
 
+    @XmlAttribute
+    private String lastCommitRelativeDate;
+
 	public Repository()
     {
     }
 
-    public Repository(int id, String repositoryType, String projectKey, String url, String username, String password, String adminUsername, String adminPassword, String accessToken)
+    public Repository(int id, String repositoryType, String projectKey, String url, String adminUsername, String adminPassword, String accessToken, String lastCommitRelativeDate)
     {
         this.id = id;
         this.repositoryType = repositoryType;
         this.projectKey = projectKey;
         this.url = url;
-		this.username = username;
-		this.password = password;
 		this.adminUsername = adminUsername;
 		this.adminPassword = adminPassword;
         this.accessToken = accessToken;
+        this.lastCommitRelativeDate = lastCommitRelativeDate;
     }
 
     public int getId()
     {
         this.sync = sync;
     }
-    public String getUsername()
-	{
-		return username;
-	}
-
-	public void setUsername(String username)
-	{
-		this.username = username;
-	}
-
-	public String getPassword()
-	{
-		return password;
-	}
-
-	public void setPassword(String password)
-	{
-		this.password = password;
-	}
 
     public String getAdminUsername()
 	{
     {
         this.accessToken = accessToken;
     }
+
+    public String getLastCommitRelativeDate() {
+        return lastCommitRelativeDate;
+    }
+
+    public void setLastCommitRelativeDate(String lastCommitRelativeDate) {
+        this.lastCommitRelativeDate = lastCommitRelativeDate;
+    }
 }

src/main/java/com/atlassian/jira/plugins/bitbucket/rest/RootResource.java

 
 import java.net.URI;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
 import javax.ws.rs.DELETE;
 import com.atlassian.jira.security.PermissionManager;
 import com.atlassian.jira.security.Permissions;
 import com.atlassian.plugins.rest.common.security.AnonymousAllowed;
+import com.atlassian.theplugin.commons.util.DateUtil;
 import com.google.common.base.Function;
 import com.google.common.collect.Lists;
 
         @Override
         public Repository apply(SourceControlRepository from)
         {
+
+            final String relativePastDate = DateUtil.getRelativePastDate(new Date(), globalRepositoryManager.getLastCommitDate(from));
+
             Repository repo = new Repository(from.getId(), from.getRepositoryType(), from.getProjectKey(), from.getRepositoryUri().getRepositoryUrl(),
-                    from.getUsername(), null, from.getAdminUsername(), null, null); // don't include password or accessToken
+                    from.getAdminUsername(), null, null, relativePastDate); // don't include password or accessToken
             Progress progress = synchronizer.getProgress(from);
             if (progress != null)
                 repo.setStatus(new SyncProgress(progress.isFinished(), progress.getChangesetCount(), progress
             String url = repository.getUrl();
             String repositoryType = repository.getRepositoryType();
             String projectKey = repository.getProjectKey();
-            String username = repository.getUsername();
-            String password = repository.getPassword();
-            String adminUsername = repository.getUsername();
-            String adminPassword = repository.getPassword();
+            String adminUsername = repository.getAdminUsername();
+            String adminPassword = repository.getAdminPassword();
             String accessToken = repository.getAccessToken();
 
             SourceControlRepository repo;
             try
             {
-                repo = globalRepositoryManager.addRepository(repositoryType, projectKey, url, username, password,
+                repo = globalRepositoryManager.addRepository(repositoryType, projectKey, url,
                         adminUsername, adminPassword, accessToken);
             } catch (SourceControlException e)
             {
     @GET
     @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
     @Path("/urlinfo")
-    public Response urlInfo(@QueryParam("repositoryUrl") String repositoryUrl)
+    public Response urlInfo(@QueryParam("repositoryUrl") String repositoryUrl, @QueryParam("projectKey") String projectKey)
     {
-        UrlInfo urlInfo = globalRepositoryManager.getUrlInfo(repositoryUrl);
+        UrlInfo urlInfo = globalRepositoryManager.getUrlInfo(repositoryUrl.trim(), projectKey);
         if (urlInfo!=null)
             return Response.ok(urlInfo).build();
         else 

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/CachingCommunicator.java

 {
     private final Communicator delegate;
 
-//    private class ChangesetKey
-//    {
-//        final String id;
-//        private final SourceControlRepository repository;
-//
-//        public ChangesetKey(SourceControlRepository repository, String id)
-//        {
-//            this.repository = repository;
-//            this.id = id;
-//        }
-//
-//        @Override
-//        public boolean equals(Object o)
-//        {
-//            if (this == o) return true;
-//            if (o == null || getClass() != o.getClass()) return false;
-//            ChangesetKey that = (ChangesetKey) o;
-//            if (!repository.equals(that.repository)) return false;
-//            if (!id.equals(that.id)) return false;
-//            return true;
-//        }
-//
-//        @Override
-//        public int hashCode()
-//        {
-//            int result = repository.hashCode();
-//            result = 31 * result + id.hashCode();
-//            return result;
-//        }
-//    }
-
     private class UserKey
     {
         SourceControlRepository repository;
             }
         });
 
-//    private final Map<ChangesetKey, Changeset> changesetMap = new MapMaker().expiration(30, TimeUnit.MINUTES).makeComputingMap(
-//        new Function<ChangesetKey, Changeset>()
-//        {
-//            @Override
-//            public Changeset apply(ChangesetKey key)
-//            {
-//                return delegate.getChangeset(key.repository, key.id);
-//            }
-//        });
-
     public CachingCommunicator(Communicator delegate)
     {
         this.delegate = delegate;
     public Changeset getChangeset(SourceControlRepository repository, String id)
     {
         return delegate.getChangeset(repository, id);
-
-//        try
-//        {
-//            return changesetMap.get(new ChangesetKey(repository, id));
-//        } catch (ComputationException e)
-//        {
-//            throw unrollException(e);
-//        }
     }
 
     private SourceControlException unrollException(ComputationException e)
     }
 
     @Override
-    public UrlInfo getUrlInfo(RepositoryUri repositoryUri)
+    public UrlInfo getUrlInfo(RepositoryUri repositoryUri, String projectKey)
     {
-        return delegate.getUrlInfo(repositoryUri);
+        return delegate.getUrlInfo(repositoryUri, projectKey);
     }
 
     @Override
-    public String getRepositoryName(String repositoryType, String projectKey, RepositoryUri repositoryUri, String username,
-        String password, String adminUsername, String adminPassword, String accessToken) throws SourceControlException
+    public String getRepositoryName(String repositoryType, String projectKey, RepositoryUri repositoryUri,
+        String adminUsername, String adminPassword, String accessToken) throws SourceControlException
     {
-        return delegate.getRepositoryName(repositoryType, projectKey, repositoryUri, username, password, adminUsername, adminPassword, accessToken);
+        return delegate.getRepositoryName(repositoryType, projectKey, repositoryUri, adminUsername, adminPassword, accessToken);
     }
 }

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/Communicator.java

 
     /**
      * @param repositoryUri
+     * @param projectKey
      * @return info about the repository or null if repository is invalid
      */
-    public UrlInfo getUrlInfo(final RepositoryUri repositoryUri);
+    public UrlInfo getUrlInfo(RepositoryUri repositoryUri, String projectKey);
 
-    public String getRepositoryName(String repositoryType, String projectKey, RepositoryUri repositoryUri, String username,
-        String password, String adminUsername, String adminPassword, String accessToken) throws SourceControlException;
+
+    public String getRepositoryName(String repositoryType, String projectKey, RepositoryUri repositoryUri,
+        String adminUsername, String adminPassword, String accessToken) throws SourceControlException;
+
 
 }

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/CustomStringUtils.java

File contents unchanged.

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/DefaultBitbucketChangesetFile.java

 {
     private final ChangesetFileAction type;
     private final String file;
-    private int additions;
-    private int deletions;
+    private final int additions;
+    private final int deletions;
 
     public DefaultBitbucketChangesetFile(ChangesetFileAction type, String file, int additions, int deletions)
     {

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/DefaultChangeset.java

 
     private final int repositoryId;
 
-    public DefaultChangeset(int repositoryId, String node, String message)
+    public DefaultChangeset(int repositoryId, String node, String message, Date timestamp)
     {
-        this(repositoryId, node, "", "", new Date(), "", "", message, Collections.<String>emptyList(), Collections.<ChangesetFile>emptyList(), 0);
+        this(repositoryId, node, "", "", timestamp, "", "", message, Collections.<String>emptyList(), Collections.<ChangesetFile>emptyList(), 0);
     }
 
     public DefaultChangeset(int repositoryId,

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/DefaultSynchronisationOperation.java

 package com.atlassian.jira.plugins.bitbucket.spi;
 
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.atlassian.jira.issue.IssueManager;
 import com.atlassian.jira.plugins.bitbucket.DefaultSynchronizer;
 import com.atlassian.jira.plugins.bitbucket.api.Changeset;
 import com.atlassian.jira.plugins.bitbucket.api.ProgressWriter;
 import com.atlassian.jira.plugins.bitbucket.api.SourceControlException;
 import com.atlassian.jira.plugins.bitbucket.api.SynchronizationKey;
-import org.apache.commons.collections.CollectionUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.HashSet;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 public class DefaultSynchronisationOperation implements SynchronisationOperation
 {
     public void synchronise()
     {
         Iterable<Changeset> changesets = getChangsetsIterator();
+        Date lastCommitDate = null;
+        if (key.getChangesets() == null)
+        {
+            // we are doing full synchronisation (maybe we should delete all
+            // issueMappings)
+            repositoryManager.setLastCommitDate(key.getRepository(), null);
+        } else
+        {
+            lastCommitDate = repositoryManager.getLastCommitDate(key.getRepository());
+        }
 
         int changesetCount = 0;
         int jiraCount = 0;
 
         for (Changeset changeset : changesets)
         {
+            if (lastCommitDate == null || lastCommitDate.before(changeset.getTimestamp()))
+            {
+                lastCommitDate = changeset.getTimestamp();
+                repositoryManager.setLastCommitDate(key.getRepository(), lastCommitDate);
+            }
             changesetCount++;
             String message = changeset.getMessage();
             log.debug("syncing changeset [{}] [{}]", changeset.getNode(), changeset.getMessage());
             if (message.contains(key.getRepository().getProjectKey()))
             {
                 Set<String> extractedIssues = extractProjectKey(key.getRepository().getProjectKey(), message);
-                // get detial changeset because in this response is not information about files
+                // get detail changeset because in this response is not information about files
                 Changeset detailChangeset = null;
                 if (CollectionUtils.isNotEmpty(extractedIssues))
                 {
                         detailChangeset = repositoryManager.getChangeset(key.getRepository(), changeset.getNode());
                     } catch (SourceControlException e)
                     {
-                        log.warn("Unable to retrieve statistics for changeset " + changeset.getNode(), e);
+                        log.warn("Unable to retrieve details for changeset " + changeset.getNode(), e);
                         synchroErrorCount++;
                     }
                 }

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/DvcsRepositoryManager.java

         @Override
         public SourceControlRepository apply(ProjectMapping pm)
         {
-            String decryptedPassword = encryptor.decrypt(pm.getPassword(), pm.getProjectKey(), pm.getRepositoryUrl());
             String decryptedAdminPassword = encryptor.decrypt(pm.getAdminPassword(), pm.getProjectKey(),
                     pm.getRepositoryUrl());
             return new DefaultSourceControlRepository(pm.getID(), pm.getRepositoryName(), pm.getRepositoryType(), getRepositoryUri(pm.getRepositoryUrl()),
-                    pm.getProjectKey(), pm.getUsername(), decryptedPassword,
-                    pm.getAdminUsername(), decryptedAdminPassword, pm.getAccessToken());
+                pm.getProjectKey(), pm.getAdminUsername(), decryptedAdminPassword, pm.getAccessToken());
         }
     };
 
         toChangesetTransformer = new ToChangesetTransformer(this);
     }
 
-    public String getRepositoryName(String repositoryType, String projectKey, String repositoryUrl, String username,
-                                    String password, String adminUsername, String adminPassword, String accessToken) throws SourceControlException
+    public String getRepositoryName(String repositoryType, String projectKey, String repositoryUrl,
+        String adminUsername, String adminPassword, String accessToken) throws SourceControlException
     {
         RepositoryUri repositoryUri = getRepositoryUri(repositoryUrl);
-        return getCommunicator().getRepositoryName(repositoryType, projectKey, repositoryUri, username, password, adminUsername, adminPassword, accessToken);
+        return getCommunicator().getRepositoryName(repositoryType, projectKey, repositoryUri, adminUsername, adminPassword, accessToken);
     }
 
     @Override
-    public SourceControlRepository addRepository(String repositoryType, String projectKey, String repositoryUrl, String username,
-                                                 String password, String adminUsername, String adminPassword, String accessToken)
+    public SourceControlRepository addRepository(String repositoryType, String projectKey, String repositoryUrl,  String adminUsername, String adminPassword, String accessToken)
     {
         // Remove trailing slashes from URL
         if (repositoryUrl.endsWith("/"))
         {
             repositoryUrl = repositoryUrl.replaceFirst("http:", "https:");
         }
-        String repositoryName = getRepositoryName(repositoryType, projectKey, repositoryUrl, username, password, adminUsername, adminPassword, accessToken);
+        String repositoryName = getRepositoryName(repositoryType, projectKey, repositoryUrl, adminUsername, adminPassword, accessToken);
 
-        String encryptedPassword = encryptor.encrypt(password, projectKey, repositoryUrl);
         String encryptedAdminPassword = encryptor.encrypt(adminPassword, projectKey, repositoryUrl);
         ProjectMapping pm = repositoryPersister.addRepository(repositoryName, repositoryType, projectKey, repositoryUrl,
-                username, encryptedPassword, adminUsername, encryptedAdminPassword, accessToken);
+            adminUsername, encryptedAdminPassword, accessToken);
         return TO_SOURCE_CONTROL_REPOSITORY.apply(pm);
     }
 
     public Changeset getChangeset(SourceControlRepository repository, String node)
     {
         return getCommunicator().getChangeset(repository, node);
-
     }
 
     @Override
         templateMap.put("login", login);
         templateMap.put("user_name", authorName);
         templateMap.put("commit_message", commitMessage);
-        templateMap.put("formatted_commit_time", getDateString(changeset.getTimestamp()));
-        templateMap.put("formatted_commit_date", getDateString(changeset.getTimestamp()));
         templateMap.put("commit_url", commitURL);
         templateMap.put("commit_hash", changeset.getNode());
 
         return sw.toString();
     }
 
-    public String getDateString(Date datetime)
-    {
-        // example:    2011-05-26 10:54:41
-        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-
-        return df.format(datetime);
-    }
 
     @Override
     public SynchronisationOperation getSynchronisationOperation(SynchronizationKey key, ProgressWriter progressProvider)
     }
 
     @Override
-    public UrlInfo getUrlInfo(String repositoryUrl)
+    public UrlInfo getUrlInfo(String repositoryUrl, String projectKey)
     {
         if (!hasValidFormat(repositoryUrl)) return null;
-        return getCommunicator().getUrlInfo(getRepositoryUri(repositoryUrl));
+        UrlInfo urlInfo = getCommunicator().getUrlInfo(getRepositoryUri(repositoryUrl), projectKey);
+        if (urlInfo==null) return null;
+        return validateUrlInfo(urlInfo);
+    }
+
+    public UrlInfo validateUrlInfo(UrlInfo urlInfo)
+    {
+        ProjectMapping[] repos = repositoryPersister.findRepositories(urlInfo.getProjectKey(), urlInfo.getRepositoryUrl());
+        if (repos.length>0)
+        {
+            urlInfo.addValidationError("Repository " + urlInfo.getRepositoryUrl() + " is already linked to project "
+                + urlInfo.getProjectKey());
+        }
+        return urlInfo; 
     }
 
     @Override
         List<Changeset> changesets = Lists.transform(latestIssueMappings, toChangesetTransformer);
         return Sets.newHashSet(changesets);
     }
+    
+    
+    @Override
+    public Date getLastCommitDate(SourceControlRepository repo)
+    {
+        ProjectMapping projectMapping = repositoryPersister.getRepository(repo.getId());
+        return projectMapping.getLastCommitDate();
+    }
+
+    @Override
+    public void setLastCommitDate(SourceControlRepository repo, Date date)
+    {
+        ProjectMapping projectMapping = repositoryPersister.getRepository(repo.getId());
+        
+        //!!! NPE ???
+        projectMapping.setLastCommitDate(date);
+        projectMapping.save();
+    }
 }

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/RepositoryManager.java

 package com.atlassian.jira.plugins.bitbucket.spi;
 
 import com.atlassian.jira.plugins.bitbucket.activeobjects.v2.IssueMapping;
-import com.atlassian.jira.plugins.bitbucket.api.Changeset;
-import com.atlassian.jira.plugins.bitbucket.api.ProgressWriter;
-import com.atlassian.jira.plugins.bitbucket.api.SourceControlRepository;
-import com.atlassian.jira.plugins.bitbucket.api.SourceControlUser;
-import com.atlassian.jira.plugins.bitbucket.api.SynchronizationKey;
+import com.atlassian.jira.plugins.bitbucket.api.*;
 import com.atlassian.jira.plugins.bitbucket.streams.GlobalFilter;
 
+import java.util.Date;
 import java.util.List;
 import java.util.Set;
 
 {
 
     /**
+     * @param repositoryId
+     * @return repository with given id
+     *         throws IllegalArgumentException if repository with given id is not found
+     */
+    public SourceControlRepository getRepository(int repositoryId);
+
+    /**
      * Mapps a repository to given project
      *
      * @param repositoryType
      * @param projectKey
      * @param repositoryUrl
-     * @param username
-     * @param password
      * @param adminUsername  - used when (un)installing postcommit hook
      * @param adminPassword  - used when (un)installing postcommit hook
      * @param accessToken    - token for authenticating if this repository is accessed using OAuth
      * @return
      */
-    public SourceControlRepository addRepository(String repositoryType, String projectKey, String repositoryUrl, String username,
-                                                 String password, String adminUsername, String adminPassword, String accessToken);
-
-    /**
-     * @param repositoryId
-     * @return repository with given id
-     *         throws IllegalArgumentException if repository with given id is not found
-     */
-    public SourceControlRepository getRepository(int repositoryId);
+    public SourceControlRepository addRepository(String repositoryType, String projectKey, String repositoryUrl,
+                                                 String adminUsername, String adminPassword, String accessToken);
 
     /**
      * @param projectKey
      */
     public Set<Changeset> getLatestChangesets(final int count, GlobalFilter gf);
 
-    public UrlInfo getUrlInfo(String repositoryUrl);
+    /**
+     * @param repositoryUrl
+     * @param projectKey
+     * @return
+     */
+    public UrlInfo getUrlInfo(String repositoryUrl, String projectKey);
 
+    /**
+     * Reloads the changeset from the repository.
+     * In previous versions of the plugin we stored  little information about changesets locally (only changset id).
+     * Now we keep more columns (date, message, author, etc) but instead of resyncing all repositories again we use
+     * lazy loading to reload old changesets only when required.
+     *
+     * @param issueMapping
+     * @return
+     */
     public Changeset reloadChangeset(IssueMapping issueMapping);
 
+    public Date getLastCommitDate(SourceControlRepository repo);
+
+    public void setLastCommitDate(SourceControlRepository repo, Date date);
+
 }

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/UrlInfo.java

 package com.atlassian.jira.plugins.bitbucket.spi;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlList;
 import javax.xml.bind.annotation.XmlRootElement;
 
 @XmlRootElement(name = "urlinfo")
     @XmlAttribute
     private boolean isPrivate;
 
+    @XmlAttribute
+    private String repositoryUrl;
+    
+    @XmlList
+    private final List<String> validationErrors =  new ArrayList<String>();
+
+    @XmlAttribute
+    private String projectKey;
+    
+
     public UrlInfo() {}
     
-    public UrlInfo(String repositoryType, boolean isPrivate)
+    public UrlInfo(String repositoryType, boolean isPrivate, String repositoryUrl, String projectKey)
     {
         this.repositoryType = repositoryType;
         this.isPrivate = isPrivate;
+        this.repositoryUrl = repositoryUrl;
+        this.projectKey = projectKey;
+    }
+    
+    public UrlInfo addValidationError(String validationError)
+    {
+        this.validationErrors.add(validationError);
+        return this;
     }
 
     public String getRepositoryType()
         return isPrivate;
     }
 
+    public String getRepositoryUrl()
+    {
+        return repositoryUrl;
+    }
+    
+    public List<String> getValidationErrors()
+    {
+        return validationErrors;
+    }
+
+    public String getProjectKey()
+    {
+        return projectKey;
+    }
 }

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/bitbucket/BitbucketChangesetFactory.java

File contents unchanged.

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/bitbucket/impl/BitbucketCommunicator.java

     }
 
     @Override
-    public UrlInfo getUrlInfo(final RepositoryUri repositoryUri)
+    public UrlInfo getUrlInfo(final RepositoryUri repositoryUri, String projectKey)
     {
         logger.debug("Get repository info in bitbucket [ {} ]", repositoryUri.getRepositoryUrl());
         Boolean repositoryPrivate = requestHelper.isRepositoryPrivate1(repositoryUri);
         if (repositoryPrivate == null) return null;
-        return new UrlInfo(BitbucketRepositoryManager.BITBUCKET, repositoryPrivate.booleanValue());
+        return new UrlInfo(BitbucketRepositoryManager.BITBUCKET, repositoryPrivate.booleanValue(), repositoryUri.getRepositoryUrl(), projectKey);
     }
 
     @Override
-    public String getRepositoryName(String repositoryType, String projectKey, RepositoryUri repositoryUri, String username, String password,
+    public String getRepositoryName(String repositoryType, String projectKey, RepositoryUri repositoryUri,
                                     String adminUsername, String adminPassword, String accessToken) throws SourceControlException
     {
 
         Authentication auth;
-        if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password))
+        if (StringUtils.isNotBlank(adminUsername) && StringUtils.isNotBlank(adminUsername))
         {
-            auth = new BasicAuthentication(username, password);
+            auth = new BasicAuthentication(adminUsername, adminUsername);
         } else
         {
             auth = Authentication.ANONYMOUS;

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/bitbucket/impl/BitbucketRepositoryManager.java

File contents unchanged.

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/bitbucket/webwork/AddBitbucketRepository.java

 package com.atlassian.jira.plugins.bitbucket.spi.bitbucket.webwork;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Qualifier;
-
 import com.atlassian.jira.plugins.bitbucket.Synchronizer;
 import com.atlassian.jira.plugins.bitbucket.api.SourceControlException;
 import com.atlassian.jira.plugins.bitbucket.api.SourceControlException.UnauthorisedException;
 import com.atlassian.jira.plugins.bitbucket.spi.bitbucket.impl.BitbucketRepositoryManager;
 import com.atlassian.jira.security.xsrf.RequiresXsrfCheck;
 import com.atlassian.jira.web.action.JiraWebActionSupport;
-import com.atlassian.sal.api.ApplicationProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
 
 /**
  * Webwork action used to configure the bitbucket repositories
     private String isPrivate;
     private String adminUsername = "";
     private String adminPassword = "";
-    private String bbUsername = "";
-    private String bbPassword = "";
-    private String addPostCommitService = "";
-    private String postCommitUrl;
 
     private final RepositoryManager globalRepositoryManager;
     private final Synchronizer synchronizer;
-    private final String baseUrl;
 
 
     public AddBitbucketRepository(@Qualifier("globalRepositoryManager") RepositoryManager globalRepositoryManager, 
-        Synchronizer synchronizer, ApplicationProperties applicationProperties)
+        Synchronizer synchronizer)
     {
         this.globalRepositoryManager = globalRepositoryManager;
         this.synchronizer = synchronizer;
-        this.baseUrl = applicationProperties.getBaseUrl();
-    }
-
-    @Override
-    protected void doValidation()
-    {
     }
 
     @Override
     @RequiresXsrfCheck
     protected String doExecute() throws Exception
     {
-        if (!addPostCommitService())
-        {
-            adminUsername = "";
-            adminPassword = "";
-        }
         SourceControlRepository repository;
         try
         {
-            repository = globalRepositoryManager.addRepository(BitbucketRepositoryManager.BITBUCKET, projectKey, repositoryUrl, bbUsername, bbPassword,
-                adminUsername, adminPassword, "");
+            repository = globalRepositoryManager.addRepository(BitbucketRepositoryManager.BITBUCKET, projectKey, repositoryUrl, adminUsername, adminPassword, "");
             synchronizer.synchronize(repository);
         } catch (UnauthorisedException e)
         {
             log.debug("Failed adding the repository: ["+e.getMessage()+"]");
             return INPUT;
         }
+        
         try
         {
-            if (addPostCommitService())
-            {
-                globalRepositoryManager.setupPostcommitHook(repository);
-            }
+            globalRepositoryManager.setupPostcommitHook(repository);
         } catch (SourceControlException e)
         {
             log.debug("Failed adding postcommit hook: ["+e.getMessage()+"]");
-            postCommitUrl = baseUrl + "/rest/bitbucket/1.0/repository/" + repository.getId() + "/sync";
-            return ERROR;
+            globalRepositoryManager.removeRepository(repository.getId());
+            addErrorMessage("Error adding postcommit hook. Do you have admin rights to the repository? <br/> Repository was not added. ["+e.getMessage()+"]");
+            return INPUT;
         }
 
         return getRedirect("ConfigureBitbucketRepositories.jspa?addedRepositoryId="+repository.getId()+"&atl_token=" + getXsrfToken());
         this.isPrivate = isPrivate;
     }
 
-    public String getAdminUsername()
-    {
+    public String getAdminUsername() {
         return adminUsername;
     }
 
-    public void setAdminUsername(String adminUsername)
-    {
+    public void setAdminUsername(String adminUsername) {
         this.adminUsername = adminUsername;
     }
 
-    public String getAdminPassword()
-    {
+    public String getAdminPassword() {
         return adminPassword;
     }
 
-    public void setAdminPassword(String adminPassword)
-    {
+    public void setAdminPassword(String adminPassword) {
         this.adminPassword = adminPassword;
     }
-
-    public String getBbUsername()
-    {
-        return bbUsername;
-    }
-
-    public void setBbUsername(String bbUsername)
-    {
-        this.bbUsername = bbUsername;
-    }
-
-    public String getBbPassword()
-    {
-        return bbPassword;
-    }
-
-    public void setBbPassword(String bbPassword)
-    {
-        this.bbPassword = bbPassword;
-    }
-
-    public String getPostCommitUrl()
-    {
-        return postCommitUrl;
-    }
-
-    public void setPostCommitUrl(String postCommitUrl)
-    {
-        this.postCommitUrl = postCommitUrl;
-    }
-
-    public boolean addPostCommitService()
-    {
-        return addPostCommitService != null && (addPostCommitService.toLowerCase().equals("on") || addPostCommitService.toLowerCase().equals("true"));
-    }
-
-    public void setAddPostCommitService(String addPostCommitService)
-    {
-        this.addPostCommitService = addPostCommitService;
-    }
 }

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/github/GithubChangesetFactory.java

 {
 
     /**
-     * Parse the json object as a bitbucket changeset
+     * Parse the json object from GitHub v2. API as a changeset. We need only minimal information from that.
+     *
+     * @param json  the json object describing the change
+     * @return the parsed {@link com.atlassian.jira.plugins.bitbucket.api.Changeset} with minimal fields
+     */
+    public static Changeset parseV2(int repositoryId, JSONObject commitJson) throws JSONException
+    {
+
+        String id = commitJson.getString("id");
+        String msg = commitJson.getString("message");
+        Date date = parseDate(commitJson.getString("committed_date"));
+
+        return new DefaultChangeset(repositoryId, id, msg, date);
+    }
+
+
+    /**
+     * Parse the json object from GitHub v3. API as a changeset
      *
      * @param json  the json object describing the change
      * @return the parsed {@link com.atlassian.jira.plugins.bitbucket.api.Changeset}
      */
-    public static Changeset parse(int repositoryId, String branch, JSONObject json)
+    public static Changeset parseV3(int repositoryId, String branch, JSONObject json)
     {
         try
         {
                 login = author.has("login") ? author.getString("login") : "";
             }
 
-            List<ChangesetFile> changesetFiles = fileList(json.getJSONArray("files"), false);
+            List<ChangesetFile> changesetFiles = fileList(json.getJSONArray("files"));
 
             return new DefaultChangeset(
                     repositoryId,
         return list;
     }
 
-    private static List<ChangesetFile> fileList(JSONArray files, boolean fromPostcommitHook) throws JSONException
+    private static List<ChangesetFile> fileList(JSONArray files) throws JSONException
     {
         List<ChangesetFile> list = new ArrayList<ChangesetFile>();
 

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/github/impl/GithubChangesetIterator.java

         currentPageNumber++;
         changesets = githubCommunicator.getChangesets(repository, branch, currentPageNumber);
         return containsChangesets();
-
     }
 
     private boolean containsChangesets() {
 class BranchesIterator implements Iterator<PagesIterator>
 {
 
-
     private ListIterator<String> branchNamesIterator = Collections.<String>emptyList().listIterator();
     private GithubCommunicator githubCommunicator;
     private SourceControlRepository repository;

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/github/impl/GithubCommunicator.java

                     CustomStringUtils.encode(slug) + "/commits/" + CustomStringUtils.encode(id), null,
                     apiUrl);
 
-            return GithubChangesetFactory.parse(repository.getId(), "master", new JSONObject(responseString));
+            return GithubChangesetFactory.parseV3(repository.getId(), "master", new JSONObject(responseString));
         } catch (ResponseException e)
         {
             throw new SourceControlException("could not get result", e);
                 for (int i = 0; i < list.length(); i++)
                 {
                     JSONObject commitJson = list.getJSONObject(i);
-                    String id = commitJson.getString("id");
-                    String msg = commitJson.getString("message");
-
-                    changesets.add(new DefaultChangeset(repository.getId(), id, msg));
+                    changesets.add(GithubChangesetFactory.parseV2(repository.getId(), commitJson));
                 }
             } else
             {
     }
 
     @Override
-    public UrlInfo getUrlInfo(final RepositoryUri repositoryUri)
+    public UrlInfo getUrlInfo(final RepositoryUri repositoryUri, String projectKey)
     {
         log.debug("get repository info in bitbucket [ {} ]", repositoryUri.getRepositoryUrl());
         Boolean repositoryPrivate = requestHelper.isRepositoryPrivate1(repositoryUri);
         if (repositoryPrivate == null) return null;
-        return new UrlInfo(GithubRepositoryManager.GITHUB, repositoryPrivate.booleanValue());
+        return new UrlInfo(GithubRepositoryManager.GITHUB, repositoryPrivate.booleanValue(), repositoryUri.getRepositoryUrl(), projectKey);
     }
 
     private List<String> getBranches(SourceControlRepository repository)
     }
 
     @Override
-    public String getRepositoryName(String repositoryType, String projectKey, RepositoryUri repositoryUri, String username,
-                                    String password, String adminUsername, String adminPassword, String accessToken) throws SourceControlException
+    public String getRepositoryName(String repositoryType, String projectKey, RepositoryUri repositoryUri,
+        String adminUsername, String adminPassword, String accessToken) throws SourceControlException
     {
 
         Authentication auth;

src/main/java/com/atlassian/jira/plugins/bitbucket/spi/github/impl/GithubRepositoryManager.java

 package com.atlassian.jira.plugins.bitbucket.spi.github.impl;
 
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+
 import com.atlassian.jira.issue.IssueManager;
 import com.atlassian.jira.plugins.bitbucket.IssueLinker;
 import com.atlassian.jira.plugins.bitbucket.api.Changeset;
 import com.atlassian.jira.plugins.bitbucket.spi.Communicator;
 import com.atlassian.jira.plugins.bitbucket.spi.DvcsRepositoryManager;
 import com.atlassian.jira.plugins.bitbucket.spi.RepositoryUri;
+import com.atlassian.jira.plugins.bitbucket.spi.UrlInfo;
+import com.atlassian.jira.plugins.bitbucket.spi.github.GithubOAuth;
 import com.atlassian.jira.util.json.JSONArray;
 import com.atlassian.jira.util.json.JSONException;
 import com.atlassian.jira.util.json.JSONObject;
 import com.atlassian.sal.api.ApplicationProperties;
 import com.atlassian.templaterenderer.TemplateRenderer;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Qualifier;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
 
 public class GithubRepositoryManager extends DvcsRepositoryManager
 {
 
     public static final String GITHUB = "github";
 
+    private final GithubOAuth githubOAuth;
+
     public GithubRepositoryManager(RepositoryPersister repositoryPersister, @Qualifier("githubCommunicator") Communicator communicator,
-                                   Encryptor encryptor, ApplicationProperties applicationProperties, IssueLinker issueLinker,
-                                   TemplateRenderer templateRenderer, IssueManager issueManager)
+        Encryptor encryptor, ApplicationProperties applicationProperties, IssueLinker issueLinker,
+        TemplateRenderer templateRenderer, IssueManager issueManager, GithubOAuth githubOAuth)
     {
         super(communicator, repositoryPersister, encryptor, applicationProperties, issueLinker, templateRenderer, issueManager);
+        this.githubOAuth = githubOAuth;
     }
 
     @Override
         return GITHUB;
     }
 
+    public UrlInfo validateUrlInfo(UrlInfo urlInfo)
+    {
+        urlInfo = super.validateUrlInfo(urlInfo);
+        if (StringUtils.isBlank(githubOAuth.getClientId()) || StringUtils.isBlank(githubOAuth.getClientSecret()))
+        {
+            String baseUrl = getApplicationProperties().getBaseUrl();
+            urlInfo.addValidationError("<a href='"+baseUrl+"/secure/admin/ConfigureGithubOAuth!default.jspa'>GitHub OAuth Settings</a> have to be configured before adding GitHub repository");
+        }
+        return urlInfo;
+    }
+
     @Override
     public RepositoryUri getRepositoryUri(String urlString)
     {