Skip to content

Commit 7cd9f06

Browse files
Ben PeartBen Peart
authored andcommitted
PR 138189: Speed up "git checkout -b foo" by avoiding the call to merge_working_tree
Speed up "git checkout -b foo" by avoiding the call to merge_working_tree when certain conditions are met. Basically you can only checkout a new branch with the current commit as this path does not need the merge to occur. Any other options force it through the old code path. With this optimization "git checkout -b test" takes 16 vs 165 seconds (cold HDD) and 3 seconds vs 44 seconds (warm HDD) on the ost2 repo. Note, all git tests still pass with this change but I'm not sure if they would take this upstream as it still changes the behavior.
1 parent 4f8576a commit 7cd9f06

File tree

2 files changed

+22
-4
lines changed

2 files changed

+22
-4
lines changed

builtin/checkout.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "resolve-undo.h"
2121
#include "submodule-config.h"
2222
#include "submodule.h"
23+
#include "gvfs.h"
2324

2425
static const char * const checkout_usage[] = {
2526
N_("git checkout [<options>] <branch>"),
@@ -824,10 +825,26 @@ static int switch_branches(const struct checkout_opts *opts,
824825
parse_commit_or_die(new->commit);
825826
}
826827

827-
ret = merge_working_tree(opts, &old, new, &writeout_error);
828-
if (ret) {
829-
free(path_to_free);
830-
return ret;
828+
/*
829+
* Optimize the performance of "git checkout foo" by skipping the call
830+
* to merge_working_tree. Make this as restrictive as possible, only
831+
* checkout a new branch with the current commit. Any other options force
832+
* it through the old path.
833+
*/
834+
if (!gvfs_config_is_set(GVFS_SKIP_MERGE_IN_CHECKOUT)
835+
|| !old.commit || !new->commit
836+
|| oidcmp(&old.commit->object.oid, &new->commit->object.oid)
837+
|| !opts->new_branch || opts->new_branch_force || opts->new_orphan_branch
838+
|| opts->patch_mode || opts->merge || opts->force || opts->force_detach
839+
|| opts->writeout_stage || !opts->overwrite_ignore
840+
|| opts->ignore_skipworktree || opts->ignore_other_worktrees
841+
|| opts->new_branch_log || opts->branch_exists || opts->prefix
842+
|| opts->source_tree) {
843+
ret = merge_working_tree(opts, &old, new, &writeout_error);
844+
if (ret) {
845+
free(path_to_free);
846+
return ret;
847+
}
831848
}
832849

833850
if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)

gvfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#define GVFS_FETCH_SKIP_REACHABILITY_AND_UPLOADPACK 16
2121
#define GVFS_LOWER_DEFAULT_SLOP 32
2222
#define GVFS_BLOCK_FILTERS_AND_EOL_CONVERSIONS 64
23+
#define GVFS_SKIP_MERGE_IN_CHECKOUT 128
2324

2425
static inline BOOL gvfs_config_is_set(int mask) {
2526
return (core_gvfs & mask) == mask;

0 commit comments

Comments
 (0)