Skip to content

Commit 6026f68

Browse files
committed
Merge branch 'sh/pull-rebase-preserve'
"git pull --rebase" always flattened the history; pull.rebase can now be set to "preserve" to invoke "rebase --preserve-merges". * sh/pull-rebase-preserve: pull: allow pull to preserve merges when rebasing
2 parents 2de0f39 + 66713ef commit 6026f68

File tree

4 files changed

+135
-11
lines changed

4 files changed

+135
-11
lines changed

Documentation/config.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,10 @@ branch.<name>.rebase::
765765
instead of merging the default branch from the default remote when
766766
"git pull" is run. See "pull.rebase" for doing this in a non
767767
branch-specific manner.
768+
+
769+
When preserve, also pass `--preserve-merges` along to 'git rebase'
770+
so that locally committed merge commits will not be flattened
771+
by running 'git pull'.
768772
+
769773
*NOTE*: this is a possibly dangerous operation; do *not* use
770774
it unless you understand the implications (see linkgit:git-rebase[1]
@@ -1878,6 +1882,10 @@ pull.rebase::
18781882
of merging the default branch from the default remote when "git
18791883
pull" is run. See "branch.<name>.rebase" for setting this on a
18801884
per-branch basis.
1885+
+
1886+
When preserve, also pass `--preserve-merges` along to 'git rebase'
1887+
so that locally committed merge commits will not be flattened
1888+
by running 'git pull'.
18811889
+
18821890
*NOTE*: this is a possibly dangerous operation; do *not* use
18831891
it unless you understand the implications (see linkgit:git-rebase[1]

Documentation/git-pull.txt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,18 @@ include::merge-options.txt[]
102102
:git-pull: 1
103103

104104
-r::
105-
--rebase::
106-
Rebase the current branch on top of the upstream branch after
107-
fetching. If there is a remote-tracking branch corresponding to
108-
the upstream branch and the upstream branch was rebased since last
109-
fetched, the rebase uses that information to avoid rebasing
110-
non-local changes.
105+
--rebase[=false|true|preserve]::
106+
When true, rebase the current branch on top of the upstream
107+
branch after fetching. If there is a remote-tracking branch
108+
corresponding to the upstream branch and the upstream branch
109+
was rebased since last fetched, the rebase uses that information
110+
to avoid rebasing non-local changes.
111+
+
112+
When preserve, also rebase the current branch on top of the upstream
113+
branch, but pass `--preserve-merges` along to `git rebase` so that
114+
locally created merge commits will not be flattened.
115+
+
116+
When false, merge the current branch into the upstream branch.
111117
+
112118
See `pull.rebase`, `branch.<name>.rebase` and `branch.autosetuprebase` in
113119
linkgit:git-config[1] if you want to make `git pull` always use

git-pull.sh

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55
# Fetch one or more remote refs and merge it/them into the current HEAD.
66

7-
USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...'
7+
USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [--[no-]rebase|--rebase=preserve] [-s strategy]... [<fetch-options>] <repo> <head>...'
88
LONG_USAGE='Fetch one or more remote refs and integrate it/them with the current HEAD.'
99
SUBDIRECTORY_OK=Yes
1010
OPTIONS_SPEC=
@@ -38,15 +38,19 @@ Please, commit your changes before you can merge.")"
3838
test -z "$(git ls-files -u)" || die_conflict
3939
test -f "$GIT_DIR/MERGE_HEAD" && die_merge
4040

41+
bool_or_string_config () {
42+
git config --bool "$1" 2>/dev/null || git config "$1"
43+
}
44+
4145
strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
4246
log_arg= verbosity= progress= recurse_submodules= verify_signatures=
43-
merge_args= edit=
47+
merge_args= edit= rebase_args=
4448
curr_branch=$(git symbolic-ref -q HEAD)
4549
curr_branch_short="${curr_branch#refs/heads/}"
46-
rebase=$(git config --bool branch.$curr_branch_short.rebase)
50+
rebase=$(bool_or_string_config branch.$curr_branch_short.rebase)
4751
if test -z "$rebase"
4852
then
49-
rebase=$(git config --bool pull.rebase)
53+
rebase=$(bool_or_string_config pull.rebase)
5054
fi
5155
dry_run=
5256
while :
@@ -110,6 +114,9 @@ do
110114
esac
111115
merge_args="$merge_args$xx "
112116
;;
117+
-r=*|--r=*|--re=*|--reb=*|--reba=*|--rebas=*|--rebase=*)
118+
rebase="${1#*=}"
119+
;;
113120
-r|--r|--re|--reb|--reba|--rebas|--rebase)
114121
rebase=true
115122
;;
@@ -145,6 +152,20 @@ do
145152
shift
146153
done
147154

