From 59776dd803c7fd9d603086e4ea9356f8aedb0e46 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 7 Jul 2021 18:08:30 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- GITLAB_SHELL_VERSION | 2 +- .../components/sidebar_dropdown_widget.vue | 27 +- app/assets/javascripts/sidebar/constants.js | 9 + .../javascripts/sidebar/mount_sidebar.js | 40 +- .../merge_request_milestone.query.graphql | 14 + .../queries/milestone.fragment.graphql | 1 + .../project_issue_milestone.mutation.graphql | 1 + .../queries/project_milestones.query.graphql | 8 +- ...e_merge_request_milestone.mutation.graphql | 17 + app/models/compare.rb | 4 + app/models/lfs_download_object.rb | 16 +- app/models/merge_request.rb | 2 +- app/models/merge_request/diff_commit_user.rb | 95 +++ app/models/merge_request_diff.rb | 2 +- app/models/merge_request_diff_commit.rb | 70 +- .../analytics/cycle_analytics/stage_entity.rb | 15 + .../lfs_download_link_list_service.rb | 2 + .../lfs_pointers/lfs_download_service.rb | 14 +- ...cess_token_about_to_expire_email.html.haml | 2 +- ...ccess_token_about_to_expire_email.text.erb | 2 +- .../access_token_expired_email.html.haml | 2 +- .../access_token_expired_email.text.erb | 2 +- app/views/shared/issuable/_sidebar.html.haml | 25 +- .../api_caching_repository_compare.yml | 8 + ...056_add_merge_request_diff_commit_users.rb | 31 + ..._merge_request_diff_commit_user_columns.rb | 25 + ..._user_signups_cap_to_namespace_settings.rb | 17 + ..._schedule_latest_pipeline_id_population.rb | 21 +- ...request_diff_users_background_migration.rb | 54 ++ ..._schedule_latest_pipeline_id_population.rb | 28 + db/schema_migrations/20210602155056 | 1 + db/schema_migrations/20210602155110 | 1 + db/schema_migrations/20210604133651 | 1 + db/schema_migrations/20210705132928 | 1 + db/schema_migrations/20210706142819 | 1 + db/structure.sql | 30 +- .../postgresql/replication_and_failover.md | 41 + ...age_group_dashboards_error_attribution.png | Bin 0 -> 166125 bytes ...ge_group_dashboards_service_sli_detail.png | Bin 0 -> 110562 bytes doc/development/stage_group_dashboards.md | 51 +- doc/update/index.md | 26 +- doc/user/packages/container_registry/index.md | 2 +- lib/api/repositories.rb | 6 +- ...migrate_merge_request_diff_commit_users.rb | 289 +++++++ .../import_export/project/import_export.yml | 8 +- .../import_export/project/object_builder.rb | 12 + .../import_export/project/relation_factory.rb | 5 +- locale/gitlab.pot | 10 +- qa/qa/page/component/issuable/sidebar.rb | 16 +- spec/db/schema_spec.rb | 1 + .../merge_request_diff_commit_users.rb | 8 + spec/features/issues/issue_sidebar_spec.rb | 30 +- spec/features/issues/user_edits_issue_spec.rb | 40 +- .../gitlab/import_export/complex/project.json | 770 ++++++++++++++++-- .../tree/project/merge_requests.ndjson | 16 +- spec/frontend/sidebar/mock_data.js | 3 + ...te_merge_request_diff_commit_users_spec.rb | 400 +++++++++ spec/lib/gitlab/import_export/all_models.yml | 2 + .../import_test_coverage_spec.rb | 4 +- .../project/object_builder_spec.rb | 26 + .../project/tree_restorer_spec.rb | 21 + .../import_export/safe_model_attributes.yml | 4 + ...st_diff_users_background_migration_spec.rb | 60 ++ ...ule_latest_pipeline_id_population_spec.rb} | 2 +- spec/models/compare_spec.rb | 10 +- spec/models/lfs_download_object_spec.rb | 50 +- .../merge_request/diff_commit_user_spec.rb | 127 +++ spec/models/merge_request_diff_commit_spec.rb | 50 ++ spec/requests/api/repositories_spec.rb | 11 + .../cycle_analytics/stage_entity_spec.rb | 8 + .../lfs_download_link_list_service_spec.rb | 13 +- .../lfs_pointers/lfs_download_service_spec.rb | 21 +- 72 files changed, 2497 insertions(+), 237 deletions(-) create mode 100644 app/assets/javascripts/sidebar/queries/merge_request_milestone.query.graphql create mode 100644 app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql create mode 100644 app/models/merge_request/diff_commit_user.rb create mode 100644 config/feature_flags/development/api_caching_repository_compare.yml create mode 100644 db/migrate/20210602155056_add_merge_request_diff_commit_users.rb create mode 100644 db/migrate/20210602155110_add_merge_request_diff_commit_user_columns.rb create mode 100644 db/migrate/20210705132928_add_new_user_signups_cap_to_namespace_settings.rb create mode 100644 db/post_migrate/20210604133651_schedule_merge_request_diff_users_background_migration.rb create mode 100644 db/post_migrate/20210706142819_re_schedule_latest_pipeline_id_population.rb create mode 100644 db/schema_migrations/20210602155056 create mode 100644 db/schema_migrations/20210602155110 create mode 100644 db/schema_migrations/20210604133651 create mode 100644 db/schema_migrations/20210705132928 create mode 100644 db/schema_migrations/20210706142819 create mode 100644 doc/development/img/stage_group_dashboards_error_attribution.png create mode 100644 doc/development/img/stage_group_dashboards_service_sli_detail.png create mode 100644 lib/gitlab/background_migration/migrate_merge_request_diff_commit_users.rb create mode 100644 spec/factories/merge_request_diff_commit_users.rb create mode 100644 spec/lib/gitlab/background_migration/migrate_merge_request_diff_commit_users_spec.rb create mode 100644 spec/migrations/20210604133651_schedule_merge_request_diff_users_background_migration_spec.rb rename spec/migrations/{schedule_latest_pipeline_id_population_spec.rb => re_schedule_latest_pipeline_id_population_spec.rb} (97%) create mode 100644 spec/models/merge_request/diff_commit_user_spec.rb diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index 7fcd2376974..dc18eab7e57 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -13.20.0 +13.19.0 diff --git a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue index 44fd1eac08d..141a5f34864 100644 --- a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue +++ b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue @@ -29,6 +29,7 @@ export default { issuableAttributesQueries, i18n: { [IssuableAttributeType.Milestone]: __('Milestone'), + expired: __('(expired)'), none: __('None'), }, directives: { @@ -74,9 +75,14 @@ export default { type: String, required: true, validator(value) { - return value === IssuableType.Issue; + return [IssuableType.Issue, IssuableType.MergeRequest].includes(value); }, }, + icon: { + type: String, + required: false, + default: undefined, + }, }, apollo: { currentAttribute: { @@ -172,6 +178,9 @@ export default { attributeTypeTitle() { return this.$options.i18n[this.issuableAttribute]; }, + attributeTypeIcon() { + return this.icon || this.issuableAttribute; + }, i18n() { return { noAttribute: sprintf(s__('DropdownWidget|No %{issuableAttribute}'), { @@ -224,7 +233,8 @@ export default { variables: { fullPath: this.workspacePath, attributeId: - this.issuableAttribute === IssuableAttributeType.Milestone + this.issuableAttribute === IssuableAttributeType.Milestone && + this.issuableType === IssuableType.Issue ? getIdFromGraphQLId(attributeId) : attributeId, iid: this.iid, @@ -255,6 +265,11 @@ export default { attributeId === this.currentAttribute?.id || (!this.currentAttribute?.id && !attributeId) ); }, + isAttributeOverdue(attribute) { + return this.issuableAttribute === IssuableAttributeType.Milestone + ? attribute?.expired + : false; + }, showDropdown() { this.$refs.newDropdown.show(); }, @@ -284,8 +299,10 @@ export default { > diff --git a/app/assets/javascripts/sidebar/constants.js b/app/assets/javascripts/sidebar/constants.js index c8c63310347..91afa75bba9 100644 --- a/app/assets/javascripts/sidebar/constants.js +++ b/app/assets/javascripts/sidebar/constants.js @@ -12,6 +12,7 @@ import issueDueDateQuery from '~/sidebar/queries/issue_due_date.query.graphql'; import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql'; import issueSubscribedQuery from '~/sidebar/queries/issue_subscribed.query.graphql'; import issueTimeTrackingQuery from '~/sidebar/queries/issue_time_tracking.query.graphql'; +import mergeRequestMilestone from '~/sidebar/queries/merge_request_milestone.query.graphql'; import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql'; import mergeRequestSubscribed from '~/sidebar/queries/merge_request_subscribed.query.graphql'; import mergeRequestTimeTrackingQuery from '~/sidebar/queries/merge_request_time_tracking.query.graphql'; @@ -24,6 +25,7 @@ import updateEpicSubscriptionMutation from '~/sidebar/queries/update_epic_subscr import updateIssueConfidentialMutation from '~/sidebar/queries/update_issue_confidential.mutation.graphql'; import updateIssueDueDateMutation from '~/sidebar/queries/update_issue_due_date.mutation.graphql'; import updateIssueSubscriptionMutation from '~/sidebar/queries/update_issue_subscription.mutation.graphql'; +import mergeRequestMilestoneMutation from '~/sidebar/queries/update_merge_request_milestone.mutation.graphql'; import updateMergeRequestSubscriptionMutation from '~/sidebar/queries/update_merge_request_subscription.mutation.graphql'; import updateAlertAssigneesMutation from '~/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql'; import getAlertAssignees from '~/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql'; @@ -171,12 +173,19 @@ export const issuableMilestoneQueries = { query: projectIssueMilestoneQuery, mutation: projectIssueMilestoneMutation, }, + [IssuableType.MergeRequest]: { + query: mergeRequestMilestone, + mutation: mergeRequestMilestoneMutation, + }, }; export const milestonesQueries = { [IssuableType.Issue]: { query: projectMilestonesQuery, }, + [IssuableType.MergeRequest]: { + query: projectMilestonesQuery, + }, }; export const IssuableAttributeType = { diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js index 67c72b17f1f..e4227a1f42c 100644 --- a/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_sidebar.js @@ -18,6 +18,7 @@ import SidebarConfidentialityWidget from '~/sidebar/components/confidential/side import SidebarDueDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue'; import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar_participants_widget.vue'; import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue'; +import SidebarDropdownWidget from '~/sidebar/components/sidebar_dropdown_widget.vue'; import { apolloProvider } from '~/sidebar/graphql'; import trackShowInviteMemberLink from '~/sidebar/track_invite_members'; import Translate from '../vue_shared/translate'; @@ -29,6 +30,7 @@ import SidebarReviewers from './components/reviewers/sidebar_reviewers.vue'; import SidebarSeverity from './components/severity/sidebar_severity.vue'; import SidebarSubscriptionsWidget from './components/subscriptions/sidebar_subscriptions_widget.vue'; import SidebarTimeTracking from './components/time_tracking/sidebar_time_tracking.vue'; +import { IssuableAttributeType } from './constants'; import SidebarMoveIssue from './lib/sidebar_move_issue'; Vue.use(Translate); @@ -154,7 +156,8 @@ function mountReviewersComponent(mediator) { issuableIid: String(iid), projectPath: fullPath, field: el.dataset.field, - issuableType: isInIssuePage() || isInDesignPage() ? 'issue' : 'merge_request', + issuableType: + isInIssuePage() || isInDesignPage() ? IssuableType.Issue : IssuableType.MergeRequest, }, }), }); @@ -166,6 +169,40 @@ function mountReviewersComponent(mediator) { } } +function mountMilestoneSelect() { + const el = document.querySelector('.js-milestone-select'); + + if (!el) { + return false; + } + + const { canEdit, projectPath, issueIid } = el.dataset; + + return new Vue({ + el, + apolloProvider, + components: { + SidebarDropdownWidget, + }, + provide: { + canUpdate: parseBoolean(canEdit), + isClassicSidebar: true, + }, + render: (createElement) => + createElement('sidebar-dropdown-widget', { + props: { + attrWorkspacePath: projectPath, + workspacePath: projectPath, + iid: issueIid, + issuableType: + isInIssuePage() || isInDesignPage() ? IssuableType.Issue : IssuableType.MergeRequest, + issuableAttribute: IssuableAttributeType.Milestone, + icon: 'clock', + }, + }), + }); +} + export function mountSidebarLabels() { const el = document.querySelector('.js-sidebar-labels'); @@ -466,6 +503,7 @@ export function mountSidebar(mediator) { mountAssigneesComponentDeprecated(mediator); } mountReviewersComponent(mediator); + mountMilestoneSelect(); mountConfidentialComponent(mediator); mountDueDateComponent(mediator); mountReferenceComponent(mediator); diff --git a/app/assets/javascripts/sidebar/queries/merge_request_milestone.query.graphql b/app/assets/javascripts/sidebar/queries/merge_request_milestone.query.graphql new file mode 100644 index 00000000000..5c0edf5acee --- /dev/null +++ b/app/assets/javascripts/sidebar/queries/merge_request_milestone.query.graphql @@ -0,0 +1,14 @@ +#import "./milestone.fragment.graphql" + +query mergeRequestMilestone($fullPath: ID!, $iid: String!) { + workspace: project(fullPath: $fullPath) { + __typename + issuable: mergeRequest(iid: $iid) { + __typename + id + attribute: milestone { + ...MilestoneFragment + } + } + } +} diff --git a/app/assets/javascripts/sidebar/queries/milestone.fragment.graphql b/app/assets/javascripts/sidebar/queries/milestone.fragment.graphql index 8db5359dac0..2ffd58a2da1 100644 --- a/app/assets/javascripts/sidebar/queries/milestone.fragment.graphql +++ b/app/assets/javascripts/sidebar/queries/milestone.fragment.graphql @@ -2,4 +2,5 @@ fragment MilestoneFragment on Milestone { id title webUrl: webPath + expired } diff --git a/app/assets/javascripts/sidebar/queries/project_issue_milestone.mutation.graphql b/app/assets/javascripts/sidebar/queries/project_issue_milestone.mutation.graphql index d88ad8b1087..721a71bef63 100644 --- a/app/assets/javascripts/sidebar/queries/project_issue_milestone.mutation.graphql +++ b/app/assets/javascripts/sidebar/queries/project_issue_milestone.mutation.graphql @@ -11,6 +11,7 @@ mutation projectIssueMilestoneMutation($fullPath: ID!, $iid: String!, $attribute title id state + expired } } } diff --git a/app/assets/javascripts/sidebar/queries/project_milestones.query.graphql b/app/assets/javascripts/sidebar/queries/project_milestones.query.graphql index 1237640c468..a3ab1ebc872 100644 --- a/app/assets/javascripts/sidebar/queries/project_milestones.query.graphql +++ b/app/assets/javascripts/sidebar/queries/project_milestones.query.graphql @@ -3,7 +3,13 @@ query projectMilestones($fullPath: ID!, $title: String, $state: MilestoneStateEnum) { workspace: project(fullPath: $fullPath) { __typename - attributes: milestones(searchTitle: $title, state: $state) { + attributes: milestones( + searchTitle: $title + state: $state + sort: EXPIRED_LAST_DUE_DATE_ASC + first: 20 + includeAncestors: true + ) { nodes { ...MilestoneFragment state diff --git a/app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql b/app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql new file mode 100644 index 00000000000..368f06fac7f --- /dev/null +++ b/app/assets/javascripts/sidebar/queries/update_merge_request_milestone.mutation.graphql @@ -0,0 +1,17 @@ +mutation mergeRequestSetMilestone($fullPath: ID!, $iid: String!, $attributeId: ID) { + issuableSetAttribute: mergeRequestSetMilestone( + input: { projectPath: $fullPath, iid: $iid, milestoneId: $attributeId } + ) { + __typename + errors + issuable: mergeRequest { + __typename + id + attribute: milestone { + title + id + state + } + } + } +} diff --git a/app/models/compare.rb b/app/models/compare.rb index 9b214171f07..2eaaf98c260 100644 --- a/app/models/compare.rb +++ b/app/models/compare.rb @@ -25,6 +25,10 @@ class Compare @straight = straight end + def cache_key + [@project, :compare, diff_refs.hash] + end + def commits @commits ||= Commit.decorate(@compare.commits, project) end diff --git a/app/models/lfs_download_object.rb b/app/models/lfs_download_object.rb index 6383f95d546..319499fd1b7 100644 --- a/app/models/lfs_download_object.rb +++ b/app/models/lfs_download_object.rb @@ -3,20 +3,32 @@ class LfsDownloadObject include ActiveModel::Validations - attr_accessor :oid, :size, :link + attr_accessor :oid, :size, :link, :headers delegate :sanitized_url, :credentials, to: :sanitized_uri validates :oid, format: { with: /\A\h{64}\z/ } validates :size, numericality: { greater_than_or_equal_to: 0 } validates :link, public_url: { protocols: %w(http https) } + validate :headers_must_be_hash - def initialize(oid:, size:, link:) + def initialize(oid:, size:, link:, headers: {}) @oid = oid @size = size @link = link + @headers = headers || {} end def sanitized_uri @sanitized_uri ||= Gitlab::UrlSanitizer.new(link) end + + def has_authorization_header? + headers.keys.map(&:downcase).include?('authorization') + end + + private + + def headers_must_be_hash + errors.add(:base, "headers must be a Hash") unless headers.is_a?(Hash) + end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 1ddedb26e1c..fcbf1202e69 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -360,7 +360,7 @@ class MergeRequest < ApplicationRecord scope :preload_approved_by_users, -> { preload(:approved_by_users) } scope :preload_metrics, -> (relation) { preload(metrics: relation) } scope :preload_project_and_latest_diff, -> { preload(:source_project, :latest_merge_request_diff) } - scope :preload_latest_diff_commit, -> { preload(latest_merge_request_diff: :merge_request_diff_commits) } + scope :preload_latest_diff_commit, -> { preload(latest_merge_request_diff: { merge_request_diff_commits: [:commit_author, :committer] }) } scope :preload_milestoneish_associations, -> { preload_routables.preload(:assignees, :labels) } scope :with_web_entity_associations, -> { preload(:author, target_project: [:project_feature, group: [:route, :parent], namespace: :route]) } diff --git a/app/models/merge_request/diff_commit_user.rb b/app/models/merge_request/diff_commit_user.rb new file mode 100644 index 00000000000..3fc5c9318a4 --- /dev/null +++ b/app/models/merge_request/diff_commit_user.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +class MergeRequest::DiffCommitUser < ApplicationRecord + validates :name, length: { maximum: 512 } + validates :email, length: { maximum: 512 } + validates :name, presence: true, unless: :email + validates :email, presence: true, unless: :name + + # Prepares a value to be inserted into a column in the table + # `merge_request_diff_commit_users`. Values in this table are limited to + # 512 characters. + # + # We treat empty strings as NULL values, as there's no point in (for + # example) storing a row where both the name and Email are an empty + # string. In addition, if we treated them differently we could end up with + # two rows: one where field X is NULL, and one where field X is an empty + # string. This is redundant, so we avoid storing such data. + def self.prepare(value) + value.present? ? value[0..511] : nil + end + + # Creates a new row, or returns an existing one if a row already exists. + def self.find_or_create(name, email) + find_or_create_by!(name: name, email: email) + rescue ActiveRecord::RecordNotUnique + retry + end + + # Finds many (name, email) pairs in bulk. + def self.bulk_find(pairs) + queries = {} + rows = [] + + pairs.each do |(name, email)| + queries[[name, email]] = where(name: name, email: email).to_sql + end + + # We may end up having to query many users. To ensure we don't hit any + # query size limits, we get a fixed number of users at a time. + queries.values.each_slice(1_000).map do |slice| + rows.concat(from("(#{slice.join("\nUNION ALL\n")}) #{table_name}").to_a) + end + + rows + end + + # Finds or creates rows for the given pairs of names and Emails. + # + # The `names_and_emails` argument must be an Array/Set of tuples like so: + # + # [ + # [name, email], + # [name, email], + # ... + # ] + # + # This method expects that the names and Emails have already been trimmed to + # at most 512 characters. + # + # The return value is a Hash that maps these tuples to instances of this + # model. + def self.bulk_find_or_create(pairs) + mapping = {} + create = [] + + # Over time, fewer new rows need to be created. We take advantage of that + # here by first finding all rows that already exist, using a limited number + # of queries (in most cases only one query will be needed). + bulk_find(pairs).each do |row| + mapping[[row.name, row.email]] = row + end + + pairs.each do |(name, email)| + create << { name: name, email: email } unless mapping[[name, email]] + end + + return mapping if create.empty? + + # Sometimes we may need to insert new users into the table. We do this in + # bulk, so we only need one INSERT for all missing users. + insert_all(create, returning: %w[id name email]).each do |row| + mapping[[row['name'], row['email']]] = + new(id: row['id'], name: row['name'], email: row['email']) + end + + # It's possible for (name, email) pairs to be inserted concurrently, + # resulting in the above insert not returning anything. Here we get any + # remaining users that were created concurrently. + bulk_find(pairs.reject { |pair| mapping.key?(pair) }).each do |row| + mapping[[row.name, row.email]] = row + end + + mapping + end +end diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index f58d7788432..d2ea663551d 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -701,7 +701,7 @@ class MergeRequestDiff < ApplicationRecord end def load_commits(limit: nil) - commits = merge_request_diff_commits.limit(limit) + commits = merge_request_diff_commits.with_users.limit(limit) .map { |commit| Commit.from_hash(commit.to_hash, project) } CommitCollection diff --git a/app/models/merge_request_diff_commit.rb b/app/models/merge_request_diff_commit.rb index ed398e0d2e0..466d28301c0 100644 --- a/app/models/merge_request_diff_commit.rb +++ b/app/models/merge_request_diff_commit.rb @@ -9,21 +9,51 @@ class MergeRequestDiffCommit < ApplicationRecord belongs_to :merge_request_diff + # This relation is called `commit_author` and not `author`, as the project + # import/export logic treats relations named `author` as instances of the + # `User` class. + # + # NOTE: these columns are _not_ indexed, nor do they use foreign keys. + # + # This is deliberate, as creating these indexes on GitLab.com takes a _very_ + # long time. In addition, there's no real need for them either based on how + # this data is used. + # + # For more information, refer to the following: + # + # - https://gitlab.com/gitlab-com/gl-infra/production/-/issues/5038#note_614592881 + # - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63669 + belongs_to :commit_author, class_name: 'MergeRequest::DiffCommitUser' + belongs_to :committer, class_name: 'MergeRequest::DiffCommitUser' + sha_attribute :sha alias_attribute :id, :sha serialize :trailers, Serializers::Json # rubocop:disable Cop/ActiveRecordSerialize validates :trailers, json_schema: { filename: 'git_trailers' } + scope :with_users, -> { preload(:commit_author, :committer) } + + # A list of keys of which their values need to be trimmed before they can be + # inserted into the merge_request_diff_commit_users table. + TRIM_USER_KEYS = + %i[author_name author_email committer_name committer_email].freeze + # Deprecated; use `bulk_insert!` from `BulkInsertSafe` mixin instead. # cf. https://gitlab.com/gitlab-org/gitlab/issues/207989 for progress def self.create_bulk(merge_request_diff_id, commits) - rows = commits.map.with_index do |commit, index| - # See #parent_ids. - commit_hash = commit.to_hash.except(:parent_ids) + commit_hashes, user_tuples = prepare_commits_for_bulk_insert(commits) + users = MergeRequest::DiffCommitUser.bulk_find_or_create(user_tuples) + + rows = commit_hashes.map.with_index do |commit_hash, index| sha = commit_hash.delete(:id) + author = users[[commit_hash[:author_name], commit_hash[:author_email]]] + committer = + users[[commit_hash[:committer_name], commit_hash[:committer_email]]] commit_hash.merge( + commit_author_id: author&.id, + committer_id: committer&.id, merge_request_diff_id: merge_request_diff_id, relative_order: index, sha: Gitlab::Database::ShaAttribute.serialize(sha), # rubocop:disable Cop/ActiveRecordSerialize @@ -36,6 +66,24 @@ class MergeRequestDiffCommit < ApplicationRecord Gitlab::Database.bulk_insert(self.table_name, rows) # rubocop:disable Gitlab/BulkInsert end + def self.prepare_commits_for_bulk_insert(commits) + user_tuples = Set.new + hashes = commits.map do |commit| + hash = commit.to_hash.except(:parent_ids) + + TRIM_USER_KEYS.each do |key| + hash[key] = MergeRequest::DiffCommitUser.prepare(hash[key]) + end + + user_tuples << [hash[:author_name], hash[:author_email]] + user_tuples << [hash[:committer_name], hash[:committer_email]] + + hash + end + + [hashes, user_tuples] + end + def self.oldest_merge_request_id_per_commit(project_id, shas) # This method is defined here and not on MergeRequest, otherwise the SHA # values used in the WHERE below won't be encoded correctly. @@ -54,4 +102,20 @@ class MergeRequestDiffCommit < ApplicationRecord ) .group(:sha) end + + def author_name + commit_author_id ? commit_author.name : super + end + + def author_email + commit_author_id ? commit_author.email : super + end + + def committer_name + committer_id ? committer.name : super + end + + def committer_email + committer_id ? committer.email : super + end end diff --git a/app/serializers/analytics/cycle_analytics/stage_entity.rb b/app/serializers/analytics/cycle_analytics/stage_entity.rb index b24148802d0..c5cc8c89fb7 100644 --- a/app/serializers/analytics/cycle_analytics/stage_entity.rb +++ b/app/serializers/analytics/cycle_analytics/stage_entity.rb @@ -9,6 +9,21 @@ module Analytics expose :description expose :id expose :custom + + # new API + expose :start_event do + expose :start_event_identifier, as: :identifier, if: -> (s) { s.custom? } + expose :start_event_label, as: :label, using: LabelEntity, if: -> (s) { s.start_event_label_based? } + expose :start_event_html_description, as: :html_description + end + + expose :end_event do + expose :end_event_identifier, as: :identifier, if: -> (s) { s.custom? } + expose :end_event_label, as: :label, using: LabelEntity, if: -> (s) { s.end_event_label_based? } + expose :end_event_html_description, as: :html_description + end + + # old API expose :start_event_identifier, if: -> (s) { s.custom? } expose :end_event_identifier, if: -> (s) { s.custom? } expose :start_event_label, using: LabelEntity, if: -> (s) { s.start_event_label_based? } diff --git a/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb b/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb index efd410088ab..c82ed97203f 100644 --- a/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb +++ b/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb @@ -81,11 +81,13 @@ module Projects def parse_response_links(objects_response) objects_response.each_with_object([]) do |entry, link_list| link = entry.dig('actions', DOWNLOAD_ACTION, 'href') + headers = entry.dig('actions', DOWNLOAD_ACTION, 'header') raise DownloadLinkNotFound unless link link_list << LfsDownloadObject.new(oid: entry['oid'], size: entry['size'], + headers: headers, link: add_credentials(link)) rescue DownloadLinkNotFound, Addressable::URI::InvalidURIError log_error("Link for Lfs Object with oid #{entry['oid']} not found or invalid.") diff --git a/app/services/projects/lfs_pointers/lfs_download_service.rb b/app/services/projects/lfs_pointers/lfs_download_service.rb index 8058e082397..cf52b47a15e 100644 --- a/app/services/projects/lfs_pointers/lfs_download_service.rb +++ b/app/services/projects/lfs_pointers/lfs_download_service.rb @@ -11,7 +11,7 @@ module Projects LARGE_FILE_SIZE = 1.megabytes attr_reader :lfs_download_object - delegate :oid, :size, :credentials, :sanitized_url, to: :lfs_download_object, prefix: :lfs + delegate :oid, :size, :credentials, :sanitized_url, :headers, to: :lfs_download_object, prefix: :lfs def initialize(project, lfs_download_object) super(project) @@ -71,17 +71,21 @@ module Projects raise_oid_error! if digester.hexdigest != lfs_oid end - def download_headers - { stream_body: true }.tap do |headers| + def download_options + http_options = { headers: lfs_headers, stream_body: true } + + return http_options if lfs_download_object.has_authorization_header? + + http_options.tap do |options| if lfs_credentials[:user].present? || lfs_credentials[:password].present? # Using authentication headers in the request - headers[:basic_auth] = { username: lfs_credentials[:user], password: lfs_credentials[:password] } + options[:basic_auth] = { username: lfs_credentials[:user], password: lfs_credentials[:password] } end end end def fetch_file(&block) - response = Gitlab::HTTP.get(lfs_sanitized_url, download_headers, &block) + response = Gitlab::HTTP.get(lfs_sanitized_url, download_options, &block) raise ResponseError, "Received error code #{response.code}" unless response.success? end diff --git a/app/views/notify/access_token_about_to_expire_email.html.haml b/app/views/notify/access_token_about_to_expire_email.html.haml index ea27f72764f..fc318de4c42 100644 --- a/app/views/notify/access_token_about_to_expire_email.html.haml +++ b/app/views/notify/access_token_about_to_expire_email.html.haml @@ -8,4 +8,4 @@ %li= token %p - pat_link_start = ''.html_safe % { url: @target_url } - = html_escape(_('You can create a new one or check them in your %{pat_link_start}personal access tokens%{pat_link_end} settings')) % { pat_link_start: pat_link_start, pat_link_end: ''.html_safe } + = html_escape(_('You can create a new one or check them in your %{pat_link_start}personal access tokens%{pat_link_end} settings.')) % { pat_link_start: pat_link_start, pat_link_end: ''.html_safe } diff --git a/app/views/notify/access_token_about_to_expire_email.text.erb b/app/views/notify/access_token_about_to_expire_email.text.erb index dc9b1379e47..39608f0d6bd 100644 --- a/app/views/notify/access_token_about_to_expire_email.text.erb +++ b/app/views/notify/access_token_about_to_expire_email.text.erb @@ -6,4 +6,4 @@ - <%= token %> <% end %> -<%= _('You can create a new one or check them in your personal access tokens settings %{pat_link}') % { pat_link: @target_url } %> +<%= _('You can create a new one or check them in your personal access tokens settings %{pat_link}.') % { pat_link: @target_url } %> diff --git a/app/views/notify/access_token_expired_email.html.haml b/app/views/notify/access_token_expired_email.html.haml index b26431cce91..1e7c07c2282 100644 --- a/app/views/notify/access_token_expired_email.html.haml +++ b/app/views/notify/access_token_expired_email.html.haml @@ -4,4 +4,4 @@ = _('One or more of your personal access tokens has expired.') %p - pat_link_start = ''.html_safe % { url: @target_url } - = html_escape(_('You can create a new one or check them in your %{pat_link_start}personal access tokens%{pat_link_end} settings')) % { pat_link_start: pat_link_start, pat_link_end: ''.html_safe } + = html_escape(_('You can create a new one or check them in your %{pat_link_start}personal access tokens%{pat_link_end} settings.')) % { pat_link_start: pat_link_start, pat_link_end: ''.html_safe } diff --git a/app/views/notify/access_token_expired_email.text.erb b/app/views/notify/access_token_expired_email.text.erb index d44f993d094..4dc67e85dc2 100644 --- a/app/views/notify/access_token_expired_email.text.erb +++ b/app/views/notify/access_token_expired_email.text.erb @@ -2,4 +2,4 @@ <%= _('One or more of your personal access tokens has expired.') %> -<%= _('You can create a new one or check them in your personal access tokens settings %{pat_link}') % { pat_link: @target_url } %> +<%= _('You can create a new one or check them in your personal access tokens settings %{pat_link}.') % { pat_link: @target_url } %> diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 15bf21d1aca..5156ad96684 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -34,31 +34,8 @@ = render_if_exists 'shared/issuable/sidebar_item_epic', issuable_sidebar: issuable_sidebar, group_path: @project.group.full_path, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid], issuable_type: issuable_type - if issuable_sidebar[:supports_milestone] - - milestone = issuable_sidebar[:milestone] || {} .block.milestone{ :class => ("gl-border-b-0!" if issuable_sidebar[:supports_iterations]), data: { qa_selector: 'milestone_block' } } - .sidebar-collapsed-icon.has-tooltip{ title: sidebar_milestone_tooltip_label(milestone), data: { container: 'body', html: 'true', placement: 'left', boundary: 'viewport' } } - = sprite_icon('clock') - %span.milestone-title.collapse-truncated-title - - if milestone.present? - = milestone[:title] - - else - = _('None') - .hide-collapsed.gl-line-height-20.gl-mb-2.gl-text-gray-900{ data: { testid: "milestone_title" } } - = _('Milestone') - = loading_icon(css_class: 'gl-vertical-align-text-bottom hidden block-loading') - - if can_edit_issuable - = link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right', data: { qa_selector: "edit_milestone_link", track_label: "right_sidebar", track_property: "milestone", track_event: "click_edit_button", track_value: "" } - .value.hide-collapsed - - if milestone.present? - - milestone_title = milestone[:expired] ? _("%{milestone_name} (Past due)").html_safe % { milestone_name: milestone[:title] } : milestone[:title] - = link_to milestone_title, milestone[:web_url], class: "bold has-tooltip", title: sidebar_milestone_remaining_days(milestone), data: { container: "body", html: 'true', boundary: 'viewport', qa_selector: 'milestone_link', qa_title: milestone[:title] } - - else - %span.no-value - = _('None') - - .selectbox.hide-collapsed - = f.hidden_field 'milestone_id', value: milestone[:id], id: nil - = dropdown_tag('Milestone', options: { title: _('Assign milestone'), toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: _('Search milestones'), data: { show_no: true, field_name: "#{issuable_type}[milestone_id]", project_id: issuable_sidebar[:project_id], issuable_id: issuable_sidebar[:id], ability_name: issuable_type, issue_update: issuable_sidebar[:issuable_json_path], use_id: true, default_no: true, selected: milestone[:title], null_default: true, display: 'static' }}) + .js-milestone-select{ data: { can_edit: can_edit_issuable.to_s, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid] } } - if @project.group.present? && issuable_sidebar[:supports_iterations] .block{ class: 'gl-pt-0!', data: { qa_selector: 'iteration_container' } } diff --git a/config/feature_flags/development/api_caching_repository_compare.yml b/config/feature_flags/development/api_caching_repository_compare.yml new file mode 100644 index 00000000000..d39bd283512 --- /dev/null +++ b/config/feature_flags/development/api_caching_repository_compare.yml @@ -0,0 +1,8 @@ +--- +name: api_caching_repository_compare +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64418 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334264 +milestone: '14.1' +type: development +group: group::source code +default_enabled: false diff --git a/db/migrate/20210602155056_add_merge_request_diff_commit_users.rb b/db/migrate/20210602155056_add_merge_request_diff_commit_users.rb new file mode 100644 index 00000000000..f99790e0eca --- /dev/null +++ b/db/migrate/20210602155056_add_merge_request_diff_commit_users.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class AddMergeRequestDiffCommitUsers < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + disable_ddl_transaction! + + def up + create_table_with_constraints :merge_request_diff_commit_users, id: :bigint do |t| + t.text :name + t.text :email + + t.text_limit :name, 512 + t.text_limit :email, 512 + + t.index [:name, :email], unique: true + end + + # Names or Emails can be optional, so in some cases one of these may be + # null. But if both are NULL/empty, no row should exist in this table. + add_check_constraint( + :merge_request_diff_commit_users, + "(COALESCE(name, '') != '') OR (COALESCE(email, '') != '')", + :merge_request_diff_commit_users_name_or_email_existence + ) + end + + def down + drop_table :merge_request_diff_commit_users + end +end diff --git a/db/migrate/20210602155110_add_merge_request_diff_commit_user_columns.rb b/db/migrate/20210602155110_add_merge_request_diff_commit_user_columns.rb new file mode 100644 index 00000000000..8cc86c7e73c --- /dev/null +++ b/db/migrate/20210602155110_add_merge_request_diff_commit_user_columns.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class AddMergeRequestDiffCommitUserColumns < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + def up + # NOTE: these columns are _not_ indexed, nor do they use foreign keys. + # + # This is deliberate, as creating these indexes on GitLab.com takes a _very_ + # long time. In addition, there's no real need for them either based on how + # this data is used. + # + # For more information, refer to the following: + # + # - https://gitlab.com/gitlab-com/gl-infra/production/-/issues/5038#note_614592881 + # - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63669 + add_column(:merge_request_diff_commits, :commit_author_id, :bigint) + add_column(:merge_request_diff_commits, :committer_id, :bigint) + end + + def down + remove_column(:merge_request_diff_commits, :commit_author_id) + remove_column(:merge_request_diff_commits, :committer_id) + end +end diff --git a/db/migrate/20210705132928_add_new_user_signups_cap_to_namespace_settings.rb b/db/migrate/20210705132928_add_new_user_signups_cap_to_namespace_settings.rb new file mode 100644 index 00000000000..7f736bf2b87 --- /dev/null +++ b/db/migrate/20210705132928_add_new_user_signups_cap_to_namespace_settings.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddNewUserSignupsCapToNamespaceSettings < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + def up + with_lock_retries do + add_column :namespace_settings, :new_user_signups_cap, :integer, null: true + end + end + + def down + with_lock_retries do + remove_column :namespace_settings, :new_user_signups_cap + end + end +end diff --git a/db/post_migrate/20210602164044_schedule_latest_pipeline_id_population.rb b/db/post_migrate/20210602164044_schedule_latest_pipeline_id_population.rb index 6d4fcc35245..dfd2806fece 100644 --- a/db/post_migrate/20210602164044_schedule_latest_pipeline_id_population.rb +++ b/db/post_migrate/20210602164044_schedule_latest_pipeline_id_population.rb @@ -1,25 +1,10 @@ # frozen_string_literal: true class ScheduleLatestPipelineIdPopulation < ActiveRecord::Migration[6.1] - include Gitlab::Database::MigrationHelpers - - DOWNTIME = false - DELAY_INTERVAL = 2.minutes.to_i - BATCH_SIZE = 100 - MIGRATION = 'PopulateLatestPipelineIds' - - disable_ddl_transaction! - def up - return unless Gitlab.ee? - - queue_background_migration_jobs_by_range_at_intervals( - Gitlab::BackgroundMigration::PopulateLatestPipelineIds::ProjectSetting.has_vulnerabilities_without_latest_pipeline_set, - MIGRATION, - DELAY_INTERVAL, - batch_size: BATCH_SIZE, - primary_column_name: 'project_id' - ) + # no-op: This migration has been marked as no-op and replaced by + # `ReScheduleLatestPipelineIdPopulation` as we've found some problems. + # For more information: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65280 end def down diff --git a/db/post_migrate/20210604133651_schedule_merge_request_diff_users_background_migration.rb b/db/post_migrate/20210604133651_schedule_merge_request_diff_users_background_migration.rb new file mode 100644 index 00000000000..e0af0ef1fc6 --- /dev/null +++ b/db/post_migrate/20210604133651_schedule_merge_request_diff_users_background_migration.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +class ScheduleMergeRequestDiffUsersBackgroundMigration < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + disable_ddl_transaction! + + # The number of rows to process in a single migration job. + # + # The minimum interval for background migrations is two minutes. On staging we + # observed we can process roughly 20 000 rows in a minute. Based on the total + # number of rows on staging, this translates to a total processing time of + # roughly 14 days. + # + # By using a batch size of 40 000, we maintain a rate of roughly 20 000 rows + # per minute, hopefully keeping the total migration time under two weeks; + # instead of four weeks. + BATCH_SIZE = 40_000 + + MIGRATION_NAME = 'MigrateMergeRequestDiffCommitUsers' + + class MergeRequestDiff < ActiveRecord::Base + self.table_name = 'merge_request_diffs' + end + + def up + start = MergeRequestDiff.minimum(:id).to_i + max = MergeRequestDiff.maximum(:id).to_i + delay = BackgroundMigrationWorker.minimum_interval + + # The table merge_request_diff_commits contains _a lot_ of rows (roughly 400 + # 000 000 on staging). Iterating a table that large to determine job ranges + # would take a while. + # + # To avoid that overhead, we simply schedule fixed ranges according to the + # minimum and maximum IDs. The background migration in turn only processes + # rows that actually exist. + while start < max + stop = start + BATCH_SIZE + + migrate_in(delay, MIGRATION_NAME, [start, stop]) + + Gitlab::Database::BackgroundMigrationJob + .create!(class_name: MIGRATION_NAME, arguments: [start, stop]) + + delay += BackgroundMigrationWorker.minimum_interval + start += BATCH_SIZE + end + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20210706142819_re_schedule_latest_pipeline_id_population.rb b/db/post_migrate/20210706142819_re_schedule_latest_pipeline_id_population.rb new file mode 100644 index 00000000000..709e0be8b79 --- /dev/null +++ b/db/post_migrate/20210706142819_re_schedule_latest_pipeline_id_population.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class ReScheduleLatestPipelineIdPopulation < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + DELAY_INTERVAL = 2.minutes.to_i + BATCH_SIZE = 100 + MIGRATION = 'PopulateLatestPipelineIds' + + disable_ddl_transaction! + + def up + return unless Gitlab.ee? + + queue_background_migration_jobs_by_range_at_intervals( + Gitlab::BackgroundMigration::PopulateLatestPipelineIds::ProjectSetting.has_vulnerabilities_without_latest_pipeline_set, + MIGRATION, + DELAY_INTERVAL, + batch_size: BATCH_SIZE, + primary_column_name: 'project_id' + ) + end + + def down + # no-op + end +end diff --git a/db/schema_migrations/20210602155056 b/db/schema_migrations/20210602155056 new file mode 100644 index 00000000000..4c6f7f95874 --- /dev/null +++ b/db/schema_migrations/20210602155056 @@ -0,0 +1 @@ +42b3090efee66f5a7a5c06d8768d1417892c5d6745f60163a09f58e6e3722761 \ No newline at end of file diff --git a/db/schema_migrations/20210602155110 b/db/schema_migrations/20210602155110 new file mode 100644 index 00000000000..127375dc477 --- /dev/null +++ b/db/schema_migrations/20210602155110 @@ -0,0 +1 @@ +aa04d433e400ed3ec11e5d40ada72f122b1d8b7a82f8803d9206da5c94ec5ef9 \ No newline at end of file diff --git a/db/schema_migrations/20210604133651 b/db/schema_migrations/20210604133651 new file mode 100644 index 00000000000..7c6394cd470 --- /dev/null +++ b/db/schema_migrations/20210604133651 @@ -0,0 +1 @@ +0c01bb41113c468a602649b591e1fd2959a6e3190c835ef2e27351cf69f50fd5 \ No newline at end of file diff --git a/db/schema_migrations/20210705132928 b/db/schema_migrations/20210705132928 new file mode 100644 index 00000000000..c1ef3ec3c1f --- /dev/null +++ b/db/schema_migrations/20210705132928 @@ -0,0 +1 @@ +c66a42fc813846a09d4389a895a2d20ad48889d8ff45ab642e771b6792490623 \ No newline at end of file diff --git a/db/schema_migrations/20210706142819 b/db/schema_migrations/20210706142819 new file mode 100644 index 00000000000..193c6ba1d56 --- /dev/null +++ b/db/schema_migrations/20210706142819 @@ -0,0 +1 @@ +ed0daff7120cbdba2f0e9ca1f2e40c11114bb2c7db4543903d16891ffbbba3f8 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 5d0b8bd043d..1dc5955ad6c 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -14760,6 +14760,24 @@ CREATE SEQUENCE merge_request_context_commits_id_seq ALTER SEQUENCE merge_request_context_commits_id_seq OWNED BY merge_request_context_commits.id; +CREATE TABLE merge_request_diff_commit_users ( + id bigint NOT NULL, + name text, + email text, + CONSTRAINT check_147358fc42 CHECK ((char_length(name) <= 512)), + CONSTRAINT check_f5fa206cf7 CHECK ((char_length(email) <= 512)), + CONSTRAINT merge_request_diff_commit_users_name_or_email_existence CHECK (((COALESCE(name, ''::text) <> ''::text) OR (COALESCE(email, ''::text) <> ''::text))) +); + +CREATE SEQUENCE merge_request_diff_commit_users_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE merge_request_diff_commit_users_id_seq OWNED BY merge_request_diff_commit_users.id; + CREATE TABLE merge_request_diff_commits ( authored_date timestamp without time zone, committed_date timestamp without time zone, @@ -14771,7 +14789,9 @@ CREATE TABLE merge_request_diff_commits ( committer_name text, committer_email text, message text, - trailers jsonb DEFAULT '{}'::jsonb NOT NULL + trailers jsonb DEFAULT '{}'::jsonb NOT NULL, + commit_author_id bigint, + committer_id bigint ); CREATE TABLE merge_request_diff_details ( @@ -15142,6 +15162,7 @@ CREATE TABLE namespace_settings ( resource_access_token_creation_allowed boolean DEFAULT true NOT NULL, lock_delayed_project_removal boolean DEFAULT false NOT NULL, prevent_sharing_groups_outside_hierarchy boolean DEFAULT false NOT NULL, + new_user_signups_cap integer, CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255)) ); @@ -20160,6 +20181,8 @@ ALTER TABLE ONLY merge_request_cleanup_schedules ALTER COLUMN merge_request_id S ALTER TABLE ONLY merge_request_context_commits ALTER COLUMN id SET DEFAULT nextval('merge_request_context_commits_id_seq'::regclass); +ALTER TABLE ONLY merge_request_diff_commit_users ALTER COLUMN id SET DEFAULT nextval('merge_request_diff_commit_users_id_seq'::regclass); + ALTER TABLE ONLY merge_request_diff_details ALTER COLUMN merge_request_diff_id SET DEFAULT nextval('merge_request_diff_details_merge_request_diff_id_seq'::regclass); ALTER TABLE ONLY merge_request_diffs ALTER COLUMN id SET DEFAULT nextval('merge_request_diffs_id_seq'::regclass); @@ -21600,6 +21623,9 @@ ALTER TABLE ONLY merge_request_context_commit_diff_files ALTER TABLE ONLY merge_request_context_commits ADD CONSTRAINT merge_request_context_commits_pkey PRIMARY KEY (id); +ALTER TABLE ONLY merge_request_diff_commit_users + ADD CONSTRAINT merge_request_diff_commit_users_pkey PRIMARY KEY (id); + ALTER TABLE ONLY merge_request_diff_commits ADD CONSTRAINT merge_request_diff_commits_pkey PRIMARY KEY (merge_request_diff_id, relative_order); @@ -23903,6 +23929,8 @@ CREATE INDEX index_merge_request_blocks_on_blocked_merge_request_id ON merge_req CREATE UNIQUE INDEX index_merge_request_cleanup_schedules_on_merge_request_id ON merge_request_cleanup_schedules USING btree (merge_request_id); +CREATE UNIQUE INDEX index_merge_request_diff_commit_users_on_name_and_email ON merge_request_diff_commit_users USING btree (name, email); + CREATE INDEX index_merge_request_diff_commits_on_sha ON merge_request_diff_commits USING btree (sha); CREATE INDEX index_merge_request_diff_details_failed_verification ON merge_request_diff_details USING btree (verification_retry_at NULLS FIRST) WHERE (verification_state = 3); diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index b6d2e36851d..3c10a245c3f 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -1017,6 +1017,47 @@ postgresql['trust_auth_cidr_addresses'] = %w(123.123.123.123/32 ) [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. +### Reset the Patroni state in Consul + +WARNING: +This is a destructive process and may lead the cluster into a bad state. Make sure that you have a healthy backup before running this process. + +As a last resort, if your Patroni cluster is in an unknown/bad state and no node can start, you can +reset the Patroni state in Consul completely, resulting in a reinitialized Patroni cluster when +the first Patroni node starts. + +To reset the Patroni state in Consul: + +1. Take note of the Patroni node that was the leader, or that the application thinks is the current leader, if the current state shows more than one, or none. One way to do this is to look on the PgBouncer nodes in `/var/opt/gitlab/consul/databases.ini`, which contains the hostname of the current leader. +1. Stop Patroni on all nodes: + + ```shell + sudo gitlab-ctl stop patroni + ``` + +1. Reset the state in Consul: + + ```shell + /opt/gitlab/embedded/bin/consul kv delete -recurse /service/postgresql-ha/ + ``` + +1. Start one Patroni node, which will initialize the Patroni cluster and be elected as a leader. + It's highly recommended to start the previous leader (noted in the first step), + in order to not lose existing writes that may have not been replicated because + of the broken cluster state: + + ```shell + sudo gitlab-ctl start patroni + ``` + +1. Start all other Patroni nodes that will join the Patroni cluster as replicas: + + ```shell + sudo gitlab-ctl start patroni + ``` + +If you are still seeing issues, the next step is restoring the last healthy backup. + ### Issues with other components If you're running into an issue with a component not outlined here, be sure to check the troubleshooting section of their specific documentation page: diff --git a/doc/development/img/stage_group_dashboards_error_attribution.png b/doc/development/img/stage_group_dashboards_error_attribution.png new file mode 100644 index 0000000000000000000000000000000000000000..bac778b7450fdacac6ed4bf24404014ab3b56dc5 GIT binary patch literal 166125 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\n+ wm\n+ Created with Sketch.\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":8,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":9,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":10,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":11,"utf8_diff":"--- /dev/null\n+++ b/foo/bar/.gitkeep\n","new_path":"foo/bar/.gitkeep","old_path":"foo/bar/.gitkeep","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":12,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":13,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":14,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":14,"created_at":"2016-06-14T15:02:24.770Z","updated_at":"2016-06-14T15:02:25.007Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"15"},"events":[{"id":224,"target_type":"MergeRequest","target_id":14,"project_id":36,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":1},{"id":174,"target_type":"MergeRequest","target_id":14,"project_id":5,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":20}]} -{"id":13,"target_branch":"improve/awesome","source_branch":"test-8","source_project_id":5,"author_id":16,"assignee_id":25,"title":"Voluptates consequatur eius nemo amet libero animi illum delectus tempore.","created_at":"2016-06-14T15:02:24.415Z","updated_at":"2016-06-14T15:02:59.958Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":5,"description":"Est eaque quasi qui qui. Similique voluptatem impedit iusto ratione reprehenderit. Itaque est illum ut nulla aut.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":793,"note":"In illum maxime aperiam nulla est aspernatur.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.782Z","updated_at":"2016-06-14T15:02:59.782Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[{"merge_request_diff_id":14,"id":529,"target_type":"Note","target_id":793,"project_id":4,"created_at":"2016-07-07T14:35:12.128Z","updated_at":"2016-07-07T14:35:12.128Z","action":6,"author_id":1}]},{"id":794,"note":"Enim quia perferendis cum distinctio tenetur optio voluptas veniam.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.807Z","updated_at":"2016-06-14T15:02:59.807Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":795,"note":"Dolor ad quia quis pariatur ducimus.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.831Z","updated_at":"2016-06-14T15:02:59.831Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":796,"note":"Et a odio voluptate aut.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.854Z","updated_at":"2016-06-14T15:02:59.854Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":797,"note":"Quis nihil temporibus voluptatum modi minima a ut.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.879Z","updated_at":"2016-06-14T15:02:59.879Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":798,"note":"Ut alias consequatur in nostrum.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.904Z","updated_at":"2016-06-14T15:02:59.904Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":799,"note":"Voluptatibus aperiam assumenda et neque sint libero.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.926Z","updated_at":"2016-06-14T15:02:59.926Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":800,"note":"Veritatis voluptatem dolor dolores magni quo ut ipsa fuga.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.956Z","updated_at":"2016-06-14T15:02:59.956Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":13,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":13,"relative_order":0,"sha":"0bfedc29d30280c7e8564e19f654584b459e5868","message":"fixes #10\n","authored_date":"2016-01-19T15:25:23.000+01:00","author_name":"James Lopez","author_email":"james@jameslopez.es","committed_date":"2016-01-19T15:25:23.000+01:00","committer_name":"James Lopez","committer_email":"james@jameslopez.es"},{"merge_request_diff_id":13,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","author_name":"Marin Jankovski","author_email":"marin@gitlab.com","committed_date":"2015-12-07T12:52:12.000+01:00","committer_name":"Marin Jankovski","committer_email":"marin@gitlab.com"},{"merge_request_diff_id":13,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","author_name":"Marin Jankovski","author_email":"maxlazio@gmail.com","committed_date":"2015-12-07T11:54:28.000+01:00","committer_name":"Marin Jankovski","committer_email":"maxlazio@gmail.com"},{"merge_request_diff_id":13,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T16:27:12.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":13,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:50:17.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":13,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:39:43.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":13,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T07:21:40.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":13,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:01:27.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com"},{"merge_request_diff_id":13,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:00:16.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com"},{"merge_request_diff_id":13,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T05:23:14.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":13,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:45.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com"},{"merge_request_diff_id":13,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:04.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com"},{"merge_request_diff_id":13,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","author_name":"Stan Hu","author_email":"stanhu@packetzoom.com","committed_date":"2015-08-25T17:53:12.000+02:00","committer_name":"Stan Hu","committer_email":"stanhu@packetzoom.com"},{"merge_request_diff_id":13,"relative_order":13,"sha":"e56497bb5f03a90a51293fc6d516788730953899","message":"Merge branch 'tree_helper_spec' into 'master'\n\nAdd directory structure for tree_helper spec\n\nThis directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module\n\nSee [merge request #275](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/275#note_732774)\n\nSee merge request !2\n","authored_date":"2015-01-10T22:23:29.000+01:00","author_name":"Sytse Sijbrandij","author_email":"sytse@gitlab.com","committed_date":"2015-01-10T22:23:29.000+01:00","committer_name":"Sytse Sijbrandij","committer_email":"sytse@gitlab.com"},{"merge_request_diff_id":13,"relative_order":14,"sha":"4cd80ccab63c82b4bad16faa5193fbd2aa06df40","message":"add directory structure for tree_helper spec\n","authored_date":"2015-01-10T21:28:18.000+01:00","author_name":"marmis85","author_email":"marmis85@gmail.com","committed_date":"2015-01-10T21:28:18.000+01:00","committer_name":"marmis85","committer_email":"marmis85@gmail.com"}],"merge_request_diff_files":[{"merge_request_diff_id":13,"relative_order":0,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":1,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":2,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+\n+\n+ \n+ wm\n+ Created with Sketch.\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":3,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":5,"utf8_diff":"--- /dev/null\n+++ b/foo/bar/.gitkeep\n","new_path":"foo/bar/.gitkeep","old_path":"foo/bar/.gitkeep","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":6,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":13,"created_at":"2016-06-14T15:02:24.420Z","updated_at":"2016-06-14T15:02:24.561Z","base_commit_sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","real_size":"7"},"events":[{"id":225,"target_type":"MergeRequest","target_id":13,"project_id":36,"created_at":"2016-06-14T15:02:24.636Z","updated_at":"2016-06-14T15:02:24.636Z","action":1,"author_id":16},{"id":173,"target_type":"MergeRequest","target_id":13,"project_id":5,"created_at":"2016-06-14T15:02:24.636Z","updated_at":"2016-06-14T15:02:24.636Z","action":1,"author_id":16}]} -{"id":12,"target_branch":"flatten-dirs","source_branch":"test-2","source_project_id":5,"author_id":1,"assignee_id":22,"title":"In a rerum harum nihil accusamus aut quia nobis non.","created_at":"2016-06-14T15:02:24.000Z","updated_at":"2016-06-14T15:03:00.225Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":4,"description":"Nam magnam odit velit rerum. Sapiente dolore sunt saepe debitis. Culpa maiores ut ad dolores dolorem et.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":801,"note":"Nihil dicta molestias expedita atque.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:03:00.001Z","updated_at":"2016-06-14T15:03:00.001Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":802,"note":"Illum culpa voluptas enim accusantium deserunt.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:03:00.034Z","updated_at":"2016-06-14T15:03:00.034Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":803,"note":"Dicta esse aliquam laboriosam unde alias.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:03:00.065Z","updated_at":"2016-06-14T15:03:00.065Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":804,"note":"Dicta autem et sed molestiae ut quae.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:03:00.097Z","updated_at":"2016-06-14T15:03:00.097Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":805,"note":"Ut ut temporibus voluptas dolore quia velit.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:03:00.129Z","updated_at":"2016-06-14T15:03:00.129Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":806,"note":"Dolores similique sint pariatur error id quia fugit aut.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:03:00.162Z","updated_at":"2016-06-14T15:03:00.162Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":807,"note":"Quisquam provident nihil aperiam voluptatem.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:03:00.193Z","updated_at":"2016-06-14T15:03:00.193Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":808,"note":"Similique quo vero expedita deserunt ipsam earum.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:03:00.224Z","updated_at":"2016-06-14T15:03:00.224Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":12,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":12,"relative_order":0,"sha":"97a0df9696e2aebf10c31b3016f40214e0e8f243","message":"fixes #10\n","authored_date":"2016-01-19T14:08:21.000+01:00","author_name":"James Lopez","author_email":"james@jameslopez.es","committed_date":"2016-01-19T14:08:21.000+01:00","committer_name":"James Lopez","committer_email":"james@jameslopez.es"},{"merge_request_diff_id":12,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","author_name":"Marin Jankovski","author_email":"marin@gitlab.com","committed_date":"2015-12-07T12:52:12.000+01:00","committer_name":"Marin Jankovski","committer_email":"marin@gitlab.com"},{"merge_request_diff_id":12,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","author_name":"Marin Jankovski","author_email":"maxlazio@gmail.com","committed_date":"2015-12-07T11:54:28.000+01:00","committer_name":"Marin Jankovski","committer_email":"maxlazio@gmail.com"},{"merge_request_diff_id":12,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T16:27:12.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":12,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:50:17.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":12,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:39:43.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":12,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T07:21:40.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":12,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:01:27.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com"},{"merge_request_diff_id":12,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:00:16.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com"},{"merge_request_diff_id":12,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T05:23:14.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":12,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:45.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com"},{"merge_request_diff_id":12,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:04.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com"},{"merge_request_diff_id":12,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","author_name":"Stan Hu","author_email":"stanhu@packetzoom.com","committed_date":"2015-08-25T17:53:12.000+02:00","committer_name":"Stan Hu","committer_email":"stanhu@packetzoom.com"}],"merge_request_diff_files":[{"merge_request_diff_id":12,"relative_order":0,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":12,"relative_order":1,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":12,"relative_order":2,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+\n+\n+ \n+ wm\n+ Created with Sketch.\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":12,"relative_order":3,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":12,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":12,"relative_order":5,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":12,"created_at":"2016-06-14T15:02:24.006Z","updated_at":"2016-06-14T15:02:24.169Z","base_commit_sha":"e56497bb5f03a90a51293fc6d516788730953899","real_size":"6"},"events":[{"id":226,"target_type":"MergeRequest","target_id":12,"project_id":36,"created_at":"2016-06-14T15:02:24.253Z","updated_at":"2016-06-14T15:02:24.253Z","action":1,"author_id":1},{"id":172,"target_type":"MergeRequest","target_id":12,"project_id":5,"created_at":"2016-06-14T15:02:24.253Z","updated_at":"2016-06-14T15:02:24.253Z","action":1,"author_id":1}]} +{"id":27,"target_branch":"feature","source_branch":"feature_conflict","source_project_id":2147483547,"author_id":1,"assignee_id":null,"title":"MR1","created_at":"2016-06-14T15:02:36.568Z","updated_at":"2016-06-14T15:02:56.815Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":9,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"diff_head_sha":"HEAD","source_branch_sha":"ABCD","target_branch_sha":"DCBA","merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":true,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":669,"note":"added 3 commits\n\n
  • 16ea4e20...074a2a32 - 2 commits from branch master
  • ca223a02 - readme: fix typos
\n\n[Compare with previous version](/group/project/merge_requests/1/diffs?diff_id=1189&start_sha=16ea4e207fb258fe4e9c73185a725207c9a4f3e1)","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4789,"commit_count":3,"action":"commit","created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z"},"events":[],"suggestions":[]},{"id":670,"note":"unmarked as a **Work In Progress**","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4790,"commit_count":null,"action":"title","created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z"},"events":[],"suggestions":[]},{"id":671,"note":"Sit voluptatibus eveniet architecto quidem.","note_html":"

something else entirely

","cached_markdown_version":917504,"noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.632Z","updated_at":"2016-06-14T15:02:56.632Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"tada","user_id":1,"awardable_type":"Note","awardable_id":1,"created_at":"2019-11-05T15:37:21.287Z","updated_at":"2019-11-05T15:37:21.287Z"}]},{"id":672,"note":"Odio maxime ratione voluptatibus sed.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.656Z","updated_at":"2016-06-14T15:02:56.656Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":673,"note":"Et deserunt et omnis nihil excepturi accusantium.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.679Z","updated_at":"2016-06-14T15:02:56.679Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":674,"note":"Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.700Z","updated_at":"2016-06-14T15:02:56.700Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[],"suggestions":[{"id":1,"note_id":674,"relative_order":0,"applied":false,"commit_id":null,"from_content":"Original line\n","to_content":"New line\n","lines_above":0,"lines_below":0,"outdated":false}]},{"id":675,"note":"Numquam est at dolor quo et sed eligendi similique.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.720Z","updated_at":"2016-06-14T15:02:56.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":676,"note":"Et perferendis aliquam sunt nisi labore delectus.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.742Z","updated_at":"2016-06-14T15:02:56.742Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":677,"note":"Aut ex rerum et in.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.791Z","updated_at":"2016-06-14T15:02:56.791Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":678,"note":"Dolor laborum earum ut exercitationem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:56.814Z","updated_at":"2016-06-14T15:02:56.814Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":243,"action":"add","issue_id":null,"merge_request_id":27,"label_id":null,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z"}],"merge_request_diff":{"id":27,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":27,"relative_order":0,"sha":"bb5206fee213d983da88c47f9cf4cc6caf9c66dc","message":"Feature conflict added\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-08-06T08:35:52.000+02:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-08-06T08:35:52.000+02:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":1,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T10:01:38.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T10:01:38.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":2,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:57:31.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:57:31.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":3,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:54:21.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:54:21.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":4,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:49:50.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:49:50.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":5,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:48:32.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:48:32.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":27,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":3,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":5,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":6,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":8,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":27,"created_at":"2016-06-14T15:02:36.572Z","updated_at":"2016-06-14T15:02:36.658Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"9"},"events":[{"id":221,"target_type":"MergeRequest","target_id":27,"project_id":36,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1},{"id":187,"target_type":"MergeRequest","target_id":27,"project_id":5,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1}],"approvals_before_merge":1,"award_emoji":[{"id":1,"name":"thumbsup","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"},{"id":2,"name":"drum","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"}]} +{"id":26,"target_branch":"master","source_branch":"feature","source_project_id":4,"author_id":1,"assignee_id":null,"title":"MR2","created_at":"2016-06-14T15:02:36.418Z","updated_at":"2016-06-14T15:02:57.013Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":8,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":679,"note":"Qui rerum totam nisi est.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.848Z","updated_at":"2016-06-14T15:02:56.848Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":680,"note":"Pariatur magni corrupti consequatur debitis minima error beatae voluptatem.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.871Z","updated_at":"2016-06-14T15:02:56.871Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":681,"note":"Qui quis ut modi eos rerum ratione.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.895Z","updated_at":"2016-06-14T15:02:56.895Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":682,"note":"Illum quidem expedita mollitia fugit.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.918Z","updated_at":"2016-06-14T15:02:56.918Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":683,"note":"Consectetur voluptate sit sint possimus veritatis quod.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.942Z","updated_at":"2016-06-14T15:02:56.942Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":684,"note":"Natus libero quibusdam rem assumenda deleniti accusamus sed earum.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.966Z","updated_at":"2016-06-14T15:02:56.966Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":685,"note":"Tenetur autem nihil rerum odit.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.989Z","updated_at":"2016-06-14T15:02:56.989Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":686,"note":"Quia maiores et odio sed.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:57.012Z","updated_at":"2016-06-14T15:02:57.012Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":26,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":26,"sha":"0b4bc9a49b562e85de7cc9e834518ea6828729b9","relative_order":0,"message":"Feature added\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:26:01.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:26:01.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":26,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,5 @@\n+class Feature\n+ def foo\n+ puts 'bar'\n+ end\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":26,"created_at":"2016-06-14T15:02:36.421Z","updated_at":"2016-06-14T15:02:36.474Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"1"},"events":[{"id":222,"target_type":"MergeRequest","target_id":26,"project_id":36,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1},{"id":186,"target_type":"MergeRequest","target_id":26,"project_id":5,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1}]} +{"id":15,"target_branch":"test-7","source_branch":"test-1","source_project_id":5,"author_id":22,"assignee_id":16,"title":"Qui accusantium et inventore facilis doloribus occaecati officiis.","created_at":"2016-06-14T15:02:25.168Z","updated_at":"2016-06-14T15:02:59.521Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":7,"description":"Et commodi deserunt aspernatur vero rerum. Ut non dolorum alias in odit est libero. Voluptatibus eos in et vitae repudiandae facilis ex mollitia.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":777,"note":"Pariatur voluptas placeat aspernatur culpa suscipit soluta.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.348Z","updated_at":"2016-06-14T15:02:59.348Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":778,"note":"Alias et iure mollitia suscipit molestiae voluptatum nostrum asperiores.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.372Z","updated_at":"2016-06-14T15:02:59.372Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":779,"note":"Laudantium qui eum qui sunt.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.395Z","updated_at":"2016-06-14T15:02:59.395Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":780,"note":"Quas rem est iusto ut delectus fugiat recusandae mollitia.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.418Z","updated_at":"2016-06-14T15:02:59.418Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":781,"note":"Repellendus ab et qui nesciunt.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.444Z","updated_at":"2016-06-14T15:02:59.444Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":782,"note":"Non possimus voluptatum odio qui ut.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.469Z","updated_at":"2016-06-14T15:02:59.469Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":783,"note":"Dolores repellendus eum ducimus quam ab dolorem quia.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.494Z","updated_at":"2016-06-14T15:02:59.494Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":784,"note":"Facilis dolorem aut corrupti id ratione occaecati.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.520Z","updated_at":"2016-06-14T15:02:59.520Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":15,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":15,"relative_order":0,"sha":"94b8d581c48d894b86661718582fecbc5e3ed2eb","message":"fixes #10\n","authored_date":"2016-01-19T13:22:56.000+01:00","author_name":"James Lopez","author_email":"james@jameslopez.es","committed_date":"2016-01-19T13:22:56.000+01:00","committer_name":"James Lopez","committer_email":"james@jameslopez.es","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}}],"merge_request_diff_files":[{"merge_request_diff_id":15,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":15,"created_at":"2016-06-14T15:02:25.171Z","updated_at":"2016-06-14T15:02:25.230Z","base_commit_sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","real_size":"1"},"events":[{"id":223,"target_type":"MergeRequest","target_id":15,"project_id":36,"created_at":"2016-06-14T15:02:25.262Z","updated_at":"2016-06-14T15:02:25.262Z","action":1,"author_id":1},{"id":175,"target_type":"MergeRequest","target_id":15,"project_id":5,"created_at":"2016-06-14T15:02:25.262Z","updated_at":"2016-06-14T15:02:25.262Z","action":1,"author_id":22}]} +{"id":14,"target_branch":"fix","source_branch":"test-3","source_project_id":5,"author_id":20,"assignee_id":20,"title":"In voluptas aut sequi voluptatem ullam vel corporis illum consequatur.","created_at":"2016-06-14T15:02:24.760Z","updated_at":"2016-06-14T15:02:59.749Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":6,"description":"Dicta magnam non voluptates nam dignissimos nostrum deserunt. Dolorum et suscipit iure quae doloremque. Necessitatibus saepe aut labore sed.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":785,"note":"Atque cupiditate necessitatibus deserunt minus natus odit.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.559Z","updated_at":"2016-06-14T15:02:59.559Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":786,"note":"Non dolorem provident mollitia nesciunt optio ex eveniet.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.587Z","updated_at":"2016-06-14T15:02:59.587Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":787,"note":"Similique officia nemo quasi commodi accusantium quae qui.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.621Z","updated_at":"2016-06-14T15:02:59.621Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":788,"note":"Et est et alias ad dolor qui.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.650Z","updated_at":"2016-06-14T15:02:59.650Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":789,"note":"Numquam temporibus ratione voluptatibus aliquid.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.675Z","updated_at":"2016-06-14T15:02:59.675Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":790,"note":"Ut ex aliquam consectetur perferendis est hic aut quia.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.703Z","updated_at":"2016-06-14T15:02:59.703Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":791,"note":"Esse eos quam quaerat aut ut asperiores officiis.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.726Z","updated_at":"2016-06-14T15:02:59.726Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":792,"note":"Sint facilis accusantium iure blanditiis.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.748Z","updated_at":"2016-06-14T15:02:59.748Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":14,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":14,"relative_order":0,"sha":"ddd4ff416a931589c695eb4f5b23f844426f6928","message":"fixes #10\n","authored_date":"2016-01-19T14:14:43.000+01:00","author_name":"James Lopez","author_email":"james@jameslopez.es","committed_date":"2016-01-19T14:14:43.000+01:00","committer_name":"James Lopez","committer_email":"james@jameslopez.es","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}},{"merge_request_diff_id":14,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","author_name":"Marin Jankovski","author_email":"marin@gitlab.com","committed_date":"2015-12-07T12:52:12.000+01:00","committer_name":"Marin Jankovski","committer_email":"marin@gitlab.com","commit_author":{"name":"Marin Jankovski","email":"marin@gitlab.com"},"committer":{"name":"Marin Jankovski","email":"marin@gitlab.com"}},{"merge_request_diff_id":14,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","author_name":"Marin Jankovski","author_email":"maxlazio@gmail.com","committed_date":"2015-12-07T11:54:28.000+01:00","committer_name":"Marin Jankovski","committer_email":"maxlazio@gmail.com","commit_author":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"},"committer":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"}},{"merge_request_diff_id":14,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T16:27:12.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:50:17.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:39:43.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T07:21:40.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:01:27.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:00:16.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T05:23:14.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:45.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:04.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","author_name":"Stan Hu","author_email":"stanhu@packetzoom.com","committed_date":"2015-08-25T17:53:12.000+02:00","committer_name":"Stan Hu","committer_email":"stanhu@packetzoom.com","commit_author":{"name":"Stan Hu","email":"stanhu@packetzoom.com"},"committer":{"name":"Stan Hu","email":"stanhu@packetzoom.com"}},{"merge_request_diff_id":14,"relative_order":13,"sha":"e56497bb5f03a90a51293fc6d516788730953899","message":"Merge branch 'tree_helper_spec' into 'master'\n\nAdd directory structure for tree_helper spec\n\nThis directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module\n\nSee [merge request #275](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/275#note_732774)\n\nSee merge request !2\n","authored_date":"2015-01-10T22:23:29.000+01:00","author_name":"Sytse Sijbrandij","author_email":"sytse@gitlab.com","committed_date":"2015-01-10T22:23:29.000+01:00","committer_name":"Sytse Sijbrandij","committer_email":"sytse@gitlab.com","commit_author":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"},"committer":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"}},{"merge_request_diff_id":14,"relative_order":14,"sha":"4cd80ccab63c82b4bad16faa5193fbd2aa06df40","message":"add directory structure for tree_helper spec\n","authored_date":"2015-01-10T21:28:18.000+01:00","author_name":"marmis85","author_email":"marmis85@gmail.com","committed_date":"2015-01-10T21:28:18.000+01:00","committer_name":"marmis85","committer_email":"marmis85@gmail.com","commit_author":{"name":"marmis85","email":"marmis85@gmail.com"},"committer":{"name":"marmis85","email":"marmis85@gmail.com"}},{"merge_request_diff_id":14,"relative_order":15,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T10:01:38.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T10:01:38.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":16,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:57:31.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:57:31.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":17,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:54:21.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:54:21.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":18,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:49:50.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:49:50.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":19,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:48:32.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:48:32.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":14,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":14,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":3,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":5,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":14,"relative_order":6,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+\n+\n+ \n+ wm\n+ Created with Sketch.\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":8,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":9,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":10,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":11,"utf8_diff":"--- /dev/null\n+++ b/foo/bar/.gitkeep\n","new_path":"foo/bar/.gitkeep","old_path":"foo/bar/.gitkeep","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":12,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":13,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":14,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":14,"created_at":"2016-06-14T15:02:24.770Z","updated_at":"2016-06-14T15:02:25.007Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"15"},"events":[{"id":224,"target_type":"MergeRequest","target_id":14,"project_id":36,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":1},{"id":174,"target_type":"MergeRequest","target_id":14,"project_id":5,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":20}]} +{"id":13,"target_branch":"improve/awesome","source_branch":"test-8","source_project_id":5,"author_id":16,"assignee_id":25,"title":"Voluptates consequatur eius nemo amet libero animi illum delectus tempore.","created_at":"2016-06-14T15:02:24.415Z","updated_at":"2016-06-14T15:02:59.958Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":5,"description":"Est eaque quasi qui qui. Similique voluptatem impedit iusto ratione reprehenderit. Itaque est illum ut nulla aut.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":793,"note":"In illum maxime aperiam nulla est aspernatur.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.782Z","updated_at":"2016-06-14T15:02:59.782Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[{"merge_request_diff_id":14,"id":529,"target_type":"Note","target_id":793,"project_id":4,"created_at":"2016-07-07T14:35:12.128Z","updated_at":"2016-07-07T14:35:12.128Z","action":6,"author_id":1}]},{"id":794,"note":"Enim quia perferendis cum distinctio tenetur optio voluptas veniam.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.807Z","updated_at":"2016-06-14T15:02:59.807Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":795,"note":"Dolor ad quia quis pariatur ducimus.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.831Z","updated_at":"2016-06-14T15:02:59.831Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":796,"note":"Et a odio voluptate aut.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.854Z","updated_at":"2016-06-14T15:02:59.854Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":797,"note":"Quis nihil temporibus voluptatum modi minima a ut.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.879Z","updated_at":"2016-06-14T15:02:59.879Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":798,"note":"Ut alias consequatur in nostrum.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.904Z","updated_at":"2016-06-14T15:02:59.904Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":799,"note":"Voluptatibus aperiam assumenda et neque sint libero.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.926Z","updated_at":"2016-06-14T15:02:59.926Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":800,"note":"Veritatis voluptatem dolor dolores magni quo ut ipsa fuga.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.956Z","updated_at":"2016-06-14T15:02:59.956Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":13,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":13,"relative_order":0,"sha":"0bfedc29d30280c7e8564e19f654584b459e5868","message":"fixes #10\n","authored_date":"2016-01-19T15:25:23.000+01:00","author_name":"James Lopez","author_email":"james@jameslopez.es","committed_date":"2016-01-19T15:25:23.000+01:00","committer_name":"James Lopez","committer_email":"james@jameslopez.es","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}},{"merge_request_diff_id":13,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","author_name":"Marin Jankovski","author_email":"marin@gitlab.com","committed_date":"2015-12-07T12:52:12.000+01:00","committer_name":"Marin Jankovski","committer_email":"marin@gitlab.com","commit_author":{"name":"Marin Jankovski","email":"marin@gitlab.com"},"committer":{"name":"Marin Jankovski","email":"marin@gitlab.com"}},{"merge_request_diff_id":13,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","author_name":"Marin Jankovski","author_email":"maxlazio@gmail.com","committed_date":"2015-12-07T11:54:28.000+01:00","committer_name":"Marin Jankovski","committer_email":"maxlazio@gmail.com","commit_author":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"},"committer":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"}},{"merge_request_diff_id":13,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T16:27:12.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":13,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:50:17.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":13,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:39:43.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":13,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T07:21:40.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":13,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:01:27.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":13,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:00:16.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":13,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T05:23:14.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":13,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:45.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":13,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:04.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":13,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","author_name":"Stan Hu","author_email":"stanhu@packetzoom.com","committed_date":"2015-08-25T17:53:12.000+02:00","committer_name":"Stan Hu","committer_email":"stanhu@packetzoom.com","commit_author":{"name":"Stan Hu","email":"stanhu@packetzoom.com"},"committer":{"name":"Stan Hu","email":"stanhu@packetzoom.com"}},{"merge_request_diff_id":13,"relative_order":13,"sha":"e56497bb5f03a90a51293fc6d516788730953899","message":"Merge branch 'tree_helper_spec' into 'master'\n\nAdd directory structure for tree_helper spec\n\nThis directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module\n\nSee [merge request #275](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/275#note_732774)\n\nSee merge request !2\n","authored_date":"2015-01-10T22:23:29.000+01:00","author_name":"Sytse Sijbrandij","author_email":"sytse@gitlab.com","committed_date":"2015-01-10T22:23:29.000+01:00","committer_name":"Sytse Sijbrandij","committer_email":"sytse@gitlab.com","commit_author":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"},"committer":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"}},{"merge_request_diff_id":13,"relative_order":14,"sha":"4cd80ccab63c82b4bad16faa5193fbd2aa06df40","message":"add directory structure for tree_helper spec\n","authored_date":"2015-01-10T21:28:18.000+01:00","author_name":"marmis85","author_email":"marmis85@gmail.com","committed_date":"2015-01-10T21:28:18.000+01:00","committer_name":"marmis85","committer_email":"marmis85@gmail.com","commit_author":{"name":"marmis85","email":"marmis85@gmail.com"},"committer":{"name":"marmis85","email":"marmis85@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":13,"relative_order":0,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":1,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":2,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+\n+\n+ \n+ wm\n+ Created with Sketch.\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":3,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":5,"utf8_diff":"--- /dev/null\n+++ b/foo/bar/.gitkeep\n","new_path":"foo/bar/.gitkeep","old_path":"foo/bar/.gitkeep","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":6,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":13,"created_at":"2016-06-14T15:02:24.420Z","updated_at":"2016-06-14T15:02:24.561Z","base_commit_sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","real_size":"7"},"events":[{"id":225,"target_type":"MergeRequest","target_id":13,"project_id":36,"created_at":"2016-06-14T15:02:24.636Z","updated_at":"2016-06-14T15:02:24.636Z","action":1,"author_id":16},{"id":173,"target_type":"MergeRequest","target_id":13,"project_id":5,"created_at":"2016-06-14T15:02:24.636Z","updated_at":"2016-06-14T15:02:24.636Z","action":1,"author_id":16}]} +{"id":12,"target_branch":"flatten-dirs","source_branch":"test-2","source_project_id":5,"author_id":1,"assignee_id":22,"title":"In a rerum harum nihil accusamus aut quia nobis non.","created_at":"2016-06-14T15:02:24.000Z","updated_at":"2016-06-14T15:03:00.225Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":4,"description":"Nam magnam odit velit rerum. Sapiente dolore sunt saepe debitis. Culpa maiores ut ad dolores dolorem et.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":801,"note":"Nihil dicta molestias expedita atque.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:03:00.001Z","updated_at":"2016-06-14T15:03:00.001Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":802,"note":"Illum culpa voluptas enim accusantium deserunt.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:03:00.034Z","updated_at":"2016-06-14T15:03:00.034Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":803,"note":"Dicta esse aliquam laboriosam unde alias.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:03:00.065Z","updated_at":"2016-06-14T15:03:00.065Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":804,"note":"Dicta autem et sed molestiae ut quae.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:03:00.097Z","updated_at":"2016-06-14T15:03:00.097Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":805,"note":"Ut ut temporibus voluptas dolore quia velit.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:03:00.129Z","updated_at":"2016-06-14T15:03:00.129Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":806,"note":"Dolores similique sint pariatur error id quia fugit aut.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:03:00.162Z","updated_at":"2016-06-14T15:03:00.162Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":807,"note":"Quisquam provident nihil aperiam voluptatem.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:03:00.193Z","updated_at":"2016-06-14T15:03:00.193Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":808,"note":"Similique quo vero expedita deserunt ipsam earum.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:03:00.224Z","updated_at":"2016-06-14T15:03:00.224Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":12,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":12,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":12,"relative_order":0,"sha":"97a0df9696e2aebf10c31b3016f40214e0e8f243","message":"fixes #10\n","authored_date":"2016-01-19T14:08:21.000+01:00","author_name":"James Lopez","author_email":"james@jameslopez.es","committed_date":"2016-01-19T14:08:21.000+01:00","committer_name":"James Lopez","committer_email":"james@jameslopez.es","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}},{"merge_request_diff_id":12,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","author_name":"Marin Jankovski","author_email":"marin@gitlab.com","committed_date":"2015-12-07T12:52:12.000+01:00","committer_name":"Marin Jankovski","committer_email":"marin@gitlab.com","commit_author":{"name":"Marin Jankovski","email":"marin@gitlab.com"},"committer":{"name":"Marin Jankovski","email":"marin@gitlab.com"}},{"merge_request_diff_id":12,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","author_name":"Marin Jankovski","author_email":"maxlazio@gmail.com","committed_date":"2015-12-07T11:54:28.000+01:00","committer_name":"Marin Jankovski","committer_email":"maxlazio@gmail.com","commit_author":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"},"committer":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"}},{"merge_request_diff_id":12,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T16:27:12.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":12,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:50:17.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":12,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:39:43.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":12,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T07:21:40.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":12,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:01:27.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":12,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:00:16.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":12,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T05:23:14.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":12,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:45.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":12,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:04.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":12,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","author_name":"Stan Hu","author_email":"stanhu@packetzoom.com","committed_date":"2015-08-25T17:53:12.000+02:00","committer_name":"Stan Hu","committer_email":"stanhu@packetzoom.com","commit_author":{"name":"Stan Hu","email":"stanhu@packetzoom.com"},"committer":{"name":"Stan Hu","email":"stanhu@packetzoom.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":12,"relative_order":0,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":12,"relative_order":1,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":12,"relative_order":2,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+\n+\n+ \n+ wm\n+ Created with Sketch.\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":12,"relative_order":3,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":12,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":12,"relative_order":5,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":12,"created_at":"2016-06-14T15:02:24.006Z","updated_at":"2016-06-14T15:02:24.169Z","base_commit_sha":"e56497bb5f03a90a51293fc6d516788730953899","real_size":"6"},"events":[{"id":226,"target_type":"MergeRequest","target_id":12,"project_id":36,"created_at":"2016-06-14T15:02:24.253Z","updated_at":"2016-06-14T15:02:24.253Z","action":1,"author_id":1},{"id":172,"target_type":"MergeRequest","target_id":12,"project_id":5,"created_at":"2016-06-14T15:02:24.253Z","updated_at":"2016-06-14T15:02:24.253Z","action":1,"author_id":1}]} {"id":11,"target_branch":"test-15","source_branch":"'test'","source_project_id":5,"author_id":16,"assignee_id":16,"title":"Corporis provident similique perspiciatis dolores eos animi.","created_at":"2016-06-14T15:02:23.767Z","updated_at":"2016-06-14T15:03:00.475Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":3,"description":"Libero nesciunt mollitia quis odit eos vero quasi. Iure voluptatem ut sint pariatur voluptates ut aut. Laborum possimus unde illum ipsum eum.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":809,"note":"Omnis ratione laboriosam dolores qui.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:03:00.260Z","updated_at":"2016-06-14T15:03:00.260Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":11,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":810,"note":"Voluptas voluptates pariatur dolores maxime est voluptas.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:03:00.290Z","updated_at":"2016-06-14T15:03:00.290Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":11,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":811,"note":"Sit perspiciatis facilis ipsum consequatur.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:03:00.323Z","updated_at":"2016-06-14T15:03:00.323Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":11,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":812,"note":"Ut neque aliquam nam et est.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:03:00.349Z","updated_at":"2016-06-14T15:03:00.349Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":11,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":813,"note":"Et debitis rerum minima sit aut dolorem.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:03:00.374Z","updated_at":"2016-06-14T15:03:00.374Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":11,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":814,"note":"Ea nisi earum fugit iste aperiam consequatur.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:03:00.397Z","updated_at":"2016-06-14T15:03:00.397Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":11,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":815,"note":"Amet ratione consequatur laudantium rerum voluptas est nobis.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:03:00.450Z","updated_at":"2016-06-14T15:03:00.450Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":11,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":816,"note":"Ab ducimus cumque quia dolorem vitae sint beatae rerum.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:03:00.474Z","updated_at":"2016-06-14T15:03:00.474Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":11,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":11,"state":"empty","merge_request_diff_commits":[],"merge_request_diff_files":[],"merge_request_id":11,"created_at":"2016-06-14T15:02:23.772Z","updated_at":"2016-06-14T15:02:23.833Z","base_commit_sha":"e56497bb5f03a90a51293fc6d516788730953899","real_size":null},"events":[{"id":227,"target_type":"MergeRequest","target_id":11,"project_id":36,"created_at":"2016-06-14T15:02:23.865Z","updated_at":"2016-06-14T15:02:23.865Z","action":1,"author_id":16},{"id":171,"target_type":"MergeRequest","target_id":11,"project_id":5,"created_at":"2016-06-14T15:02:23.865Z","updated_at":"2016-06-14T15:02:23.865Z","action":1,"author_id":16}]} -{"id":10,"target_branch":"feature","source_branch":"test-5","source_project_id":5,"author_id":20,"assignee_id":25,"title":"Eligendi reprehenderit doloribus quia et sit id.","created_at":"2016-06-14T15:02:23.014Z","updated_at":"2016-06-14T15:03:00.685Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":2,"description":"Ut dolor quia aliquid dolore et nisi. Est minus suscipit enim quaerat sapiente consequatur rerum. Eveniet provident consequatur dolor accusantium reiciendis.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":817,"note":"Recusandae et voluptas enim qui et.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:03:00.510Z","updated_at":"2016-06-14T15:03:00.510Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":818,"note":"Asperiores dolorem rerum ipsum totam.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:03:00.538Z","updated_at":"2016-06-14T15:03:00.538Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":819,"note":"Qui quam et iure quasi provident cumque itaque sequi.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:03:00.562Z","updated_at":"2016-06-14T15:03:00.562Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":820,"note":"Sint accusantium aliquid iste qui iusto minus vel.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:03:00.585Z","updated_at":"2016-06-14T15:03:00.585Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":821,"note":"Dolor corrupti dolorem blanditiis voluptas.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:03:00.610Z","updated_at":"2016-06-14T15:03:00.610Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":822,"note":"Est perferendis assumenda aliquam aliquid sit ipsum ullam aut.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:03:00.635Z","updated_at":"2016-06-14T15:03:00.635Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":823,"note":"Hic neque reiciendis quaerat maiores.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:03:00.659Z","updated_at":"2016-06-14T15:03:00.659Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":824,"note":"Sequi architecto doloribus ut vel autem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:03:00.683Z","updated_at":"2016-06-14T15:03:00.683Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":10,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":10,"relative_order":0,"sha":"f998ac87ac9244f15e9c15109a6f4e62a54b779d","message":"fixes #10\n","authored_date":"2016-01-19T14:43:23.000+01:00","author_name":"James Lopez","author_email":"james@jameslopez.es","committed_date":"2016-01-19T14:43:23.000+01:00","committer_name":"James Lopez","committer_email":"james@jameslopez.es"},{"merge_request_diff_id":10,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","author_name":"Marin Jankovski","author_email":"marin@gitlab.com","committed_date":"2015-12-07T12:52:12.000+01:00","committer_name":"Marin Jankovski","committer_email":"marin@gitlab.com"},{"merge_request_diff_id":10,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","author_name":"Marin Jankovski","author_email":"maxlazio@gmail.com","committed_date":"2015-12-07T11:54:28.000+01:00","committer_name":"Marin Jankovski","committer_email":"maxlazio@gmail.com"},{"merge_request_diff_id":10,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T16:27:12.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":10,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:50:17.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":10,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:39:43.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":10,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T07:21:40.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":10,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:01:27.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com"},{"merge_request_diff_id":10,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:00:16.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com"},{"merge_request_diff_id":10,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T05:23:14.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com"},{"merge_request_diff_id":10,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:45.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com"},{"merge_request_diff_id":10,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:04.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com"},{"merge_request_diff_id":10,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","author_name":"Stan Hu","author_email":"stanhu@packetzoom.com","committed_date":"2015-08-25T17:53:12.000+02:00","committer_name":"Stan Hu","committer_email":"stanhu@packetzoom.com"},{"merge_request_diff_id":10,"relative_order":13,"sha":"e56497bb5f03a90a51293fc6d516788730953899","message":"Merge branch 'tree_helper_spec' into 'master'\n\nAdd directory structure for tree_helper spec\n\nThis directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module\n\nSee [merge request #275](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/275#note_732774)\n\nSee merge request !2\n","authored_date":"2015-01-10T22:23:29.000+01:00","author_name":"Sytse Sijbrandij","author_email":"sytse@gitlab.com","committed_date":"2015-01-10T22:23:29.000+01:00","committer_name":"Sytse Sijbrandij","committer_email":"sytse@gitlab.com"},{"merge_request_diff_id":10,"relative_order":14,"sha":"4cd80ccab63c82b4bad16faa5193fbd2aa06df40","message":"add directory structure for tree_helper spec\n","authored_date":"2015-01-10T21:28:18.000+01:00","author_name":"marmis85","author_email":"marmis85@gmail.com","committed_date":"2015-01-10T21:28:18.000+01:00","committer_name":"marmis85","committer_email":"marmis85@gmail.com"},{"merge_request_diff_id":10,"relative_order":16,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T10:01:38.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T10:01:38.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com"},{"merge_request_diff_id":10,"relative_order":17,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:57:31.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:57:31.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com"},{"merge_request_diff_id":10,"relative_order":18,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:54:21.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:54:21.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com"},{"merge_request_diff_id":10,"relative_order":19,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:49:50.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:49:50.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com"},{"merge_request_diff_id":10,"relative_order":20,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:48:32.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:48:32.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com"}],"merge_request_diff_files":[{"merge_request_diff_id":10,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":10,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":3,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":5,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":10,"relative_order":6,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+\n+\n+ \n+ wm\n+ Created with Sketch.\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":8,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":9,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":10,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":11,"utf8_diff":"--- /dev/null\n+++ b/foo/bar/.gitkeep\n","new_path":"foo/bar/.gitkeep","old_path":"foo/bar/.gitkeep","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":12,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":13,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":14,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":10,"created_at":"2016-06-14T15:02:23.019Z","updated_at":"2016-06-14T15:02:23.493Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"15"},"events":[{"id":228,"target_type":"MergeRequest","target_id":10,"project_id":36,"created_at":"2016-06-14T15:02:23.660Z","updated_at":"2016-06-14T15:02:23.660Z","action":1,"author_id":1},{"id":170,"target_type":"MergeRequest","target_id":10,"project_id":5,"created_at":"2016-06-14T15:02:23.660Z","updated_at":"2016-06-14T15:02:23.660Z","action":1,"author_id":20}]} -{"id":9,"target_branch":"test-6","source_branch":"test-12","source_project_id":5,"author_id":16,"assignee_id":6,"title":"Et ipsam voluptas velit sequi illum ut.","created_at":"2016-06-14T15:02:22.825Z","updated_at":"2016-06-14T15:03:00.904Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":1,"description":"Eveniet nihil ratione veniam similique qui aut sapiente tempora. Sed praesentium iusto dignissimos possimus id repudiandae quo nihil. Qui doloremque autem et iure fugit.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":825,"note":"Aliquid voluptatem consequatur voluptas ex perspiciatis.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:03:00.722Z","updated_at":"2016-06-14T15:03:00.722Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":826,"note":"Itaque optio voluptatem praesentium voluptas.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:03:00.745Z","updated_at":"2016-06-14T15:03:00.745Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":827,"note":"Ut est corporis fuga asperiores delectus excepturi aperiam.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:03:00.771Z","updated_at":"2016-06-14T15:03:00.771Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":828,"note":"Similique ea dolore officiis temporibus.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:03:00.798Z","updated_at":"2016-06-14T15:03:00.798Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":829,"note":"Qui laudantium qui quae quis.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:03:00.828Z","updated_at":"2016-06-14T15:03:00.828Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":830,"note":"Et vel voluptas amet laborum qui soluta.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:03:00.850Z","updated_at":"2016-06-14T15:03:00.850Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":831,"note":"Enim ad consequuntur assumenda provident voluptatem similique deleniti.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:03:00.876Z","updated_at":"2016-06-14T15:03:00.876Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":832,"note":"Officiis sequi commodi pariatur totam fugiat voluptas corporis dignissimos.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:03:00.902Z","updated_at":"2016-06-14T15:03:00.902Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":9,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":9,"relative_order":0,"sha":"a4e5dfebf42e34596526acb8611bc7ed80e4eb3f","message":"fixes #10\n","authored_date":"2016-01-19T15:44:02.000+01:00","author_name":"James Lopez","author_email":"james@jameslopez.es","committed_date":"2016-01-19T15:44:02.000+01:00","committer_name":"James Lopez","committer_email":"james@jameslopez.es"}],"merge_request_diff_files":[{"merge_request_diff_id":9,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":9,"created_at":"2016-06-14T15:02:22.829Z","updated_at":"2016-06-14T15:02:22.900Z","base_commit_sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","real_size":"1"},"events":[{"id":229,"target_type":"MergeRequest","target_id":9,"project_id":36,"created_at":"2016-06-14T15:02:22.927Z","updated_at":"2016-06-14T15:02:22.927Z","action":1,"author_id":16},{"id":169,"target_type":"MergeRequest","target_id":9,"project_id":5,"created_at":"2016-06-14T15:02:22.927Z","updated_at":"2016-06-14T15:02:22.927Z","action":1,"author_id":16}]} +{"id":10,"target_branch":"feature","source_branch":"test-5","source_project_id":5,"author_id":20,"assignee_id":25,"title":"Eligendi reprehenderit doloribus quia et sit id.","created_at":"2016-06-14T15:02:23.014Z","updated_at":"2016-06-14T15:03:00.685Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":2,"description":"Ut dolor quia aliquid dolore et nisi. Est minus suscipit enim quaerat sapiente consequatur rerum. Eveniet provident consequatur dolor accusantium reiciendis.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":817,"note":"Recusandae et voluptas enim qui et.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:03:00.510Z","updated_at":"2016-06-14T15:03:00.510Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":818,"note":"Asperiores dolorem rerum ipsum totam.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:03:00.538Z","updated_at":"2016-06-14T15:03:00.538Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":819,"note":"Qui quam et iure quasi provident cumque itaque sequi.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:03:00.562Z","updated_at":"2016-06-14T15:03:00.562Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":820,"note":"Sint accusantium aliquid iste qui iusto minus vel.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:03:00.585Z","updated_at":"2016-06-14T15:03:00.585Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":821,"note":"Dolor corrupti dolorem blanditiis voluptas.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:03:00.610Z","updated_at":"2016-06-14T15:03:00.610Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":822,"note":"Est perferendis assumenda aliquam aliquid sit ipsum ullam aut.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:03:00.635Z","updated_at":"2016-06-14T15:03:00.635Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":823,"note":"Hic neque reiciendis quaerat maiores.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:03:00.659Z","updated_at":"2016-06-14T15:03:00.659Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":824,"note":"Sequi architecto doloribus ut vel autem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:03:00.683Z","updated_at":"2016-06-14T15:03:00.683Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":10,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":10,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":10,"relative_order":0,"sha":"f998ac87ac9244f15e9c15109a6f4e62a54b779d","message":"fixes #10\n","authored_date":"2016-01-19T14:43:23.000+01:00","author_name":"James Lopez","author_email":"james@jameslopez.es","committed_date":"2016-01-19T14:43:23.000+01:00","committer_name":"James Lopez","committer_email":"james@jameslopez.es","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}},{"merge_request_diff_id":10,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","author_name":"Marin Jankovski","author_email":"marin@gitlab.com","committed_date":"2015-12-07T12:52:12.000+01:00","committer_name":"Marin Jankovski","committer_email":"marin@gitlab.com","commit_author":{"name":"Marin Jankovski","email":"marin@gitlab.com"},"committer":{"name":"Marin Jankovski","email":"marin@gitlab.com"}},{"merge_request_diff_id":10,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","author_name":"Marin Jankovski","author_email":"maxlazio@gmail.com","committed_date":"2015-12-07T11:54:28.000+01:00","committer_name":"Marin Jankovski","committer_email":"maxlazio@gmail.com","commit_author":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"},"committer":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"}},{"merge_request_diff_id":10,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T16:27:12.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":10,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:50:17.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":10,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T08:39:43.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":10,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T07:21:40.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":10,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:01:27.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":10,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T06:00:16.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":10,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","author_name":"Stan Hu","author_email":"stanhu@gmail.com","committed_date":"2015-11-13T05:23:14.000+01:00","committer_name":"Stan Hu","committer_email":"stanhu@gmail.com","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":10,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:45.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":10,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","author_name":"윤민식","author_email":"minsik.yoon@samsung.com","committed_date":"2015-11-13T05:08:04.000+01:00","committer_name":"윤민식","committer_email":"minsik.yoon@samsung.com","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":10,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","author_name":"Stan Hu","author_email":"stanhu@packetzoom.com","committed_date":"2015-08-25T17:53:12.000+02:00","committer_name":"Stan Hu","committer_email":"stanhu@packetzoom.com","commit_author":{"name":"Stan Hu","email":"stanhu@packetzoom.com"},"committer":{"name":"Stan Hu","email":"stanhu@packetzoom.com"}},{"merge_request_diff_id":10,"relative_order":13,"sha":"e56497bb5f03a90a51293fc6d516788730953899","message":"Merge branch 'tree_helper_spec' into 'master'\n\nAdd directory structure for tree_helper spec\n\nThis directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module\n\nSee [merge request #275](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/275#note_732774)\n\nSee merge request !2\n","authored_date":"2015-01-10T22:23:29.000+01:00","author_name":"Sytse Sijbrandij","author_email":"sytse@gitlab.com","committed_date":"2015-01-10T22:23:29.000+01:00","committer_name":"Sytse Sijbrandij","committer_email":"sytse@gitlab.com","commit_author":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"},"committer":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"}},{"merge_request_diff_id":10,"relative_order":14,"sha":"4cd80ccab63c82b4bad16faa5193fbd2aa06df40","message":"add directory structure for tree_helper spec\n","authored_date":"2015-01-10T21:28:18.000+01:00","author_name":"marmis85","author_email":"marmis85@gmail.com","committed_date":"2015-01-10T21:28:18.000+01:00","committer_name":"marmis85","committer_email":"marmis85@gmail.com","commit_author":{"name":"marmis85","email":"marmis85@gmail.com"},"committer":{"name":"marmis85","email":"marmis85@gmail.com"}},{"merge_request_diff_id":10,"relative_order":16,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T10:01:38.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T10:01:38.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":10,"relative_order":17,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:57:31.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:57:31.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":10,"relative_order":18,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:54:21.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:54:21.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":10,"relative_order":19,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:49:50.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:49:50.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":10,"relative_order":20,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:48:32.000+01:00","author_name":"Dmitriy Zaporozhets","author_email":"dmitriy.zaporozhets@gmail.com","committed_date":"2014-02-27T09:48:32.000+01:00","committer_name":"Dmitriy Zaporozhets","committer_email":"dmitriy.zaporozhets@gmail.com","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":10,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":10,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":3,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":5,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":10,"relative_order":6,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+\n+\n+ \n+ wm\n+ Created with Sketch.\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":8,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":9,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":10,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":11,"utf8_diff":"--- /dev/null\n+++ b/foo/bar/.gitkeep\n","new_path":"foo/bar/.gitkeep","old_path":"foo/bar/.gitkeep","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":12,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":13,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":10,"relative_order":14,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":10,"created_at":"2016-06-14T15:02:23.019Z","updated_at":"2016-06-14T15:02:23.493Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"15"},"events":[{"id":228,"target_type":"MergeRequest","target_id":10,"project_id":36,"created_at":"2016-06-14T15:02:23.660Z","updated_at":"2016-06-14T15:02:23.660Z","action":1,"author_id":1},{"id":170,"target_type":"MergeRequest","target_id":10,"project_id":5,"created_at":"2016-06-14T15:02:23.660Z","updated_at":"2016-06-14T15:02:23.660Z","action":1,"author_id":20}]} +{"id":9,"target_branch":"test-6","source_branch":"test-12","source_project_id":5,"author_id":16,"assignee_id":6,"title":"Et ipsam voluptas velit sequi illum ut.","created_at":"2016-06-14T15:02:22.825Z","updated_at":"2016-06-14T15:03:00.904Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":1,"description":"Eveniet nihil ratione veniam similique qui aut sapiente tempora. Sed praesentium iusto dignissimos possimus id repudiandae quo nihil. Qui doloremque autem et iure fugit.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":825,"note":"Aliquid voluptatem consequatur voluptas ex perspiciatis.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:03:00.722Z","updated_at":"2016-06-14T15:03:00.722Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":826,"note":"Itaque optio voluptatem praesentium voluptas.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:03:00.745Z","updated_at":"2016-06-14T15:03:00.745Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":827,"note":"Ut est corporis fuga asperiores delectus excepturi aperiam.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:03:00.771Z","updated_at":"2016-06-14T15:03:00.771Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":828,"note":"Similique ea dolore officiis temporibus.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:03:00.798Z","updated_at":"2016-06-14T15:03:00.798Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":829,"note":"Qui laudantium qui quae quis.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:03:00.828Z","updated_at":"2016-06-14T15:03:00.828Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":830,"note":"Et vel voluptas amet laborum qui soluta.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:03:00.850Z","updated_at":"2016-06-14T15:03:00.850Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":831,"note":"Enim ad consequuntur assumenda provident voluptatem similique deleniti.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:03:00.876Z","updated_at":"2016-06-14T15:03:00.876Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":832,"note":"Officiis sequi commodi pariatur totam fugiat voluptas corporis dignissimos.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:03:00.902Z","updated_at":"2016-06-14T15:03:00.902Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":9,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":9,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":9,"relative_order":0,"sha":"a4e5dfebf42e34596526acb8611bc7ed80e4eb3f","message":"fixes #10\n","authored_date":"2016-01-19T15:44:02.000+01:00","author_name":"James Lopez","author_email":"james@jameslopez.es","committed_date":"2016-01-19T15:44:02.000+01:00","committer_name":"James Lopez","committer_email":"james@jameslopez.es","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}}],"merge_request_diff_files":[{"merge_request_diff_id":9,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":9,"created_at":"2016-06-14T15:02:22.829Z","updated_at":"2016-06-14T15:02:22.900Z","base_commit_sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","real_size":"1"},"events":[{"id":229,"target_type":"MergeRequest","target_id":9,"project_id":36,"created_at":"2016-06-14T15:02:22.927Z","updated_at":"2016-06-14T15:02:22.927Z","action":1,"author_id":16},{"id":169,"target_type":"MergeRequest","target_id":9,"project_id":5,"created_at":"2016-06-14T15:02:22.927Z","updated_at":"2016-06-14T15:02:22.927Z","action":1,"author_id":16}]} diff --git a/spec/frontend/sidebar/mock_data.js b/spec/frontend/sidebar/mock_data.js index 6366600b0e8..9fab24d7518 100644 --- a/spec/frontend/sidebar/mock_data.js +++ b/spec/frontend/sidebar/mock_data.js @@ -530,6 +530,7 @@ export const mockMilestone1 = { title: 'Foobar Milestone', webUrl: 'http://gdk.test:3000/groups/gitlab-org/-/milestones/1', state: 'active', + expired: false, }; export const mockMilestone2 = { @@ -538,6 +539,7 @@ export const mockMilestone2 = { title: 'Awesome Milestone', webUrl: 'http://gdk.test:3000/groups/gitlab-org/-/milestones/2', state: 'active', + expired: false, }; export const mockProjectMilestonesResponse = { @@ -571,6 +573,7 @@ export const mockMilestoneMutationResponse = { id: 'gid://gitlab/Milestone/2', title: 'Awesome Milestone', state: 'active', + expired: false, __typename: 'Milestone', }, __typename: 'Issue', diff --git a/spec/lib/gitlab/background_migration/migrate_merge_request_diff_commit_users_spec.rb b/spec/lib/gitlab/background_migration/migrate_merge_request_diff_commit_users_spec.rb new file mode 100644 index 00000000000..496ce151032 --- /dev/null +++ b/spec/lib/gitlab/background_migration/migrate_merge_request_diff_commit_users_spec.rb @@ -0,0 +1,400 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::MigrateMergeRequestDiffCommitUsers do + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:users) { table(:users) } + let(:merge_requests) { table(:merge_requests) } + let(:diffs) { table(:merge_request_diffs) } + let(:commits) do + table(:merge_request_diff_commits).tap do |t| + t.extend(SuppressCompositePrimaryKeyWarning) + end + end + + let(:commit_users) { described_class::MergeRequestDiffCommitUser } + + let(:namespace) { namespaces.create!(name: 'foo', path: 'foo') } + let(:project) { projects.create!(namespace_id: namespace.id) } + let(:merge_request) do + merge_requests.create!( + source_branch: 'x', + target_branch: 'master', + target_project_id: project.id + ) + end + + let(:diff) { diffs.create!(merge_request_id: merge_request.id) } + let(:migration) { described_class.new } + + describe 'MergeRequestDiffCommit' do + describe '.each_row_to_migrate' do + it 'yields the rows to migrate for a given range' do + commit1 = commits.create!( + merge_request_diff_id: diff.id, + relative_order: 0, + sha: Gitlab::Database::ShaAttribute.serialize('123abc'), + author_name: 'bob', + author_email: 'bob@example.com', + committer_name: 'bob', + committer_email: 'bob@example.com' + ) + + commit2 = commits.create!( + merge_request_diff_id: diff.id, + relative_order: 1, + sha: Gitlab::Database::ShaAttribute.serialize('123abc'), + author_name: 'Alice', + author_email: 'alice@example.com', + committer_name: 'Alice', + committer_email: 'alice@example.com' + ) + + # We stub this constant to make sure we run at least two pagination + # queries for getting the data. This way we can test if the pagination + # is actually working properly. + stub_const( + 'Gitlab::BackgroundMigration::MigrateMergeRequestDiffCommitUsers::COMMIT_ROWS_PER_QUERY', + 1 + ) + + rows = [] + + described_class::MergeRequestDiffCommit.each_row_to_migrate(diff.id, diff.id + 1) do |row| + rows << row + end + + expect(rows.length).to eq(2) + + expect(rows[0].author_name).to eq(commit1.author_name) + expect(rows[1].author_name).to eq(commit2.author_name) + end + end + end + + describe 'MergeRequestDiffCommitUser' do + describe '.union' do + it 'produces a union of the given queries' do + alice = commit_users.create!(name: 'Alice', email: 'alice@example.com') + bob = commit_users.create!(name: 'Bob', email: 'bob@example.com') + users = commit_users.union([ + commit_users.where(name: 'Alice').to_sql, + commit_users.where(name: 'Bob').to_sql + ]) + + expect(users).to include(alice) + expect(users).to include(bob) + end + end + end + + describe '#perform' do + it 'migrates the data in the range' do + commits.create!( + merge_request_diff_id: diff.id, + relative_order: 0, + sha: Gitlab::Database::ShaAttribute.serialize('123abc'), + author_name: 'bob', + author_email: 'bob@example.com', + committer_name: 'bob', + committer_email: 'bob@example.com' + ) + + migration.perform(diff.id, diff.id + 1) + + bob = commit_users.find_by(name: 'bob') + commit = commits.first + + expect(commit.commit_author_id).to eq(bob.id) + expect(commit.committer_id).to eq(bob.id) + end + + it 'treats empty names and Emails the same as NULL values' do + commits.create!( + merge_request_diff_id: diff.id, + relative_order: 0, + sha: Gitlab::Database::ShaAttribute.serialize('123abc'), + author_name: 'bob', + author_email: 'bob@example.com', + committer_name: '', + committer_email: '' + ) + + migration.perform(diff.id, diff.id + 1) + + bob = commit_users.find_by(name: 'bob') + commit = commits.first + + expect(commit.commit_author_id).to eq(bob.id) + expect(commit.committer_id).to be_nil + end + + it 'does not update rows without a committer and author' do + commits.create!( + merge_request_diff_id: diff.id, + relative_order: 0, + sha: Gitlab::Database::ShaAttribute.serialize('123abc') + ) + + migration.perform(diff.id, diff.id + 1) + + commit = commits.first + + expect(commit_users.count).to eq(0) + expect(commit.commit_author_id).to be_nil + expect(commit.committer_id).to be_nil + end + + it 'marks the background job as done' do + Gitlab::Database::BackgroundMigrationJob.create!( + class_name: 'MigrateMergeRequestDiffCommitUsers', + arguments: [diff.id, diff.id + 1] + ) + + migration.perform(diff.id, diff.id + 1) + + job = Gitlab::Database::BackgroundMigrationJob.first + + expect(job.status).to eq('succeeded') + end + end + + describe '#get_data_to_update' do + it 'returns the users and commit rows to update' do + commits.create!( + merge_request_diff_id: diff.id, + relative_order: 0, + sha: Gitlab::Database::ShaAttribute.serialize('123abc'), + author_name: 'bob' + ('a' * 510), + author_email: 'bob@example.com', + committer_name: 'bob' + ('a' * 510), + committer_email: 'bob@example.com' + ) + + commits.create!( + merge_request_diff_id: diff.id, + relative_order: 1, + sha: Gitlab::Database::ShaAttribute.serialize('456abc'), + author_name: 'alice', + author_email: 'alice@example.com', + committer_name: 'alice', + committer_email: 'alice@example.com' + ) + + users, to_update = migration.get_data_to_update(diff.id, diff.id + 1) + + bob_name = 'bob' + ('a' * 509) + + expect(users).to include(%w[alice alice@example.com]) + expect(users).to include([bob_name, 'bob@example.com']) + + expect(to_update[[diff.id, 0]]) + .to eq([[bob_name, 'bob@example.com'], [bob_name, 'bob@example.com']]) + + expect(to_update[[diff.id, 1]]) + .to eq([%w[alice alice@example.com], %w[alice alice@example.com]]) + end + + it 'does not include a user if both the name and Email are missing' do + commits.create!( + merge_request_diff_id: diff.id, + relative_order: 0, + sha: Gitlab::Database::ShaAttribute.serialize('123abc'), + author_name: nil, + author_email: nil, + committer_name: 'bob', + committer_email: 'bob@example.com' + ) + + users, _ = migration.get_data_to_update(diff.id, diff.id + 1) + + expect(users).to eq([%w[bob bob@example.com]].to_set) + end + end + + describe '#get_user_rows_in_batches' do + it 'retrieves all existing users' do + alice = commit_users.create!(name: 'alice', email: 'alice@example.com') + bob = commit_users.create!(name: 'bob', email: 'bob@example.com') + + users = [[alice.name, alice.email], [bob.name, bob.email]] + mapping = {} + + migration.get_user_rows_in_batches(users, mapping) + + expect(mapping[%w[alice alice@example.com]]).to eq(alice) + expect(mapping[%w[bob bob@example.com]]).to eq(bob) + end + end + + describe '#create_missing_users' do + it 'creates merge request diff commit users that are missing' do + alice = commit_users.create!(name: 'alice', email: 'alice@example.com') + users = [%w[alice alice@example.com], %w[bob bob@example.com]] + mapping = { %w[alice alice@example.com] => alice } + + migration.create_missing_users(users, mapping) + + expect(mapping[%w[alice alice@example.com]]).to eq(alice) + expect(mapping[%w[bob bob@example.com]].name).to eq('bob') + expect(mapping[%w[bob bob@example.com]].email).to eq('bob@example.com') + end + end + + describe '#update_commit_rows' do + it 'updates the merge request diff commit rows' do + to_update = { [42, 0] => [%w[alice alice@example.com], []] } + user_mapping = { %w[alice alice@example.com] => double(:user, id: 1) } + + expect(migration) + .to receive(:bulk_update_commit_rows) + .with({ [42, 0] => [1, nil] }) + + migration.update_commit_rows(to_update, user_mapping) + end + end + + describe '#bulk_update_commit_rows' do + context 'when there are no authors and committers' do + it 'does not update any rows' do + migration.bulk_update_commit_rows({ [1, 0] => [] }) + + expect(described_class::MergeRequestDiffCommit.connection) + .not_to receive(:execute) + end + end + + context 'when there are only authors' do + it 'only updates the author IDs' do + author = commit_users.create!(name: 'Alice', email: 'alice@example.com') + commit = commits.create!( + merge_request_diff_id: diff.id, + relative_order: 0, + sha: Gitlab::Database::ShaAttribute.serialize('123abc') + ) + + mapping = { + [commit.merge_request_diff_id, commit.relative_order] => + [author.id, nil] + } + + migration.bulk_update_commit_rows(mapping) + + commit = commits.first + + expect(commit.commit_author_id).to eq(author.id) + expect(commit.committer_id).to be_nil + end + end + + context 'when there are only committers' do + it 'only updates the committer IDs' do + committer = + commit_users.create!(name: 'Alice', email: 'alice@example.com') + + commit = commits.create!( + merge_request_diff_id: diff.id, + relative_order: 0, + sha: Gitlab::Database::ShaAttribute.serialize('123abc') + ) + + mapping = { + [commit.merge_request_diff_id, commit.relative_order] => + [nil, committer.id] + } + + migration.bulk_update_commit_rows(mapping) + + commit = commits.first + + expect(commit.committer_id).to eq(committer.id) + expect(commit.commit_author_id).to be_nil + end + end + + context 'when there are both authors and committers' do + it 'updates both the author and committer IDs' do + author = commit_users.create!(name: 'Bob', email: 'bob@example.com') + committer = + commit_users.create!(name: 'Alice', email: 'alice@example.com') + + commit = commits.create!( + merge_request_diff_id: diff.id, + relative_order: 0, + sha: Gitlab::Database::ShaAttribute.serialize('123abc') + ) + + mapping = { + [commit.merge_request_diff_id, commit.relative_order] => + [author.id, committer.id] + } + + migration.bulk_update_commit_rows(mapping) + + commit = commits.first + + expect(commit.commit_author_id).to eq(author.id) + expect(commit.committer_id).to eq(committer.id) + end + end + + context 'when there are multiple commit rows to update' do + it 'updates all the rows' do + author = commit_users.create!(name: 'Bob', email: 'bob@example.com') + committer = + commit_users.create!(name: 'Alice', email: 'alice@example.com') + + commit1 = commits.create!( + merge_request_diff_id: diff.id, + relative_order: 0, + sha: Gitlab::Database::ShaAttribute.serialize('123abc') + ) + + commit2 = commits.create!( + merge_request_diff_id: diff.id, + relative_order: 1, + sha: Gitlab::Database::ShaAttribute.serialize('456abc') + ) + + mapping = { + [commit1.merge_request_diff_id, commit1.relative_order] => + [author.id, committer.id], + + [commit2.merge_request_diff_id, commit2.relative_order] => + [author.id, nil] + } + + migration.bulk_update_commit_rows(mapping) + + commit1 = commits.find_by(relative_order: 0) + commit2 = commits.find_by(relative_order: 1) + + expect(commit1.commit_author_id).to eq(author.id) + expect(commit1.committer_id).to eq(committer.id) + + expect(commit2.commit_author_id).to eq(author.id) + expect(commit2.committer_id).to be_nil + end + end + end + + describe '#primary_key' do + it 'returns the primary key for the commits table' do + key = migration.primary_key + + expect(key.to_sql).to eq('("merge_request_diff_commits"."merge_request_diff_id", "merge_request_diff_commits"."relative_order")') + end + end + + describe '#prepare' do + it 'trims a value to at most 512 characters' do + expect(migration.prepare('€' * 1_000)).to eq('€' * 512) + end + + it 'returns nil if the value is an empty string' do + expect(migration.prepare('')).to be_nil + end + end +end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 5fef1dbac0a..93e931579ed 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -198,6 +198,8 @@ merge_request_diff: - merge_request_diff_files merge_request_diff_commits: - merge_request_diff +- commit_author +- committer merge_request_diff_detail: - merge_request_diff merge_request_diff_files: diff --git a/spec/lib/gitlab/import_export/import_test_coverage_spec.rb b/spec/lib/gitlab/import_export/import_test_coverage_spec.rb index 7a9e7d8afba..9c6d2708607 100644 --- a/spec/lib/gitlab/import_export/import_test_coverage_spec.rb +++ b/spec/lib/gitlab/import_export/import_test_coverage_spec.rb @@ -109,14 +109,14 @@ RSpec.describe 'Test coverage of the Project Import' do def failure_message(not_tested_relations) <<~MSG - These relations seem to be added recenty and + These relations seem to be added recently and they expected to be covered in our Import specs: #{not_tested_relations}. To do that, expand one of the files listed in `project_json_fixtures` (or expand the list if you consider adding a new fixture file). After that, add a new spec into - `spec/lib/gitlab/import_export/project_tree_restorer_spec.rb` + `spec/lib/gitlab/import_export/project/tree_restorer_spec.rb` to check that the relation is being imported correctly. In case the spec breaks the master or there is a sense of urgency, diff --git a/spec/lib/gitlab/import_export/project/object_builder_spec.rb b/spec/lib/gitlab/import_export/project/object_builder_spec.rb index 20d882c82be..4c9f9f7c690 100644 --- a/spec/lib/gitlab/import_export/project/object_builder_spec.rb +++ b/spec/lib/gitlab/import_export/project/object_builder_spec.rb @@ -150,4 +150,30 @@ RSpec.describe Gitlab::ImportExport::Project::ObjectBuilder do expect(merge_request.persisted?).to be true end end + + context 'merge request diff commit users' do + it 'finds the existing user' do + user = MergeRequest::DiffCommitUser + .find_or_create('Alice', 'alice@example.com') + + found = described_class.build( + MergeRequest::DiffCommitUser, + 'name' => 'Alice', + 'email' => 'alice@example.com' + ) + + expect(found).to eq(user) + end + + it 'creates a new user' do + found = described_class.build( + MergeRequest::DiffCommitUser, + 'name' => 'Alice', + 'email' => 'alice@example.com' + ) + + expect(found.name).to eq('Alice') + expect(found.email).to eq('alice@example.com') + end + end end diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb index 1b5fba85020..82f465c4f9e 100644 --- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb @@ -224,6 +224,27 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do expect(MergeRequestDiffCommit.count).to eq(77) end + it 'assigns committer and author details to all diff commits' do + MergeRequestDiffCommit.all.each do |commit| + expect(commit.commit_author_id).not_to be_nil + expect(commit.committer_id).not_to be_nil + end + end + + it 'assigns the correct commit users to different diff commits' do + commit1 = MergeRequestDiffCommit + .find_by(sha: '0b4bc9a49b562e85de7cc9e834518ea6828729b9') + + commit2 = MergeRequestDiffCommit + .find_by(sha: 'a4e5dfebf42e34596526acb8611bc7ed80e4eb3f') + + expect(commit1.commit_author.name).to eq('Dmitriy Zaporozhets') + expect(commit1.commit_author.email).to eq('dmitriy.zaporozhets@gmail.com') + + expect(commit2.commit_author.name).to eq('James Lopez') + expect(commit2.commit_author.email).to eq('james@jameslopez.es') + end + it 'has the correct data for merge request latest_merge_request_diff' do MergeRequest.find_each do |merge_request| expect(merge_request.latest_merge_request_diff_id).to eq(merge_request.merge_request_diffs.maximum(:id)) diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 4b30d2d5af0..77d126e012e 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -235,6 +235,10 @@ MergeRequestDiffCommit: - committer_email - message - trailers +MergeRequest::DiffCommitUser: +- id +- name +- email MergeRequestDiffFile: - merge_request_diff_id - relative_order diff --git a/spec/migrations/20210604133651_schedule_merge_request_diff_users_background_migration_spec.rb b/spec/migrations/20210604133651_schedule_merge_request_diff_users_background_migration_spec.rb new file mode 100644 index 00000000000..923e97520d2 --- /dev/null +++ b/spec/migrations/20210604133651_schedule_merge_request_diff_users_background_migration_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! 'schedule_merge_request_diff_users_background_migration' + +RSpec.describe ScheduleMergeRequestDiffUsersBackgroundMigration, :migration do + let(:migration) { described_class.new } + + describe '#up' do + before do + allow(described_class::MergeRequestDiff) + .to receive(:minimum) + .with(:id) + .and_return(42) + + allow(described_class::MergeRequestDiff) + .to receive(:maximum) + .with(:id) + .and_return(85_123) + end + + it 'schedules the migrations in batches' do + expect(migration) + .to receive(:migrate_in) + .ordered + .with(2.minutes.to_i, described_class::MIGRATION_NAME, [42, 40_042]) + + expect(migration) + .to receive(:migrate_in) + .ordered + .with(4.minutes.to_i, described_class::MIGRATION_NAME, [40_042, 80_042]) + + expect(migration) + .to receive(:migrate_in) + .ordered + .with(6.minutes.to_i, described_class::MIGRATION_NAME, [80_042, 120_042]) + + migration.up + end + + it 'creates rows to track the background migration jobs' do + expect(Gitlab::Database::BackgroundMigrationJob) + .to receive(:create!) + .ordered + .with(class_name: described_class::MIGRATION_NAME, arguments: [42, 40_042]) + + expect(Gitlab::Database::BackgroundMigrationJob) + .to receive(:create!) + .ordered + .with(class_name: described_class::MIGRATION_NAME, arguments: [40_042, 80_042]) + + expect(Gitlab::Database::BackgroundMigrationJob) + .to receive(:create!) + .ordered + .with(class_name: described_class::MIGRATION_NAME, arguments: [80_042, 120_042]) + + migration.up + end + end +end diff --git a/spec/migrations/schedule_latest_pipeline_id_population_spec.rb b/spec/migrations/re_schedule_latest_pipeline_id_population_spec.rb similarity index 97% rename from spec/migrations/schedule_latest_pipeline_id_population_spec.rb rename to spec/migrations/re_schedule_latest_pipeline_id_population_spec.rb index 199bbdff917..354a0896ac9 100644 --- a/spec/migrations/schedule_latest_pipeline_id_population_spec.rb +++ b/spec/migrations/re_schedule_latest_pipeline_id_population_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' require_migration! -RSpec.describe ScheduleLatestPipelineIdPopulation do +RSpec.describe ReScheduleLatestPipelineIdPopulation do let(:namespaces) { table(:namespaces) } let(:pipelines) { table(:ci_pipelines) } let(:projects) { table(:projects) } diff --git a/spec/models/compare_spec.rb b/spec/models/compare_spec.rb index d395aa359e5..86bab569ab0 100644 --- a/spec/models/compare_spec.rb +++ b/spec/models/compare_spec.rb @@ -13,7 +13,15 @@ RSpec.describe Compare do let(:raw_compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, start_commit.id, head_commit.id) } - subject { described_class.new(raw_compare, project) } + subject(:compare) { described_class.new(raw_compare, project) } + + describe '#cache_key' do + subject { compare.cache_key } + + it { is_expected.to include(project) } + it { is_expected.to include(:compare) } + it { is_expected.to include(compare.diff_refs.hash) } + end describe '#start_commit' do it 'returns raw compare base commit' do diff --git a/spec/models/lfs_download_object_spec.rb b/spec/models/lfs_download_object_spec.rb index d1c323cd177..d82e432b7d6 100644 --- a/spec/models/lfs_download_object_spec.rb +++ b/spec/models/lfs_download_object_spec.rb @@ -6,8 +6,45 @@ RSpec.describe LfsDownloadObject do let(:oid) { 'cd293be6cea034bd45a0352775a219ef5dc7825ce55d1f7dae9762d80ce64411' } let(:link) { 'http://www.example.com' } let(:size) { 1 } + let(:headers) { { test: "asdf" } } - subject { described_class.new(oid: oid, size: size, link: link) } + subject { described_class.new(oid: oid, size: size, link: link, headers: headers) } + + describe '#headers' do + it 'returns specified Hash' do + expect(subject.headers).to eq(headers) + end + + context 'with nil headers' do + let(:headers) { nil } + + it 'returns a Hash' do + expect(subject.headers).to eq({}) + end + end + end + + describe '#has_authorization_header?' do + it 'returns false' do + expect(subject.has_authorization_header?).to be false + end + + context 'with uppercase form' do + let(:headers) { { 'Authorization' => 'Basic 12345' } } + + it 'returns true' do + expect(subject.has_authorization_header?).to be true + end + end + + context 'with lowercase form' do + let(:headers) { { 'authorization' => 'Basic 12345' } } + + it 'returns true' do + expect(subject.has_authorization_header?).to be true + end + end + end describe 'validations' do it { is_expected.to validate_numericality_of(:size).is_greater_than_or_equal_to(0) } @@ -66,5 +103,16 @@ RSpec.describe LfsDownloadObject do end end end + + context 'headers attribute' do + it 'only nil and Hash values are valid' do + aggregate_failures do + expect(described_class.new(oid: oid, size: size, link: 'http://www.example.com', headers: nil)).to be_valid + expect(described_class.new(oid: oid, size: size, link: 'http://www.example.com', headers: {})).to be_valid + expect(described_class.new(oid: oid, size: size, link: 'http://www.example.com', headers: { 'test' => 123 })).to be_valid + expect(described_class.new(oid: oid, size: size, link: 'http://www.example.com', headers: 'test')).to be_invalid + end + end + end end end diff --git a/spec/models/merge_request/diff_commit_user_spec.rb b/spec/models/merge_request/diff_commit_user_spec.rb new file mode 100644 index 00000000000..08e073568f9 --- /dev/null +++ b/spec/models/merge_request/diff_commit_user_spec.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe MergeRequest::DiffCommitUser do + describe 'validations' do + it 'requires that names are less than 512 characters long' do + expect(described_class.new(name: 'a' * 1000)).not_to be_valid + end + + it 'requires that Emails are less than 512 characters long' do + expect(described_class.new(email: 'a' * 1000)).not_to be_valid + end + + it 'requires either a name or Email' do + expect(described_class.new).not_to be_valid + end + + it 'allows setting of just a name' do + expect(described_class.new(name: 'Alice')).to be_valid + end + + it 'allows setting of just an Email' do + expect(described_class.new(email: 'alice@example.com')).to be_valid + end + + it 'allows setting of both a name and Email' do + expect(described_class.new(name: 'Alice', email: 'alice@example.com')) + .to be_valid + end + end + + describe '.prepare' do + it 'trims a value to at most 512 characters' do + expect(described_class.prepare('€' * 1_000)).to eq('€' * 512) + end + + it 'returns nil if the value is an empty string' do + expect(described_class.prepare('')).to be_nil + end + end + + describe '.find_or_create' do + it 'creates a new row if none exist' do + alice = described_class.find_or_create('Alice', 'alice@example.com') + + expect(alice.name).to eq('Alice') + expect(alice.email).to eq('alice@example.com') + end + + it 'returns an existing row if one exists' do + user1 = create(:merge_request_diff_commit_user) + user2 = described_class.find_or_create(user1.name, user1.email) + + expect(user1).to eq(user2) + end + + it 'handles concurrent inserts' do + user = create(:merge_request_diff_commit_user) + + expect(described_class) + .to receive(:find_or_create_by!) + .ordered + .with(name: user.name, email: user.email) + .and_raise(ActiveRecord::RecordNotUnique) + + expect(described_class) + .to receive(:find_or_create_by!) + .ordered + .with(name: user.name, email: user.email) + .and_return(user) + + expect(described_class.find_or_create(user.name, user.email)).to eq(user) + end + end + + describe '.bulk_find_or_create' do + it 'bulk creates missing rows and reuses existing rows' do + bob = create( + :merge_request_diff_commit_user, + name: 'Bob', + email: 'bob@example.com' + ) + + users = described_class.bulk_find_or_create( + [%w[Alice alice@example.com], %w[Bob bob@example.com]] + ) + alice = described_class.find_by(name: 'Alice') + + expect(users[%w[Alice alice@example.com]]).to eq(alice) + expect(users[%w[Bob bob@example.com]]).to eq(bob) + end + + it 'does not insert any data when all users exist' do + bob = create( + :merge_request_diff_commit_user, + name: 'Bob', + email: 'bob@example.com' + ) + + users = described_class.bulk_find_or_create([%w[Bob bob@example.com]]) + + expect(described_class).not_to receive(:insert_all) + expect(users[%w[Bob bob@example.com]]).to eq(bob) + end + + it 'handles concurrently inserted rows' do + bob = create( + :merge_request_diff_commit_user, + name: 'Bob', + email: 'bob@example.com' + ) + + input = [%w[Bob bob@example.com]] + + expect(described_class) + .to receive(:bulk_find) + .twice + .with(input) + .and_return([], [bob]) + + users = described_class.bulk_find_or_create(input) + + expect(users[%w[Bob bob@example.com]]).to eq(bob) + end + end +end diff --git a/spec/models/merge_request_diff_commit_spec.rb b/spec/models/merge_request_diff_commit_spec.rb index a24628b0f9d..6290468d4a7 100644 --- a/spec/models/merge_request_diff_commit_spec.rb +++ b/spec/models/merge_request_diff_commit_spec.rb @@ -16,6 +16,11 @@ RSpec.describe MergeRequestDiffCommit do let(:invalid_items_for_bulk_insertion) { [] } # class does not have any validations defined end + describe 'associations' do + it { is_expected.to belong_to(:commit_author) } + it { is_expected.to belong_to(:committer) } + end + describe '#to_hash' do subject { merge_request.commits.first } @@ -46,6 +51,8 @@ RSpec.describe MergeRequestDiffCommit do "committed_date": "2014-02-27T10:01:38.000+01:00".to_time, "committer_name": "Dmitriy Zaporozhets", "committer_email": "dmitriy.zaporozhets@gmail.com", + "commit_author_id": an_instance_of(Integer), + "committer_id": an_instance_of(Integer), "merge_request_diff_id": merge_request_diff_id, "relative_order": 0, "sha": Gitlab::Database::ShaAttribute.serialize("5937ac0a7beb003549fc5fd26fc247adbce4a52e"), @@ -59,6 +66,8 @@ RSpec.describe MergeRequestDiffCommit do "committed_date": "2014-02-27T09:57:31.000+01:00".to_time, "committer_name": "Dmitriy Zaporozhets", "committer_email": "dmitriy.zaporozhets@gmail.com", + "commit_author_id": an_instance_of(Integer), + "committer_id": an_instance_of(Integer), "merge_request_diff_id": merge_request_diff_id, "relative_order": 1, "sha": Gitlab::Database::ShaAttribute.serialize("570e7b2abdd848b95f2f578043fc23bd6f6fd24d"), @@ -76,6 +85,21 @@ RSpec.describe MergeRequestDiffCommit do subject end + it 'creates diff commit users' do + diff = create(:merge_request_diff, merge_request: merge_request) + + described_class.create_bulk(diff.id, [commits.first]) + + commit_row = MergeRequestDiffCommit + .find_by(merge_request_diff_id: diff.id, relative_order: 0) + + commit_user_row = + MergeRequest::DiffCommitUser.find_by(name: 'Dmitriy Zaporozhets') + + expect(commit_row.commit_author).to eq(commit_user_row) + expect(commit_row.committer).to eq(commit_user_row) + end + context 'with dates larger than the DB limit' do let(:commits) do # This commit's date is "Sun Aug 17 07:12:55 292278994 +0000" @@ -92,6 +116,8 @@ RSpec.describe MergeRequestDiffCommit do "committed_date": timestamp, "committer_name": "Alejandro Rodríguez", "committer_email": "alejorro70@gmail.com", + "commit_author_id": an_instance_of(Integer), + "committer_id": an_instance_of(Integer), "merge_request_diff_id": merge_request_diff_id, "relative_order": 0, "sha": Gitlab::Database::ShaAttribute.serialize("ba3343bc4fa403a8dfbfcab7fc1a8c29ee34bd69"), @@ -107,4 +133,28 @@ RSpec.describe MergeRequestDiffCommit do end end end + + describe '.prepare_commits_for_bulk_insert' do + it 'returns the commit hashes and unique user tuples' do + commit = double(:commit, to_hash: { + parent_ids: %w[foo bar], + author_name: 'a' * 1000, + author_email: 'a' * 1000, + committer_name: 'Alice', + committer_email: 'alice@example.com' + }) + + hashes, tuples = described_class.prepare_commits_for_bulk_insert([commit]) + + expect(hashes).to eq([{ + author_name: 'a' * 512, + author_email: 'a' * 512, + committer_name: 'Alice', + committer_email: 'alice@example.com' + }]) + + expect(tuples) + .to include(['a' * 512, 'a' * 512], %w[Alice alice@example.com]) + end + end end diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index d019e89e0b4..17cdb5166db 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -488,6 +488,17 @@ RSpec.describe API::Repositories do let(:current_user) { nil } end end + + context 'api_caching_repository_compare is disabled' do + before do + stub_feature_flags(api_caching_repository_compare: false) + end + + it_behaves_like 'repository compare' do + let(:project) { create(:project, :public, :repository) } + let(:current_user) { nil } + end + end end describe 'GET /projects/:id/repository/contributors' do diff --git a/spec/serializers/analytics/cycle_analytics/stage_entity_spec.rb b/spec/serializers/analytics/cycle_analytics/stage_entity_spec.rb index 90cc7f7827b..8b45e8a64fc 100644 --- a/spec/serializers/analytics/cycle_analytics/stage_entity_spec.rb +++ b/spec/serializers/analytics/cycle_analytics/stage_entity_spec.rb @@ -11,4 +11,12 @@ RSpec.describe Analytics::CycleAnalytics::StageEntity do expect(entity_json).to have_key(:start_event_html_description) expect(entity_json).to have_key(:end_event_html_description) end + + it 'exposes start_event and end_event objects' do + expect(entity_json[:start_event][:identifier]).to eq(entity_json[:start_event_identifier]) + expect(entity_json[:end_event][:identifier]).to eq(entity_json[:end_event_identifier]) + + expect(entity_json[:start_event][:html_description]).to eq(entity_json[:start_event_html_description]) + expect(entity_json[:end_event][:html_description]).to eq(entity_json[:end_event_html_description]) + end end diff --git a/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb index 66a450bd734..047ebe65dff 100644 --- a/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb +++ b/spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb @@ -6,6 +6,7 @@ RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do let(:lfs_endpoint) { "#{import_url}/info/lfs/objects/batch" } let!(:project) { create(:project, import_url: import_url) } let(:new_oids) { { 'oid1' => 123, 'oid2' => 125 } } + let(:headers) { { 'X-Some-Header' => '456' }} let(:remote_uri) { URI.parse(lfs_endpoint) } let(:request_object) { HTTParty::Request.new(Net::HTTP::Post, '/') } @@ -18,7 +19,7 @@ RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do { 'oid' => oid, 'size' => size, 'actions' => { - 'download' => { 'href' => "#{import_url}/gitlab-lfs/objects/#{oid}" } + 'download' => { 'href' => "#{import_url}/gitlab-lfs/objects/#{oid}", header: headers } } } end @@ -48,12 +49,20 @@ RSpec.describe Projects::LfsPointers::LfsDownloadLinkListService do end describe '#execute' do + let(:download_objects) { subject.execute(new_oids) } + it 'retrieves each download link of every non existent lfs object' do - subject.execute(new_oids).each do |lfs_download_object| + download_objects.each do |lfs_download_object| expect(lfs_download_object.link).to eq "#{import_url}/gitlab-lfs/objects/#{lfs_download_object.oid}" end end + it 'stores headers' do + download_objects.each do |lfs_download_object| + expect(lfs_download_object.headers).to eq(headers) + end + end + context 'when lfs objects size is larger than the batch size' do def stub_successful_request(batch) response = custom_response(success_net_response, objects_response(batch)) diff --git a/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb index cfe8e863223..8f6aab84664 100644 --- a/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb +++ b/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb @@ -155,13 +155,24 @@ RSpec.describe Projects::LfsPointers::LfsDownloadService do context 'when credentials present' do let(:download_link_with_credentials) { "http://user:password@gitlab.com/#{oid}" } let(:lfs_object) { LfsDownloadObject.new(oid: oid, size: size, link: download_link_with_credentials) } - - before do - stub_full_request(download_link).with(headers: { 'Authorization' => 'Basic dXNlcjpwYXNzd29yZA==' }).to_return(body: lfs_content) - end + let!(:request_stub) { stub_full_request(download_link).with(headers: { 'Authorization' => 'Basic dXNlcjpwYXNzd29yZA==' }).to_return(body: lfs_content) } it 'the request adds authorization headers' do - subject + subject.execute + + expect(request_stub).to have_been_requested + end + + context 'when Authorization header is present' do + let(:auth_header) { { 'Authorization' => 'Basic 12345' } } + let(:lfs_object) { LfsDownloadObject.new(oid: oid, size: size, link: download_link_with_credentials, headers: auth_header) } + let!(:request_stub) { stub_full_request(download_link).with(headers: auth_header).to_return(body: lfs_content) } + + it 'request uses the header auth' do + subject.execute + + expect(request_stub).to have_been_requested + end end end