Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
6fca7f0a
Commit
6fca7f0a
authored
Feb 21, 2017
by
Rémy Coutable
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'squash-default' into 'master'
Squash default See merge request !1272
parents
d4437eee
f728dd24
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
138 additions
and
58 deletions
+138
-58
app/services/merge_requests/squash_service.rb
app/services/merge_requests/squash_service.rb
+63
-4
app/services/merge_requests/working_copy_base_service.rb
app/services/merge_requests/working_copy_base_service.rb
+1
-1
doc/user/project/merge_requests/squash_and_merge.md
doc/user/project/merge_requests/squash_and_merge.md
+2
-2
spec/features/merge_requests/squash_spec.rb
spec/features/merge_requests/squash_spec.rb
+2
-3
spec/services/merge_requests/squash_service_spec.rb
spec/services/merge_requests/squash_service_spec.rb
+68
-47
spec/support/test_env.rb
spec/support/test_env.rb
+2
-1
No files found.
app/services/merge_requests/squash_service.rb
View file @
6fca7f0a
...
@@ -23,13 +23,15 @@ module MergeRequests
...
@@ -23,13 +23,15 @@ module MergeRequests
end
end
run_git_command
(
run_git_command
(
%W(worktree add
#{
tree_path
}
#{
merge_request
.
target_branch
}
--detach
)
,
%W(worktree add
--no-checkout --detach
#{
tree_path
}
)
,
repository
.
path_to_repo
,
repository
.
path_to_repo
,
git_env
,
git_env
,
'add worktree for squash'
'add worktree for squash'
)
)
diff
=
git_command
(
%W(diff --binary
#{
merge_request
.
diff_start_sha
}
...
#{
merge_request
.
diff_head_sha
}
)
)
configure_sparse_checkout
diff
=
git_command
(
%W(diff --binary
#{
diff_range
}
)
)
apply
=
git_command
(
%w(apply --index)
)
apply
=
git_command
(
%w(apply --index)
)
run_command
(
run_command
(
...
@@ -40,9 +42,12 @@ module MergeRequests
...
@@ -40,9 +42,12 @@ module MergeRequests
)
)
run_git_command
(
run_git_command
(
%W(commit -
C
#{
merge_request
.
diff_head_sha
}
)
,
%W(commit -
-no-verify -m
#{
merge_request
.
title
}
)
,
tree_path
,
tree_path
,
git_env
.
merge
(
'GIT_COMMITTER_NAME'
=>
current_user
.
name
,
'GIT_COMMITTER_EMAIL'
=>
current_user
.
email
),
git_env
.
merge
(
'GIT_COMMITTER_NAME'
=>
current_user
.
name
,
'GIT_COMMITTER_EMAIL'
=>
current_user
.
email
,
'GIT_AUTHOR_NAME'
=>
merge_request
.
author
.
name
,
'GIT_AUTHOR_EMAIL'
=>
merge_request
.
author
.
email
),
'commit squashed changes'
'commit squashed changes'
)
)
...
@@ -62,10 +67,64 @@ module MergeRequests
...
@@ -62,10 +67,64 @@ module MergeRequests
false
false
ensure
ensure
clean_dir
clean_dir
clean_worktree
end
end
def
tree_path
def
tree_path
@tree_path
||=
merge_request
.
squash_dir_path
@tree_path
||=
merge_request
.
squash_dir_path
end
end
def
diff_range
@diff_range
||=
"
#{
merge_request
.
diff_start_sha
}
...
#{
merge_request
.
diff_head_sha
}
"
end
def
worktree_path
@worktree_path
||=
File
.
join
(
repository
.
path_to_repo
,
'worktrees'
,
merge_request
.
id
.
to_s
)
end
def
clean_worktree
FileUtils
.
rm_rf
(
worktree_path
)
if
File
.
exist?
(
worktree_path
)
end
# Adding a worktree means checking out the repository. For large repos, this
# can be very expensive, so set up sparse checkout for the worktree to only
# check out the files we're interested in.
#
def
configure_sparse_checkout
run_git_command
(
%w(config core.sparseCheckout true)
,
repository
.
path_to_repo
,
git_env
,
'configure sparse checkout'
)
# Get the same diff we'll apply, excluding added files. (We can't check
# out files on the target branch if they don't exist yet!)
#
diff_files
=
run_git_command
(
%W(diff --name-only --diff-filter=a --binary
#{
diff_range
}
)
,
repository
.
path_to_repo
,
git_env
,
'get files in diff'
)
# If only new files are introduced by this MR, then our sparse checkout
# doesn't need to have any files at all.
#
unless
diff_files
.
empty?
worktree_info
=
File
.
join
(
worktree_path
,
'info'
)
FileUtils
.
mkdir_p
(
worktree_info
)
unless
File
.
directory?
(
worktree_info
)
File
.
write
(
File
.
join
(
worktree_info
,
'sparse-checkout'
),
diff_files
)
end
run_git_command
(
%W(checkout --detach
#{
merge_request
.
target_branch
}
)
,
tree_path
,
git_env
,
'check out target branch'
)
end
end
end
end
end
app/services/merge_requests/working_copy_base_service.rb
View file @
6fca7f0a
...
@@ -41,7 +41,7 @@ module MergeRequests
...
@@ -41,7 +41,7 @@ module MergeRequests
end
end
def
log_error
(
message
)
def
log_error
(
message
)
Gitlab
::
GitLogger
.
error
(
message
)
Gitlab
::
GitLogger
.
error
(
"
#{
self
.
class
.
name
}
error (
#{
merge_request
.
to_reference
(
full:
true
)
}
):
#{
message
}
"
)
end
end
def
clean_dir
def
clean_dir
...
...
doc/user/project/merge_requests/squash_and_merge.md
View file @
6fca7f0a
...
@@ -35,8 +35,8 @@ This can then be overridden at the time of accepting the merge request:
...
@@ -35,8 +35,8 @@ This can then be overridden at the time of accepting the merge request:
The squashed commit has the following metadata:
The squashed commit has the following metadata:
*
Message: t
aken from the last commit in the source branch
.
*
Message: t
he title of the merge request
.
*
Author: t
aken from the last commit in the source branch
.
*
Author: t
he author of the merge request
.
*
Committer: the user who initiated the squash.
*
Committer: the user who initiated the squash.
## Squashing and [fast-forward merge][ff-merge]
## Squashing and [fast-forward merge][ff-merge]
...
...
spec/features/merge_requests/squash_spec.rb
View file @
6fca7f0a
...
@@ -12,11 +12,10 @@ feature 'Squashing merge requests', js: true, feature: true do
...
@@ -12,11 +12,10 @@ feature 'Squashing merge requests', js: true, feature: true do
shared_examples
'squash'
do
shared_examples
'squash'
do
it
'squashes the commits into a single commit, and adds a merge commit'
do
it
'squashes the commits into a single commit, and adds a merge commit'
do
latest_master_commits
=
project
.
repository
.
commits_between
(
original_head
.
sha
,
'master'
).
map
(
&
:raw
)
latest_master_commits
=
project
.
repository
.
commits_between
(
original_head
.
sha
,
'master'
).
map
(
&
:raw
)
last_mr_commit
=
project
.
repository
.
commit
(
source_branch
)
squash_commit
=
an_object_having_attributes
(
sha:
a_string_matching
(
/\h{40}/
),
squash_commit
=
an_object_having_attributes
(
sha:
a_string_matching
(
/\h{40}/
),
message:
"
#{
last_mr_commit
.
message
}
\n
"
,
message:
"
Csv
\n
"
,
author_name:
last_mr_commit
.
author_
name
,
author_name:
user
.
name
,
committer_name:
user
.
name
)
committer_name:
user
.
name
)
merge_commit
=
an_object_having_attributes
(
sha:
a_string_matching
(
/\h{40}/
),
merge_commit
=
an_object_having_attributes
(
sha:
a_string_matching
(
/\h{40}/
),
...
...
spec/services/merge_requests/squash_service_spec.rb
View file @
6fca7f0a
...
@@ -5,83 +5,102 @@ describe MergeRequests::SquashService do
...
@@ -5,83 +5,102 @@ describe MergeRequests::SquashService do
let
(
:user
)
{
project
.
owner
}
let
(
:user
)
{
project
.
owner
}
let
(
:project
)
{
create
(
:project
)
}
let
(
:project
)
{
create
(
:project
)
}
let
(
:merge_request
)
do
let
(
:merge_request_with_one_commit
)
do
create
(
:merge_request
,
source_branch:
'feature.custom-highlighting'
,
source_project:
project
,
target_branch:
'master'
,
target_project:
project
)
end
let
(
:merge_request_with_only_new_files
)
do
create
(
:merge_request
,
create
(
:merge_request
,
source_branch:
'video'
,
source_project:
project
,
source_branch:
'video'
,
source_project:
project
,
target_branch:
'master'
,
target_project:
project
)
target_branch:
'master'
,
target_project:
project
)
end
end
let
(
:merge_request_with_
one_commit
)
do
let
(
:merge_request_with_
large_files
)
do
create
(
:merge_request
,
create
(
:merge_request
,
source_branch:
'
feature.custom-highlighting
'
,
source_project:
project
,
source_branch:
'
squash-large-files
'
,
source_project:
project
,
target_branch:
'master'
,
target_project:
project
)
target_branch:
'master'
,
target_project:
project
)
end
end
describe
'#execute'
do
shared_examples
'the squash succeeds'
do
context
'when there is only one commit in the merge request'
do
it
'returns the squashed commit SHA'
do
it
'returns that commit SHA'
do
result
=
service
.
execute
(
merge_request
)
result
=
service
.
execute
(
merge_request_with_one_commit
)
expect
(
result
).
to
match
(
status: :success
,
squash_sha:
merge_request_with_one_commit
.
diff_head_sha
)
expect
(
result
).
to
match
(
status: :success
,
squash_sha:
a_string_matching
(
/\h{40}/
))
end
expect
(
result
[
:squash_sha
]).
not_to
eq
(
merge_request
.
diff_head_sha
)
end
it
'does not perform any git actions
'
do
it
'cleans up the temporary directory
'
do
expect
(
service
).
not_to
receive
(
:run_git_command
)
expect
(
service
).
to
receive
(
:clean_dir
).
and_call_original
service
.
execute
(
merge_request_with_one_commit
)
service
.
execute
(
merge_request
)
end
end
end
context
'when the squash succeeds
'
do
it
'does not keep the branch push event
'
do
it
'returns the squashed commit SHA'
do
expect
{
service
.
execute
(
merge_request
)
}.
not_to
change
{
Event
.
count
}
result
=
service
.
execute
(
merge_request
)
end
expect
(
result
).
to
match
(
status: :success
,
squash_sha:
a_string_matching
(
/\h{40}/
))
context
'the squashed commit'
do
expect
(
result
[
:squash_sha
]).
not_to
eq
(
merge_request
.
diff_head_sha
)
let
(
:squash_sha
)
{
service
.
execute
(
merge_request
)[
:squash_sha
]
}
end
let
(
:squash_commit
)
{
project
.
repository
.
commit
(
squash_sha
)
}
it
'cleans up the temporary directory'
do
it
'copies the author info and message from the merge request'
do
expect
(
service
).
to
receive
(
:clean_dir
).
and_call_original
expect
(
squash_commit
.
author_name
).
to
eq
(
merge_request
.
author
.
name
)
expect
(
squash_commit
.
author_email
).
to
eq
(
merge_request
.
author
.
email
)
service
.
execute
(
merge_request
)
# Commit messages have a trailing newline, but titles don't.
expect
(
squash_commit
.
message
.
chomp
).
to
eq
(
merge_request
.
title
)
end
end
it
'does not keep the branch push event'
do
it
'sets the current user as the committer'
do
expect
{
service
.
execute
(
merge_request
)
}.
not_to
change
{
Event
.
count
}
expect
(
squash_commit
.
committer_name
).
to
eq
(
user
.
name
.
chomp
(
'.'
))
expect
(
squash_commit
.
committer_email
).
to
eq
(
user
.
email
)
end
end
context
'the squashed commit'
do
it
'has the same diff as the merge request, but a different SHA'
do
let
(
:squash_sha
)
{
service
.
execute
(
merge_request
)[
:squash_sha
]
}
rugged
=
project
.
repository
.
rugged
let
(
:squash_commit
)
{
project
.
repository
.
commit
(
squash_sha
)
}
mr_diff
=
rugged
.
diff
(
merge_request
.
diff_base_sha
,
merge_request
.
diff_head_sha
)
squash_diff
=
rugged
.
diff
(
merge_request
.
diff_start_sha
,
squash_sha
)
it
'copies the author info and message from the last commit in the source branch'
do
diff_head_commit
=
merge_request
.
diff_head_commit
expect
(
squash_commit
.
author_name
).
to
eq
(
diff_head_commit
.
author_name
)
expect
(
squash_diff
.
patch
.
length
).
to
eq
(
mr_diff
.
patch
.
length
)
expect
(
squash_commit
.
author_email
).
to
eq
(
diff_head_commit
.
author_email
)
expect
(
squash_commit
.
sha
).
not_to
eq
(
merge_request
.
diff_head_sha
)
end
end
end
# The commit message on the 'real' commit doesn't have a trailing newline
describe
'#execute'
do
expect
(
squash_commit
.
message
.
chomp
).
to
eq
(
diff_head_commit
.
message
)
context
'when there is only one commit in the merge request'
do
end
it
'returns that commit SHA'
do
result
=
service
.
execute
(
merge_request_with_one_commit
)
it
'sets the current user as the committer'
do
expect
(
result
).
to
match
(
status: :success
,
squash_sha:
merge_request_with_one_commit
.
diff_head_sha
)
expect
(
squash_commit
.
committer_name
).
to
eq
(
user
.
name
.
chomp
(
'.'
))
end
expect
(
squash_commit
.
committer_email
).
to
eq
(
user
.
email
)
end
it
'has the same diff as the merge request, but a different SHA'
do
it
'does not perform any git actions'
do
rugged
=
project
.
repository
.
rugged
expect
(
service
).
not_to
receive
(
:run_git_command
)
mr_diff
=
rugged
.
diff
(
merge_request
.
diff_base_sha
,
merge_request
.
diff_head_sha
)
squash_diff
=
rugged
.
diff
(
merge_request
.
diff_start_sha
,
squash_sha
)
expect
(
squash_diff
.
patch
).
to
eq
(
mr_diff
.
patch
)
service
.
execute
(
merge_request_with_one_commit
)
expect
(
squash_commit
.
sha
).
not_to
eq
(
merge_request
.
diff_head_sha
)
end
end
end
end
end
context
'when squashing only new files'
do
let
(
:merge_request
)
{
merge_request_with_only_new_files
}
include_examples
'the squash succeeds'
end
context
'when squashing with files too large to display'
do
let
(
:merge_request
)
{
merge_request_with_large_files
}
include_examples
'the squash succeeds'
end
stages
=
{
stages
=
{
'add worktree for squash'
=>
'worktree'
,
'add worktree for squash'
=>
'worktree'
,
'configure sparse checkout'
=>
'config'
,
'get files in diff'
=>
'diff --name-only'
,
'check out target branch'
=>
'checkout'
,
'apply patch'
=>
'diff --binary'
,
'apply patch'
=>
'diff --binary'
,
'commit squashed changes'
=>
'commit'
,
'commit squashed changes'
=>
'commit'
,
'get SHA of squashed commit'
=>
'rev-parse'
'get SHA of squashed commit'
=>
'rev-parse'
...
@@ -89,13 +108,14 @@ describe MergeRequests::SquashService do
...
@@ -89,13 +108,14 @@ describe MergeRequests::SquashService do
stages
.
each
do
|
stage
,
command
|
stages
.
each
do
|
stage
,
command
|
context
"when the
#{
stage
}
stage fails"
do
context
"when the
#{
stage
}
stage fails"
do
let
(
:merge_request
)
{
merge_request_with_only_new_files
}
let
(
:error
)
{
'A test error'
}
let
(
:error
)
{
'A test error'
}
before
do
before
do
git_command
=
a_collection_containing_exactly
(
git_command
=
a_collection_containing_exactly
(
a_string_starting_with
(
"
#{
Gitlab
.
config
.
git
.
bin_path
}
#{
command
}
"
)
a_string_starting_with
(
"
#{
Gitlab
.
config
.
git
.
bin_path
}
#{
command
}
"
)
).
or
(
).
or
(
a_collection_starting_with
([
Gitlab
.
config
.
git
.
bin_path
,
command
]
)
a_collection_starting_with
([
Gitlab
.
config
.
git
.
bin_path
]
+
command
.
split
)
)
)
allow
(
service
).
to
receive
(
:popen
).
and_return
([
''
,
0
])
allow
(
service
).
to
receive
(
:popen
).
and_return
([
''
,
0
])
...
@@ -123,6 +143,7 @@ describe MergeRequests::SquashService do
...
@@ -123,6 +143,7 @@ describe MergeRequests::SquashService do
end
end
context
'when any other exception is thrown'
do
context
'when any other exception is thrown'
do
let
(
:merge_request
)
{
merge_request_with_only_new_files
}
let
(
:error
)
{
'A test error'
}
let
(
:error
)
{
'A test error'
}
before
do
before
do
...
...
spec/support/test_env.rb
View file @
6fca7f0a
...
@@ -38,7 +38,8 @@ module TestEnv
...
@@ -38,7 +38,8 @@ module TestEnv
'conflict-too-large'
=>
'39fa04f'
,
'conflict-too-large'
=>
'39fa04f'
,
'deleted-image-test'
=>
'6c17798'
,
'deleted-image-test'
=>
'6c17798'
,
'wip'
=>
'b9238ee'
,
'wip'
=>
'b9238ee'
,
'csv'
=>
'3dd0896'
'csv'
=>
'3dd0896'
,
'squash-large-files'
=>
'54cec52'
}
}
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment