diff --git a/versioncontrol_project_issue/plugins/event_processor/VersioncontrolEventProcessorGitIssueMapper.inc b/versioncontrol_project_issue/plugins/event_processor/VersioncontrolEventProcessorGitIssueMapper.inc deleted file mode 100644 index d019d2a..0000000 --- a/versioncontrol_project_issue/plugins/event_processor/VersioncontrolEventProcessorGitIssueMapper.inc +++ /dev/null @@ -1,134 +0,0 @@ - $event->getRepository()->repo_id))->fetchField(); - if (!$project_nid) { - // No project associated. - return; - } - // We are depending on getCommitInterval(), so check we are using the right - // synchronization plugin. - $synchronizer = $event->getRepository()->getSynchronizer(); - if (!method_exists($synchronizer, 'getCommitInterval')) { - watchdog('versioncontrol_project_issue', 'git_issue_mapper: Cannot use a repository synchronizer of type %type', array('%type' => get_class($synchronizer)), WATCHDOG_WARNING); - return; - } - $operation_nid_maps = array(); - foreach ($event as $ref) { - if ($ref->reftype != VERSIONCONTROL_GIT_REFTYPE_BRANCH) { - // Only process branch refs for now. - return; - } - if ($ref->ff != 1) { - // @fixme support non-ff. - continue; - } - if ($ref->eventDeletedMe()) { - // Nothing to do. - // We could try to figure out orphaned commits from label removal. Let's - // trust on hook_versioncontrol_entity_commit_delete() for now. - continue; - } - // Usual ff ref update. - $commit_hashes = $synchronizer->getCommitInterval($ref->old_sha1, $ref->new_sha1); - // @todo Try to deal with big pushes loading operations in batches. - $related_operations = $event->getRepository()->loadCommits(array(), array('revision' => $commit_hashes)); - foreach ($related_operations as $operation) { - $nids_on_message = $this->getIssuesFromMessage($operation->message); - if (count($nids_on_message) < 1) { - // Nothing to do. - continue; - } - // Make sure we are dealing with project issues. - $possible_issues = node_load_multiple($nids_on_message); - foreach ($possible_issues as $possible_issue) { - if (!project_issue_node_is_issue($possible_issue)) { - continue; - } - // Associate only if repository project is the same than issue - // project. - if (empty($possible_issue->field_project[LANGUAGE_NONE][0]['target_id'])) { - continue; - } - if ($possible_issue->field_project[LANGUAGE_NONE][0]['target_id'] != $project_nid) { - continue; - } - $operation_nid_maps[] = array( - 'nid' => $possible_issue->nid, - 'vc_op_id' => $operation->vc_op_id - ); - } - } - } - if (empty($operation_nid_maps)) { - // Nothing to do. - return; - } - // Insert mappings. - $insert_query = db_insert('versioncontrol_project_issue_operations')->fields(array('nid', 'vc_op_id')); - foreach ($operation_nid_maps as $operation_nid_map) { - $insert_query->values($operation_nid_map); - } - $insert_query->execute(); - } - - public function setRepository(VersioncontrolRepository $repository) { - $this->repository = $repository; - } - - public function setConfiguration($default_data) { - $this->messagePattern = !empty($default_data['message_pattern']) ? $default_data['message_pattern'] : ''; - } - - public function buildForm($default_data) { - $form = array(); - $form['event-processor-issue-mapper-pattern'] = array( - '#type' => 'textfield', - '#title' => t('Extract pattern'), - '#description' => t('A pattern to be used to extract the issue ids from the commit message. i.e. "/#(\d+)\b/"'), - '#default_value' => !empty($default_data['message_pattern']) ? $default_data['message_pattern'] : '', - ); - return $form; - } - - public function getFormData($form_state_values_data) { - return array( - 'message_pattern' => $form_state_values_data['event-processor-issue-mapper-pattern'], - ); - } - - public function submitForm(&$form, &$form_state) { - // Nothing special. - } - - /** - * Retrieve the issues from operation message. - */ - function getIssuesFromMessage($message) { - if (preg_match_all($this->messagePattern, $message, $matches)) { - return $matches[1]; - } - return array(); - } -} diff --git a/versioncontrol_project_issue/plugins/event_processor/git_issue_mapper.inc b/versioncontrol_project_issue/plugins/event_processor/git_issue_mapper.inc deleted file mode 100644 index 6eb17ae..0000000 --- a/versioncontrol_project_issue/plugins/event_processor/git_issue_mapper.inc +++ /dev/null @@ -1,14 +0,0 @@ - 'git', - 'title' => t('Issue mapper for git repositories'), - 'handler' => array( - 'class' => 'VersioncontrolEventProcessorGitIssueMapper', - 'file' => 'VersioncontrolEventProcessorGitIssueMapper.inc', - 'path' => drupal_get_path('module', 'versioncontrol_project_issue') . '/plugins/event_processor', - ), -); diff --git a/versioncontrol_project_issue/versioncontrol_project_issue.install b/versioncontrol_project_issue/versioncontrol_project_issue.install index 1d873d7..c519135 100644 --- a/versioncontrol_project_issue/versioncontrol_project_issue.install +++ b/versioncontrol_project_issue/versioncontrol_project_issue.install @@ -12,7 +12,7 @@ function versioncontrol_project_issue_schema() { 'description' => 'This table associates project issue nodes with versioncontrol operations.', 'fields' => array( 'nid' => array( - 'description' => 'Foreign key for the project issue({project_issues}.nid).', + 'description' => 'Foreign key for the project issue nid.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, diff --git a/versioncontrol_project_issue/versioncontrol_project_issue.module b/versioncontrol_project_issue/versioncontrol_project_issue.module index 98280e6..2afb20b 100644 --- a/versioncontrol_project_issue/versioncontrol_project_issue.module +++ b/versioncontrol_project_issue/versioncontrol_project_issue.module @@ -1,19 +1,10 @@ project_nid)) { - // No project associated. - return; - } - - // Load all associated project issue nids. - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', 'node') - ->entityCondition('bundle', project_issue_issue_node_types()) - ->fieldCondition('field_project', 'target_id', $repository->project_nid); - $result = $query->execute(); - if (empty($result['node'])) { - // No issues associated. - return; - } - $issue_nids = array_keys($result['node']); - // Skip full operation loading, and do it manually for performance reasons. - $vc_op_ids_by_id = db_query('SELECT vco.vc_op_id, vco.revision FROM {versioncontrol_operations} vco JOIN {versioncontrol_project_projects} vcp WHERE vcp.repo_id = :repo_id', array(':repo_id' => $repository->project_nid))->fetchAllKeyed(); - $vc_op_ids_by_issue_nid = versioncontrol_project_issue_get_issue_operation_ids($issue_nids); - $commit_hashes_by_issue_nid = array(); - foreach ($vc_op_ids_by_issue_nid as $issue_nid => $vc_op_ids) { - foreach ($vc_op_ids as $vc_op_id) { - $commit_hashes_by_issue_nid[$issue_nid][] = $vc_op_ids_by_id[$vc_op_id]; - } - } - - // Only set the cache if there were results that'll need refreshing later - if (!empty($commit_hashes_by_issue_nid)) { - drupal_static('versioncontrol_project_issue_git_resync_recover_' . $repository->repo_id, $commit_hashes_by_issue_nid); - // @fixme Convert to simple query. to avoid passing possible large amount of nodes. - db_delete('versioncontrol_project_issue_operations')->condition('nid', $issue_nids, 'IN')->execute(); - } -} - -/** - * Implements hook_versioncontrol_repository_post_resync(). - * - * @fixme Move to an specific git module. - */ -function versioncontrol_project_issue_versioncontrol_repository_post_resync(VersioncontrolRepository $repository, $bypass) { - $commit_hashes_by_issue_nid = &drupal_static('versioncontrol_project_issue_git_resync_recover_' . $repository->repo_id); - if (empty($commit_hashes_by_issue_nid)) { - // Nothing to do. - return; - } - $data_to_insert = FALSE; - $insert_query = db_insert('versioncontrol_project_issue_operations')->fields(array('nid', 'vc_op_id')); - foreach ($commit_hashes_by_issue_nid as $issue_nid => $commit_hashes) { - // Add data only if the commits are still there. - foreach ($repository->loadCommits(array(), array('revision' => $commit_hashes)) as $vc_op_id => $operation) { - $data_to_insert = TRUE; - $insert_query->values(array('nid' => $issue_nid, 'vc_op_id' => $operation->vc_op_id)); - } - } - if ($data_to_insert) { - $insert_query->execute(); - } - drupal_static_reset('versioncontrol_project_issue_git_resync_recover_' . $repository->repo_id); -} - -/** * Implements hook_versioncontrol_repository_bypassing_purge(). * - * @fixme db_delete seems to not support joins, is that possible to use for the + * @todo db_delete seems to not support joins, is that possible to use for the * generic case? i.e. no mysql(should be more efficient, in case a lot of * issues are associated). */ @@ -172,7 +93,10 @@ function versioncontrol_project_issue_versioncontrol_repository_bypassing_purge( } // Remove related rows from versioncontrol_project_issue_operations table. $issue_nids = array_keys($result['node']); - db_delete('versioncontrol_project_issue_operations')->condition('nid', $issue_nids, 'IN')->execute(); + // Do it in chunks of 500 items. + foreach (array_chunk($issue_nids, 500) as $chunk) { + db_delete('versioncontrol_project_issue_operations')->condition('nid', array_values($chunk), 'IN')->execute(); + } } /** diff --git a/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/VersioncontrolEventProcessorGitIssueMapper.inc b/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/VersioncontrolEventProcessorGitIssueMapper.inc new file mode 100644 index 0000000..9164b17 --- /dev/null +++ b/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/VersioncontrolEventProcessorGitIssueMapper.inc @@ -0,0 +1,136 @@ + $event->getRepository()->repo_id))->fetchField(); + if (!$project_nid) { + // No project associated. + return; + } + // We are depending on getCommitInterval(), so check we are using the right + // synchronization plugin. + $synchronizer = $event->getRepository()->getSynchronizer(); + if (!method_exists($synchronizer, 'getCommitInterval')) { + watchdog('versioncontrol_project_issue', 'git_issue_mapper: Cannot use a repository synchronizer of type %type', array('%type' => get_class($synchronizer)), WATCHDOG_WARNING); + return; + } + $operation_nid_maps = array(); + foreach ($event as $ref) { + if ($ref->reftype != VERSIONCONTROL_GIT_REFTYPE_BRANCH) { + // Only process branch refs for now. + return; + } + if ($ref->ff != 1) { + // @fixme support non-ff. + continue; + } + if ($ref->eventDeletedMe()) { + // Nothing to do. + // We could try to figure out orphaned commits from label removal. Let's + // trust on hook_versioncontrol_entity_commit_delete() for now. + continue; + } + // Usual ff ref update. + $commit_hashes = $synchronizer->getCommitInterval($ref->old_sha1, $ref->new_sha1); + // Try to deal with big pushes loading operations in chunks of 500 items. + foreach (array_chunk($commit_hashes, 500) as $commit_hashes_chunk) { + $related_operations = $event->getRepository()->loadCommits(array(), array('revision' => $commit_hashes_chunk)); + foreach ($related_operations as $operation) { + $nids_on_message = $this->getIssuesFromMessage($operation->message); + if (count($nids_on_message) < 1) { + // Nothing to do. + continue; + } + // Make sure we are dealing with project issues. + $possible_issues = node_load_multiple($nids_on_message); + foreach ($possible_issues as $possible_issue) { + if (!project_issue_node_is_issue($possible_issue)) { + continue; + } + // Associate only if repository project is the same than issue + // project. + if (empty($possible_issue->field_project[LANGUAGE_NONE][0]['target_id'])) { + continue; + } + if ($possible_issue->field_project[LANGUAGE_NONE][0]['target_id'] != $project_nid) { + continue; + } + $operation_nid_maps[] = array( + 'nid' => $possible_issue->nid, + 'vc_op_id' => $operation->vc_op_id + ); + } + } + } + } + if (empty($operation_nid_maps)) { + // Nothing to do. + return; + } + // Insert mappings. + $insert_query = db_insert('versioncontrol_project_issue_operations')->fields(array('nid', 'vc_op_id')); + foreach ($operation_nid_maps as $operation_nid_map) { + $insert_query->values($operation_nid_map); + } + $insert_query->execute(); + } + + public function setRepository(VersioncontrolRepository $repository) { + $this->repository = $repository; + } + + public function setConfiguration($default_data) { + $this->messagePattern = !empty($default_data['message_pattern']) ? $default_data['message_pattern'] : ''; + } + + public function buildForm($default_data) { + $form = array(); + $form['event-processor-issue-mapper-pattern'] = array( + '#type' => 'textfield', + '#title' => t('Extract pattern'), + '#description' => t('A pattern to be used to extract the issue ids from the commit message. i.e. "/#(\d+)\b/"'), + '#default_value' => !empty($default_data['message_pattern']) ? $default_data['message_pattern'] : '', + ); + return $form; + } + + public function getFormData($form_state_values_data) { + return array( + 'message_pattern' => $form_state_values_data['event-processor-issue-mapper-pattern'], + ); + } + + public function submitForm(&$form, &$form_state) { + // Nothing special. + } + + /** + * Retrieve the issues from operation message. + */ + function getIssuesFromMessage($message) { + if (preg_match_all($this->messagePattern, $message, $matches)) { + return $matches[1]; + } + return array(); + } +} diff --git a/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/git_issue_mapper.inc b/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/git_issue_mapper.inc new file mode 100644 index 0000000..2f9fec3 --- /dev/null +++ b/versioncontrol_project_issue/versioncontrol_project_issue_git/plugins/event_processor/git_issue_mapper.inc @@ -0,0 +1,14 @@ + 'git', + 'title' => t('Issue mapper for git repositories'), + 'handler' => array( + 'class' => 'VersioncontrolEventProcessorGitIssueMapper', + 'file' => 'VersioncontrolEventProcessorGitIssueMapper.inc', + 'path' => drupal_get_path('module', 'versioncontrol_project_issue_git') . '/plugins/event_processor', + ), +); diff --git a/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.info b/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.info new file mode 100644 index 0000000..4c1ef66 --- /dev/null +++ b/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.info @@ -0,0 +1,7 @@ +name = "Version Control / Project issue integration - Git" +description = "Expose some functionality that integrates project_issue and Version Control API Git backend." +package = Version Control +dependencies[] = versioncontrol_project_issue +dependencies[] = versioncontrol_git +dependencies[] = project_issue +core = 7.x diff --git a/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.module b/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.module new file mode 100644 index 0000000..54e3cbc --- /dev/null +++ b/versioncontrol_project_issue/versioncontrol_project_issue_git/versioncontrol_project_issue_git.module @@ -0,0 +1,92 @@ +project_nid)) { + // No project associated. + return; + } + if (!$repository instanceof VersioncontrolGitRepository) { + // Not a git repository. + return; + } + + // Load all associated project issue nids. + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'node') + ->entityCondition('bundle', project_issue_issue_node_types()) + ->fieldCondition('field_project', 'target_id', $repository->project_nid); + $result = $query->execute(); + if (empty($result['node'])) { + // No issues associated. + return; + } + $issue_nids = array_keys($result['node']); + // Skip full operation loading, and do it manually for performance reasons. + $vc_op_ids_by_id = db_query('SELECT vco.vc_op_id, vco.revision FROM {versioncontrol_operations} vco JOIN {versioncontrol_project_projects} vcp WHERE vcp.repo_id = :repo_id', array(':repo_id' => $repository->project_nid))->fetchAllKeyed(); + $vc_op_ids_by_issue_nid = versioncontrol_project_issue_get_issue_operation_ids($issue_nids); + $commit_hashes_by_issue_nid = array(); + foreach ($vc_op_ids_by_issue_nid as $issue_nid => $vc_op_ids) { + foreach ($vc_op_ids as $vc_op_id) { + $commit_hashes_by_issue_nid[$issue_nid][] = $vc_op_ids_by_id[$vc_op_id]; + } + } + + // Only set the cache if there were results that'll need refreshing later + if (!empty($commit_hashes_by_issue_nid)) { + drupal_static('versioncontrol_project_issue_git_resync_recover_' . $repository->repo_id, $commit_hashes_by_issue_nid); + // @todo Use delete query with join? + // Do it in chunks of 500 items. + foreach (array_chunk($issue_nids, 500) as $chunk) { + db_delete('versioncontrol_project_issue_operations')->condition('nid', array_values($chunk), 'IN')->execute(); + } + } +} + +/** + * Implements hook_versioncontrol_repository_post_resync(). + */ +function versioncontrol_project_issue_git_versioncontrol_repository_post_resync(VersioncontrolRepository $repository, $bypass) { + if (!$repository instanceof VersioncontrolGitRepository) { + // Not a git repository. + return; + } + $commit_hashes_by_issue_nid = &drupal_static('versioncontrol_project_issue_git_resync_recover_' . $repository->repo_id); + if (empty($commit_hashes_by_issue_nid)) { + // Nothing to do. + return; + } + $data_to_insert = FALSE; + $insert_query = db_insert('versioncontrol_project_issue_operations')->fields(array('nid', 'vc_op_id')); + foreach ($commit_hashes_by_issue_nid as $issue_nid => $commit_hashes) { + // Add data only if the commits are still there. + foreach ($repository->loadCommits(array(), array('revision' => $commit_hashes)) as $vc_op_id => $operation) { + $data_to_insert = TRUE; + $insert_query->values(array('nid' => $issue_nid, 'vc_op_id' => $operation->vc_op_id)); + } + } + if ($data_to_insert) { + $insert_query->execute(); + } + drupal_static_reset('versioncontrol_project_issue_git_resync_recover_' . $repository->repo_id); +}