Skip to content

Commit 8c587d7

Browse files
committed
Merge pull request git-for-windows#404: Make 'ort' the default merge strategy
The 'ort' strategy is a new algorithm to replace the 'recursive' merge strategy. I've been reviewing some of the performance patches upstream, many of which are already in Git 2.32.0 (more coming in 2.33.0) and even with the ones already included, it is a clear performance win for our large repos. I tested on the Office monorepo and consistently saw merge times in the 5-6 second range. With the 'recursive' strategy, these would range from 7-20 seconds. My tests reproduced merges found within the commit history, and the ones that succeeded without conflicts matched the committed changes. There were even a few where the 'recursive' strategy did not resolve to the committed change, but the 'ort' version did (probably because of better rename detection). Not only is this a beneficial performance change for our users across `microsoft/git`, it will be a critical step to allowing `git merge` to work quickly with sparse index. In my testing of a prototype, I was able to get `git merge` commands with sparse index and the 'ort' strategy down to 0.5-1.5 seconds in most cases. (Cases with a merge conflict outside of the sparse-checkout definition jumped back up to the 6-7 second range, which is expected, and should be rare.) cc: @newren for awareness. Thanks for the patches! These were applied from those sent to the list via git#1055.
2 parents 61a4750 + 68d5610 commit 8c587d7

9 files changed

+98
-72
lines changed

Documentation/git-rebase.txt

+15-14
Original file line numberDiff line numberDiff line change
@@ -340,9 +340,7 @@ See also INCOMPATIBLE OPTIONS below.
340340

341341
-m::
342342
--merge::
343-
Use merging strategies to rebase. When the recursive (default) merge
344-
strategy is used, this allows rebase to be aware of renames on the
345-
upstream side. This is the default.
343+
Using merging strategies to rebase (default).
346344
+
347345
Note that a rebase merge works by replaying each commit from the working
348346
branch on top of the <upstream> branch. Because of this, when a merge
@@ -354,9 +352,8 @@ See also INCOMPATIBLE OPTIONS below.
354352

355353
-s <strategy>::
356354
--strategy=<strategy>::
357-
Use the given merge strategy.
358-
If there is no `-s` option 'git merge-recursive' is used
359-
instead. This implies --merge.
355+
Use the given merge strategy, instead of the default `ort`.
356+
This implies `--merge`.
360357
+
361358
Because 'git rebase' replays each commit from the working branch
362359
on top of the <upstream> branch using the given strategy, using
@@ -369,7 +366,7 @@ See also INCOMPATIBLE OPTIONS below.
369366
--strategy-option=<strategy-option>::
370367
Pass the <strategy-option> through to the merge strategy.
371368
This implies `--merge` and, if no strategy has been
372-
specified, `-s recursive`. Note the reversal of 'ours' and
369+
specified, `-s ort`. Note the reversal of 'ours' and
373370
'theirs' as noted above for the `-m` option.
374371
+
375372
See also INCOMPATIBLE OPTIONS below.
@@ -530,7 +527,7 @@ The `--rebase-merges` mode is similar in spirit to the deprecated
530527
where commits can be reordered, inserted and dropped at will.
531528
+
532529
It is currently only possible to recreate the merge commits using the
533-
`recursive` merge strategy; Different merge strategies can be used only via
530+
`ort` merge strategy; different merge strategies can be used only via
534531
explicit `exec git merge -s <strategy> [...]` commands.
535532
+
536533
See also REBASING MERGES and INCOMPATIBLE OPTIONS below.
@@ -1219,12 +1216,16 @@ successful merge so that the user can edit the message.
12191216
If a `merge` command fails for any reason other than merge conflicts (i.e.
12201217
when the merge operation did not even start), it is rescheduled immediately.
12211218

1222-
At this time, the `merge` command will *always* use the `recursive`
1223-
merge strategy for regular merges, and `octopus` for octopus merges,
1224-
with no way to choose a different one. To work around
1225-
this, an `exec` command can be used to call `git merge` explicitly,
1226-
using the fact that the labels are worktree-local refs (the ref
1227-
`refs/rewritten/onto` would correspond to the label `onto`, for example).
1219+
By default, the `merge` command will use the `ort` merge strategy for
1220+
regular merges, and `octopus` for octopus merges. One can specify a
1221+
default strategy for all merges using the `--strategy` argument when
1222+
invoking rebase, or can override specific merges in the interactive
1223+
list of commands by using an `exec` command to call `git merge`
1224+
explicitly with a `--strategy` argument. Note that when calling `git
1225+
merge` explicitly like this, you can make use of the fact that the
1226+
labels are worktree-local refs (the ref `refs/rewritten/onto` would
1227+
correspond to the label `onto`, for example) in order to refer to the
1228+
branches you want to merge.
12281229

