/** * Retrieve all items that were affected by an operation. * * @param $fetch_source_items * If TRUE, source and replaced items will be retrieved as well, * and stored as additional properties inside each item array. * If FALSE, only current/new items will be retrieved. * If NULL (default), source and replaced items will be retrieved for commits * but not for branch or tag operations. * * @return * A structured array containing all items that were affected by the given * operation. Array keys are the current/new paths, even if the item doesn't * exist anymore (as is the case with delete actions in commits). * The associated array elements are structured item arrays and consist of * the following elements: * * - 'type': Specifies the item type, which is either * VERSIONCONTROL_ITEM_FILE or VERSIONCONTROL_ITEM_DIRECTORY for items * that still exist, or VERSIONCONTROL_ITEM_FILE_DELETED respectively * VERSIONCONTROL_ITEM_DIRECTORY_DELETED for items that have been * removed (by a commit's delete action). * - 'path': The path of the item at the specific revision. * - 'revision': The (file-level) revision when the item was changed. * If there is no such revision (which may be the case for * directory items) then the 'revision' element is an empty string. * - 'item_revision_id': Identifier of this item revision in the database. * Note that you can only rely on this element to exist for * operation items - functions that interface directly with the VCS * (such as VersioncontrolItem::getDirectoryContents() or * VersioncontrolItem::getParallelItems()) might not include * this identifier, for obvious reasons. * * If the @p $fetch_source_items parameter is TRUE, * versioncontrol_fetch_source_items() will be called on the list of items * in order to retrieve additional information about their origin. * The following elements will be set for each item in addition * to the ones listed above: * * - 'action': Specifies how the item was changed. * One of the predefined VERSIONCONTROL_ACTION_* values. * - 'source_items': An array with the previous revision(s) of the affected * item. Empty if 'action' is VERSIONCONTROL_ACTION_ADDED. The key for * all items in this array is the respective item path. * - 'replaced_item': The previous but technically unrelated item at the * same location as the current item. Only exists if this previous item * was deleted and replaced by a different one that was just moved * or copied to this location. * - 'line_changes': Only exists if line changes have been recorded for this * action - if so, this is an array containing the number of added lines * in an element with key 'added', and the number of removed lines in * the 'removed' key. * FIXME refactor me to oo */ public function getItems($fetch_source_items = NULL) { $items = array(); $result = db_query('SELECT ir.item_revision_id, ir.path, ir.revision, ir.type FROM {versioncontrol_operation_items} opitem INNER JOIN {versioncontrol_item_revisions} ir ON opitem.item_revision_id = ir.item_revision_id WHERE opitem.vc_op_id = %d AND opitem.type = %d', $this->vc_op_id, VERSIONCONTROL_OPERATION_MEMBER_ITEM); while ($item_revision = db_fetch_object($result)) { $items[$item_revision->path] = new $this->repository->backend->classes['item']($item_revision->type, $item_revision->path, $item_revision->revision, NULL, $this->repository, NULL, $item_revision->item_revision_id); $items[$item_revision->path]->selected_label = new stdClass(); $items[$item_revision->path]->selected_label->get_from = 'operation'; $items[$item_revision->path]->selected_label->operation =& $this; //TODO inherit from operation class insteadof types? if ($this->type == VERSIONCONTROL_OPERATION_COMMIT) { $items[$item_revision->path]->commit_operation = $this; } } if (!isset($fetch_source_items)) { // By default, fetch source items for commits but not for branch or tag ops. $fetch_source_items = $this->type == VERSIONCONTROL_OPERATION_COMMIT; } if ($fetch_source_items) { versioncontrol_fetch_source_items($this->repository, $items); } ksort($items); // similar paths should be next to each other return $items; }
/** * Retrieve the revisions where the given item has been changed, * in reverse chronological order. * * Only one direct source or successor of each item will be retrieved, * which means that you won't get parallel history logs with a single * function call. In order to retrieve the log for this item in a * different branch, you need to switch the selected label of the item * by retrieving a different version of it with a call of * Item::getParallelItems() (if the backend supports this function). * * TODO: params doc * * @return * An array containing a list of item arrays, each one specifying a * revision of the same item that was given as argument. The array is * sorted in reverse chronological order, so the newest revision * comes first. Each element has its (file-level) item revision as * key, and a standard item object (as the ones retrieved by * VersioncontrolOperation::getItems()) as value. All items except * for the oldest one will also have the 'action' and 'source_items' * properties filled in, the oldest item might or might not have * them. (If they exist for the oldest item, 'action' will be * VERSIONCONTROL_ACTION_ADDED and 'source_items' an empty array.) * * NULL is returned if the given item is not under version control, * or was not under version control at the time of the given * revision, or if no history could be retrieved for any other * reason. */ public function getHistory($successor_item_limit = NULL, $source_item_limit = NULL) { // Items without revision have no history, don't even try to fetch it. if (empty($this->revision)) { return NULL; } // If we don't yet know the item_revision_id (required for db // queries), try to retrieve it. If we don't find it, we can't go on // with this function. if (!$this->fetchItemRevisionId()) { return NULL; } // Make sure we don't run into infinite loops when passed bad // arguments. if (is_numeric($successor_item_limit) && $successor_item_limit < 0) { $successor_item_limit = 0; } if (is_numeric($source_item_limit) && $source_item_limit < 0) { $source_item_limit = 0; } // Naive implementation - can probably be improved by sticking to // the samerepo_id/path until an action other than "modified" or // "other" appears. (With the drawback that code will probably need // to be duplicated among this function and // versioncontrol_fetch_{source,successor}_items(). // Find (recursively) all successor items within the successor item // limit. $history_successor_items = array(); $source_item = $this; while (!isset($successor_item_limit) || $successor_item_limit > 0) { $source_items = array($source_item->path => $source_item); versioncontrol_fetch_successor_items($this->repository, $source_items); $source_item = $source_items[$source_item->path]; // If there are no successor items, we are obviously at the end of // the log. if (empty($source_item->successor_items)) { break; } // There might be multiple successor items - in most cases, the // first one is the only one so that's ok except for "merged" // actions. $successor_item = NULL; $highest_priority_so_far = 0; foreach ($source_item->successor_items as $path => $succ_item) { if (!isset($successor_item) || self::$successor_action_priority[$succ_item->action] > $highest_priority_so_far) { $successor_item = $succ_item; $highest_priority_so_far = self::$successor_action_priority[$succ_item->action]; } } $history_successor_items[$successor_item->revision] = $successor_item; $source_item = $successor_item; // Decrement the counter until the item limit is reached. if (isset($successor_item_limit)) { --$successor_item_limit; } } // We want the newest revisions first, so reverse the successor array. $history_successor_items = array_reverse($history_successor_items, TRUE); // Find (recursively) all source items within the source item limit. $history_source_items = array(); $successor_item = $this; while (!isset($source_item_limit) || $source_item_limit > 0) { $successor_items = array($successor_item->path => $successor_item); versioncontrol_fetch_source_items($repository, $successor_items); $successor_item = $successor_items[$successor_item->path]; // If there are no source items, we are obviously at the end of the log. if (empty($successor_item->source_items)) { break; } // There might be multiple source items - in most cases, the first one is // the only one so that's ok except for "merged" actions. $source_item = NULL; if ($successor_item->action == VERSIONCONTROL_ACTION_MERGED) { if (isset($successor_item->source_items[$successor_item->path])) { $source_item = $successor_item->source_items[$successor_item->path]; } } if (!isset($source_item)) { $source_item = reset($successor_item->source_items); // first item } $history_source_items[$source_item->revision] = $source_item; $successor_item = $source_item; // Decrement the counter until the item limit is reached. if (isset($source_item_limit)) { --$source_item_limit; } } return $history_successor_items + array($this->revision => $this) + $history_source_items; }