155+
case "$rebase" in
156+
preserve)
157+
rebase=true
158+
rebase_args=--preserve-merges
159+
;;
160+
true|false|'')
161+
;;
162+
*)
163+
echo "Invalid value for --rebase, should be true, false, or preserve"
164+
usage
165+
exit 1
166+
;;
167+
esac
168+
148169
error_on_no_merge_candidates () {
149170
exec >&2
150171
for opt
@@ -292,7 +313,7 @@ fi
292313
merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
293314
case "$rebase" in
294315
true)
295-
eval="git-rebase $diffstat $strategy_args $merge_args $verbosity"
316+
eval="git-rebase $diffstat $strategy_args $merge_args $rebase_args $verbosity"
296317
eval="$eval --onto $merge_head ${oldremoteref:-$merge_head}"
297318
;;
298319
*)

t/t5520-pull.sh

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,95 @@ test_expect_success 'branch.to-rebase.rebase should override pull.rebase' '
148148
test new = $(git show HEAD:file2)
149149
'
150150

151+
# add a feature branch, keep-merge, that is merged into master, so the
152+
# test can try preserving the merge commit (or not) with various
153+
# --rebase flags/pull.rebase settings.
154+
test_expect_success 'preserve merge setup' '
155+
git reset --hard before-rebase &&
156+
git checkout -b keep-merge second^ &&
157+
test_commit file3 &&
158+
git checkout to-rebase &&
159+
git merge keep-merge &&
160+
git tag before-preserve-rebase
161+
'
162+
163+
test_expect_success 'pull.rebase=false create a new merge commit' '
164+
git reset --hard before-preserve-rebase &&
165+
test_config pull.rebase false &&
166+
git pull . copy &&
167+
test $(git rev-parse HEAD^1) = $(git rev-parse before-preserve-rebase) &&
168+
test $(git rev-parse HEAD^2) = $(git rev-parse copy) &&
169+
test file3 = $(git show HEAD:file3.t)
170+
'
171+
172+
test_expect_success 'pull.rebase=true flattens keep-merge' '
173+
git reset --hard before-preserve-rebase &&
174+
test_config pull.rebase true &&
175+
git pull . copy &&
176+
test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
177+
test file3 = $(git show HEAD:file3.t)
178+
'
179+
180+
test_expect_success 'pull.rebase=1 is treated as true and flattens keep-merge' '
181+
git reset --hard before-preserve-rebase &&
182+
test_config pull.rebase 1 &&
183+
git pull . copy &&
184+
test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
185+
test file3 = $(git show HEAD:file3.t)
186+
'
187+
188+
test_expect_success 'pull.rebase=preserve rebases and merges keep-merge' '
189+
git reset --hard before-preserve-rebase &&
190+
test_config pull.rebase preserve &&
191+
git pull . copy &&
192+
test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
193+
test $(git rev-parse HEAD^2) = $(git rev-parse keep-merge)
194+
'
195+
196+
test_expect_success 'pull.rebase=invalid fails' '
197+
git reset --hard before-preserve-rebase &&
198+
test_config pull.rebase invalid &&
199+
! git pull . copy
200+
'
201+
202+
test_expect_success '--rebase=false create a new merge commit' '
203+
git reset --hard before-preserve-rebase &&
204+
test_config pull.rebase true &&
205+
git pull --rebase=false . copy &&
206+
test $(git rev-parse HEAD^1) = $(git rev-parse before-preserve-rebase) &&
207+
test $(git rev-parse HEAD^2) = $(git rev-parse copy) &&
208+
test file3 = $(git show HEAD:file3.t)
209+
'
210+
211+
test_expect_success '--rebase=true rebases and flattens keep-merge' '
212+
git reset --hard before-preserve-rebase &&
213+
test_config pull.rebase preserve &&
214+
git pull --rebase=true . copy &&
215+
test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
216+
test file3 = $(git show HEAD:file3.t)
217+
'
218+
219+
test_expect_success '--rebase=preserve rebases and merges keep-merge' '
220+
git reset --hard before-preserve-rebase &&
221+
test_config pull.rebase true &&
222+
git pull --rebase=preserve . copy &&
223+
test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
224+
test $(git rev-parse HEAD^2) = $(git rev-parse keep-merge)
225+
'
226+
227+
test_expect_success '--rebase=invalid fails' '
228+
git reset --hard before-preserve-rebase &&
229+
! git pull --rebase=invalid . copy
230+
'
231+
232+
test_expect_success '--rebase overrides pull.rebase=preserve and flattens keep-merge' '
233+
git reset --hard before-preserve-rebase &&
234+
test_config pull.rebase preserve &&
235+
git pull --rebase . copy &&
236+
test $(git rev-parse HEAD^^) = $(git rev-parse copy) &&
237+
test file3 = $(git show HEAD:file3.t)
238+
'
239+
151240
test_expect_success '--rebase with rebased upstream' '
152241
153242
git remote add -f me . &&

0 commit comments

Comments
 (0)