12291230
Note: the first command (`label onto`) labels the revision onto which
12301231
the commits are rebased; The name `onto` is just a convention, as a nod

Documentation/gitfaq.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ best to always use a regular merge commit.
275275

276276
[[merge-two-revert-one]]
277277
If I make a change on two branches but revert it on one, why does the merge of those branches include the change?::
278-
By default, when Git does a merge, it uses a strategy called the recursive
278+
By default, when Git does a merge, it uses a strategy called the `ort`
279279
strategy, which does a fancy three-way merge. In such a case, when Git
280280
performs the merge, it considers exactly three points: the two heads and a
281281
third point, called the _merge base_, which is usually the common ancestor of

Documentation/merge-options.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ With --squash, --commit is not allowed, and will fail.
112112
Use the given merge strategy; can be supplied more than
113113
once to specify them in the order they should be tried.
114114
If there is no `-s` option, a built-in list of strategies
115-
is used instead ('git merge-recursive' when merging a single
116-
head, 'git merge-octopus' otherwise).
115+
is used instead (`ort` when merging a single head,
116+
`octopus` otherwise).
117117

118118
-X <option>::
119119
--strategy-option=<option>::

Documentation/merge-strategies.txt

+58-41
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,23 @@ backend 'merge strategies' to be chosen with `-s` option. Some strategies
66
can also take their own options, which can be passed by giving `-X<option>`
77
arguments to `git merge` and/or `git pull`.
88

9-
resolve::
10-
This can only resolve two heads (i.e. the current branch
11-
and another branch you pulled from) using a 3-way merge
12-
algorithm. It tries to carefully detect criss-cross
13-
merge ambiguities and is considered generally safe and
14-
fast.
15-
16-
recursive::
17-
This can only resolve two heads using a 3-way merge
18-
algorithm. When there is more than one common
19-
ancestor that can be used for 3-way merge, it creates a
20-
merged tree of the common ancestors and uses that as
21-
the reference tree for the 3-way merge. This has been
22-
reported to result in fewer merge conflicts without
23-
causing mismerges by tests done on actual merge commits
24-
taken from Linux 2.6 kernel development history.
25-
Additionally this can detect and handle merges involving
26-
renames, but currently cannot make use of detected
27-
copies. This is the default merge strategy when pulling
28-
or merging one branch.
9+
ort::
10+
This is the default merge strategy when pulling or merging one
11+
branch. This strategy can only resolve two heads using a
12+
3-way merge algorithm. When there is more than one common
13+
ancestor that can be used for 3-way merge, it creates a merged
14+
tree of the common ancestors and uses that as the reference
15+
tree for the 3-way merge. This has been reported to result in
16+
fewer merge conflicts without causing mismerges by tests done
17+
on actual merge commits taken from Linux 2.6 kernel
18+
development history. Additionally this strategy can detect
19+
and handle merges involving renames. It does not make use of
20+
detected copies. The name for this algorithm is an acronym
21+
("Ostensibly Recursive's Twin") and came from the fact that it
22+
was written as a replacement for the previous default
23+
algorithm, `recursive`.
2924
+
30-
The 'recursive' strategy can take the following options:
25+
The 'ort' strategy can take the following options:
3126

3227
ours;;
3328
This option forces conflicting hunks to be auto-resolved cleanly by
@@ -43,19 +38,6 @@ theirs;;
4338
This is the opposite of 'ours'; note that, unlike 'ours', there is
4439
no 'theirs' merge strategy to confuse this merge option with.
4540

46-
patience;;
47-
With this option, 'merge-recursive' spends a little extra time
48-
to avoid mismerges that sometimes occur due to unimportant
49-
matching lines (e.g., braces from distinct functions). Use
50-
this when the branches to be merged have diverged wildly.
51-
See also linkgit:git-diff[1] `--patience`.
52-
53-
diff-algorithm=[patience|minimal|histogram|myers];;
54-
Tells 'merge-recursive' to use a different diff algorithm, which
55-
can help avoid mismerges that occur due to unimportant matching
56-
lines (such as braces from distinct functions). See also
57-
linkgit:git-diff[1] `--diff-algorithm`.
58-
5941
ignore-space-change;;
6042
ignore-all-space;;
6143
ignore-space-at-eol;;
@@ -84,11 +66,6 @@ no-renormalize;;
8466
Disables the `renormalize` option. This overrides the
8567
`merge.renormalize` configuration variable.
8668

87-
no-renames;;
88-
Turn off rename detection. This overrides the `merge.renames`
89-
configuration variable.
90-
See also linkgit:git-diff[1] `--no-renames`.
91-
9269
find-renames[=<n>];;
9370
Turn on rename detection, optionally setting the similarity
9471
threshold. This is the default. This overrides the
@@ -105,6 +82,46 @@ subtree[=<path>];;
10582
is prefixed (or stripped from the beginning) to make the shape of
10683
two trees to match.
10784

85+
recursive::
86+
This can only resolve two heads using a 3-way merge
87+
algorithm. When there is more than one common
88+
ancestor that can be used for 3-way merge, it creates a
89+
merged tree of the common ancestors and uses that as
90+
the reference tree for the 3-way merge. This has been
91+
reported to result in fewer merge conflicts without
92+
causing mismerges by tests done on actual merge commits
93+
taken from Linux 2.6 kernel development history.
94+
Additionally this can detect and handle merges involving
95+
renames. It does not make use of detected copies. This was
96+
the default strategy for resolving two heads from Git v0.99.9k
97+
until v2.33.0.
98+
+
99+
The 'recursive' strategy takes the same options as 'ort'. However,
100+
there are three additional options that 'ort' ignores (not documented
101+
above) that are potentially useful with the 'recursive' strategy:
102+
103+
patience;;
104+
Deprecated synonym for `diff-algorithm=patience`.
105+
106+
diff-algorithm=[patience|minimal|histogram|myers];;
107+
Use a different diff algorithm while merging, which can help
108+
avoid mismerges that occur due to unimportant matching lines
109+
(such as braces from distinct functions). See also
110+
linkgit:git-diff[1] `--diff-algorithm`. Note that `ort`
111+
specifically uses `diff-algorithm=histogram`, while `recursive`
112+
defaults to the `diff.algorithm` config setting.
113+
114+
no-renames;;
115+
Turn off rename detection. This overrides the `merge.renames`
116+
configuration variable.
117+
See also linkgit:git-diff[1] `--no-renames`.
118+
119+
resolve::
120+
This can only resolve two heads (i.e. the current branch
121+
and another branch you pulled from) using a 3-way merge
122+
algorithm. It tries to carefully detect criss-cross
123+
merge ambiguities. It does not handle renames.
124+
108125
octopus::
109126
This resolves cases with more than two heads, but refuses to do
110127
a complex merge that needs manual resolution. It is
@@ -121,13 +138,13 @@ ours::
121138
the 'recursive' merge strategy.
122139

123140
subtree::
124-
This is a modified recursive strategy. When merging trees A and
141+
This is a modified ort strategy. When merging trees A and
125142
B, if B corresponds to a subtree of A, B is first adjusted to
126143
match the tree structure of A, instead of reading the trees at
127144
the same level. This adjustment is also done to the common
128145
ancestor tree.
129146

130-
With the strategies that use 3-way merge (including the default, 'recursive'),
147+
With the strategies that use 3-way merge (including the default, 'ort'),
131148
if a change is made on both branches, but later reverted on one of the
132149
branches, that change will be present in the merged result; some people find
133150
this behavior confusing. It occurs because only the heads and the merge base

Documentation/technical/directory-rename-detection.txt

+8-6
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ Directory rename detection
22
==========================
33

44
Rename detection logic in diffcore-rename that checks for renames of
5-
individual files is aggregated and analyzed in merge-recursive for cases
6-
where combinations of renames indicate that a full directory has been
7-
renamed.
5+
individual files is also aggregated there and then analyzed in either
6+
merge-ort or merge-recursive for cases where combinations of renames
7+
indicate that a full directory has been renamed.
88

99
Scope of abilities
1010
------------------
@@ -88,9 +88,11 @@ directory rename detection support in:
8888
Folks have requested in the past that `git diff` detect directory
8989
renames and somehow simplify its output. It is not clear whether this
9090
would be desirable or how the output should be simplified, so this was
91-
simply not implemented. Further, to implement this, directory rename
92-
detection logic would need to move from merge-recursive to
93-
diffcore-rename.
91+
simply not implemented. Also, while diffcore-rename has most of the
92+
logic for detecting directory renames, some of the logic is still found
93+
within merge-ort and merge-recursive. Fully supporting directory
94+
rename detection in diffs would require copying or moving the remaining
95+
bits of logic to the diff machinery.
9496

9597
* am
9698

Documentation/user-manual.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -3190,7 +3190,7 @@ that *updated* thing--the old state that you added originally ends up
31903190
not being pointed to by any commit or tree, so it's now a dangling blob
31913191
object.
31923192

3193-
Similarly, when the "recursive" merge strategy runs, and finds that
3193+
Similarly, when the "ort" merge strategy runs, and finds that
31943194
there are criss-cross merges and thus more than one merge base (which is
31953195
fairly unusual, but it does happen), it will generate one temporary
31963196
midway tree (or possibly even more, if you had lots of criss-crossing

builtin/merge.c

+9-3
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ static int autostash;
8888
static int no_verify;
8989

9090
static struct strategy all_strategy[] = {
91-
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
91+
{ "recursive", NO_TRIVIAL },
9292
{ "octopus", DEFAULT_OCTOPUS },
93-
{ "ort", NO_TRIVIAL },
93+
{ "ort", DEFAULT_TWOHEAD | NO_TRIVIAL },
9494
{ "resolve", 0 },
9595
{ "ours", NO_FAST_FORWARD | NO_TRIVIAL },
9696
{ "subtree", NO_FAST_FORWARD | NO_TRIVIAL },
@@ -739,7 +739,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
739739

740740
for (x = 0; x < xopts_nr; x++)
741741
if (parse_merge_opt(&o, xopts[x]))
742-
die(_("Unknown option for merge-recursive: -X%s"), xopts[x]);
742+
die(_("unknown strategy option: -X%s"), xopts[x]);
743743

744744
o.branch1 = head_arg;
745745
o.branch2 = merge_remote_util(remoteheads->item)->name;
@@ -1485,6 +1485,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
14851485
fast_forward = FF_NO;
14861486
}
14871487

1488+
if (!use_strategies && !pull_twohead &&
1489+
remoteheads && !remoteheads->next) {
1490+
char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM");
1491+
if (default_strategy)
1492+
append_strategy(get_strategy(default_strategy));
1493+
}
14881494
if (!use_strategies) {
14891495
if (!remoteheads)
14901496
; /* already up-to-date */

builtin/rebase.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1713,7 +1713,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
17131713
int i;
17141714

17151715
if (!options.strategy)
1716-
options.strategy = "recursive";
1716+
options.strategy = "ort";
17171717

17181718
strbuf_reset(&buf);
17191719
for (i = 0; i < strategy_options.nr; i++)

sequencer.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,7 @@ static int do_recursive_merge(struct repository *r,
636636
for (i = 0; i < opts->xopts_nr; i++)
637637
parse_merge_opt(&o, opts->xopts[i]);
638638

639-
if (opts->strategy && !strcmp(opts->strategy, "ort")) {
639+
if (!opts->strategy || !strcmp(opts->strategy, "ort")) {
640640
memset(&result, 0, sizeof(result));
641641
merge_incore_nonrecursive(&o, base_tree, head_tree, next_tree,
642642
&result);
@@ -2065,7 +2065,7 @@ static int do_pick_commit(struct repository *r,
20652065
/*
20662066
* We do not intend to commit immediately. We just want to
20672067
* merge the differences in, so let's compute the tree
2068-
* that represents the "current" state for merge-recursive
2068+
* that represents the "current" state for the merge machinery
20692069
* to work on.
20702070
*/
20712071
if (write_index_as_tree(&head, r->index, r->index_file, 0, NULL))
@@ -3988,7 +3988,7 @@ static int do_merge(struct repository *r,
39883988
o.branch2 = ref_name.buf;
39893989
o.buffer_output = 2;
39903990

3991-
if (opts->strategy && !strcmp(opts->strategy, "ort")) {
3991+
if (!opts->strategy || !strcmp(opts->strategy, "ort")) {
39923992
/*
39933993
* TODO: Should use merge_incore_recursive() and
39943994
* merge_switch_to_result(), skipping the call to

0 commit comments

Comments
 (0)