Make orphaned commit checks configurable
cover Make orphaned commit checks configurable
Hi there - my name's Wyatt Holden and I'm a Software Engineer at Microsoft :) I've been working with Derrick Stolee over the past few weeks on optimizing git in the Office mono-repo. We found some issues around slow checkouts on mono-repos in an automated environment:
When leaving a detached HEAD, git checkout and git switch
call orphaned_commit_warning() to warn about commits that are not reachable from any existing ref or the destination HEAD. This requires enumerating refs and walking commit history, which can be expensive in large repositories.
In an internal mono-repo, Trace2 showed tree unpacking finishing approximately 12 seconds into checkout, but the process continuing for roughly another 193 seconds. A stack dump captured the main thread inside orphaned_commit_warning(), enumerating loose refs and opening ref files. Running checkout with --quiet, which bypasses this routine, reduced the observed runtime to approximately 14 seconds. However, --quiet also suppresses unrelated output.
This three-commit series adds a targeted opt-out, advertises it when the check is slow, and enables it in Scalar:
1. checkout: make orphaned commit checks configurable Add checkout.orphanedCommitWarning , enabled by default, for both checkout and switch. Setting it to false skips the reachability check while retaining the previous HEAD description and normal checkout output. Add a Trace2 region to expose the check’s duration and tests verifying that disabling the setting bypasses the routine.
2. checkout: advise when orphaned commit checks are slow When the check takes more than two seconds, suggest the opt-out and explain that disabling it also removes the warning about commits left behind. Control this advice independently through advice.checkoutOrphanedCommitWarning. Test advice and suppression without relying on wall-clock delays.
3. scalar: disable orphaned commit checks by default Add checkout.orphanedCommitWarning=false to Scalar’s recommended configuration using its existing set-if-missing mechanism, preserving explicit user settings during clone, register, and reconfigure. Document the tradeoff and reflog recovery; rely on existing Scalar tests for configuration-preservation behavior.
Ordinary Git retains its existing default behavior. Users who disable the check lose the immediate warning about commits left behind; those commits can still be recovered through the HEAD reflog while the relevant entries and objects remain available.
Wyatt Holden (3):
checkout: make orphaned commit checks configurable
checkout: advise when orphaned commit checks are slow
scalar: disable orphaned commit checks by default
Documentation/config/advice.adoc | 6 +++ Documentation/config/checkout.adoc | 11 +++++ Documentation/scalar.adoc | 7 +++ advice.c | 1 + advice.h | 1 + builtin/checkout.c | 31 +++++++++++++- scalar.c | 1 + t/README | 5 +++ t/t2020-checkout-detach.sh | 69 ++++++++++++++++++++++++++++++ 9 files changed, 130 insertions(+), 2 deletions(-)
base-commit: 6de20f6092dcf9bdb1c8efe03db4b70c82b423dd
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2264%2Fwyatt-holden%2Fwyattholden%2Fcheckout-orphaned-commit-warning-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2264/wyatt-holden/wyattholden/checkout-orphaned-commit-warning-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2264
Signature · 1 line
gitgitgadget
PATCH 1/3 checkout: make orphaned commit checks configurable
From: Wyatt Holden <[email protected]>
When leaving a detached HEAD, checkout enumerates refs and walks history to warn about commits left behind. This work can dominate checkout time even after the working tree has been updated.
In an automated checkout of an internal monorepo, unpack_trees ended about 12 seconds into a roughly 204-second run. Stack dumps captured the main thread in orphaned_commit_warning(), enumerating loose refs and opening ref files. Using --quiet reduced the observed checkout time to about 14 seconds, but also suppressed unrelated output. These observations motivate an opt-out; they do not establish how much of the delay came from enumeration versus history traversal.
Add checkout.orphanedCommitWarning, enabled by default, so checkout and switch can skip the check while retaining normal checkout output. Automated callers can opt out when they do not need a warning about leaving detached commits behind.
Follow the configuration precedent of 06b324c1d71 (status: add status.aheadbehind setting, 2019-06-18). Unlike status, introduce no dedicated command-line option; callers can use git -c for a one-off override.
Add a Trace2 region around the check to make its cost visible in future investigations and to verify that disabling the setting skips the routine rather than merely hiding its warning. Document the setting and test both checkout and switch.
--- Documentation/config/checkout.adoc | 11 +++++++++++ builtin/checkout.c | 18 ++++++++++++++++-- t/t2020-checkout-detach.sh | 29 +++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-)
Documentation/config/checkout.adoc +11 -0
@@ -22,6 +22,17 @@ commands or functionality in the future. option in `git checkout` and `git switch`. See linkgit:git-switch[1] and linkgit:git-checkout[1]. +`checkout.orphanedCommitWarning`:: + When leaving a detached `HEAD`, linkgit:git-checkout[1] and + linkgit:git-switch[1] check whether any commits are being left + behind that are not reachable from any ref or the new `HEAD`, + and warn if so. This check can be expensive in large repositories. + Set this variable to `false` to skip the check and its warning + without suppressing other checkout output. Defaults to `true`. ++ +When disabled, commits left behind can still be found in the `HEAD` +reflog until its entries expire. See linkgit:git-reflog[1]. + `checkout.workers`:: The number of parallel workers to use when updating the working tree. The default is one, i.e. sequential execution. If set to a value less
builtin/checkout.c +16 -2
@@ -51,6 +51,7 @@ struct checkout_opts {int patch_interhunk_context;int auto_advance;int quiet; +int orphaned_commit_warning;int merge;int force;int force_detach;
@@ -99,6 +100,7 @@ struct checkout_opts { .patch_context = -1, \ .patch_interhunk_context = -1, \ .auto_advance = 1, \ + .orphaned_commit_warning = 1, \ }#define MERGE_WORKING_TREE_UNPACK_FAILED (-2)
@@ -1132,6 +1134,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *nestruct rev_info revs;struct object *object = &old_commit->object; +trace2_region_enter ("checkout" ,"orphaned_commit_warning" , the_repository);repo_init_revisions (the_repository, &revs,NULL );setup_revisions (0 ,NULL , &revs,NULL );
@@ -1155,6 +1158,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne/* Clean up objects used, as they will be reused. */ repo_clear_commit_marks (the_repository,ALL_REV_FLAGS );release_revisions (&revs); +trace2_region_leave ("checkout" ,"orphaned_commit_warning" , the_repository); } static int switch_branches(const struct checkout_opts *opts,
@@ -1253,8 +1257,14 @@ static int switch_branches(const struct checkout_opts *opts, } } -if (!opts->quiet && !old_branch_info.path && old_branch_info.commit && new_branch_info->commit != old_branch_info.commit) -orphaned_commit_warning (old_branch_info.commit, new_branch_info->commit); +if (!opts->quiet && !old_branch_info.path && old_branch_info.commit && + new_branch_info->commit != old_branch_info.commit) { +if (opts->orphaned_commit_warning) +orphaned_commit_warning (old_branch_info.commit, new_branch_info->commit); +else +describe_detached_head (_ ("Previous HEAD position was" ), + old_branch_info.commit); + }if (autostash_res ==STASH_APPLY_CONFLICT && !opts->quiet)fputc ('\n' , stderr);
@@ -1295,6 +1305,10 @@ static int git_checkout_config(const char *var, const char *value, opts->dwim_new_local_branch =git_config_bool (var, value);return 0 ; } +if (!strcmp (var,"checkout.orphanedcommitwarning" )) { + opts->orphaned_commit_warning =git_config_bool (var, value); +return 0 ; + }if (starts_with (var,"submodule." ))return git_default_submodule_config (var, value,NULL );
t/t2020-checkout-detach.sh +29 -0
@@ -164,6 +164,35 @@ test_expect_success 'checkout does not warn leaving reachable commit' ' check_no_orphan_warning stderr ' +for command in checkout switch +do + test_expect_success "$command honors checkout.orphanedCommitWarning" ' + git checkout --detach "$orphan2" && + GIT_TRACE2_EVENT="$(pwd)/trace-enabled-$command" \ + git -c checkout.orphanedCommitWarning=true \ + $command main 2>stderr && + check_orphan_warning stderr "2 commits" && + test_grep "\"label\":\"orphaned_commit_warning\"" \ + trace-enabled-$command && + + git checkout --detach "$orphan2" && + GIT_TRACE2_EVENT="$(pwd)/trace-disabled-$command" \ + git -c checkout.orphanedCommitWarning=false \ + $command main 2>stderr && + check_no_orphan_warning stderr && + test_grep "Switched to branch" stderr && + test_grep ! "\"label\":\"orphaned_commit_warning\"" \ + trace-disabled-$command && + check_not_detached + ' + + test_expect_success "$command rejects invalid checkout.orphanedCommitWarning" ' + test_must_fail git -c checkout.orphanedCommitWarning=invalid \ + $command main 2>stderr && + test_grep "bad boolean config value.*checkout.orphanedcommitwarning" stderr + ' +done + cat >expect <<'EOF' Your branch is behind 'main' by 1 commit, and can be fast-forwarded. (use "git pull" to update your local branch)
Signature · 1 line
gitgitgadget
PATCH 2/3 checkout: advise when orphaned commit checks are slow
In reply to Wyatt Holden via GitGitGadgetFrom: Wyatt Holden <[email protected]>
The orphaned-commit check can take much longer than updating the working tree. Users who encounter this cost need a way to discover the checkout.orphanedCommitWarning setting.
Measure the complete check and suggest disabling it when it takes more than two seconds. Explain that opting out also removes the warning about commits left behind, and allow the advice itself to be disabled with advice.checkoutOrphanedCommitWarning.
Follow the slow-operation advice introduced by:
0a53561a622 (status: warn when a/b calculation takes too long, 2019-06-18)
Unlike that status advice, recommend a configuration setting rather than a dedicated command-line option.
Keep a test hook to exercise advice and suppression without relying on wall-clock delays. Force the advice condition without replacing the measured duration, and check the formatted duration rather than a fabricated fixed value.
--- Documentation/config/advice.adoc | 6 +++++ advice.c | 1 + advice.h | 1 + builtin/checkout.c | 13 +++++++++++ t/README | 5 ++++ t/t2020-checkout-detach.sh | 40 ++++++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+)
Documentation/config/advice.adoc +6 -0
@@ -38,6 +38,12 @@ all advice messages. configuration variable for how to set a given remote to be used by default in some situations where this advice would be printed. + checkoutOrphanedCommitWarning:: + Shown when linkgit:git-checkout[1] or linkgit:git-switch[1] + takes more than 2 seconds to check for commits left behind + when leaving a detached `HEAD`. Suggests setting + `checkout.orphanedCommitWarning` to `false` to skip the check. + Disabling this advice does not disable the check itself. commitBeforeMerge:: Shown when linkgit:git-merge[1] refuses to merge to avoid overwriting local changes.
advice.c +1 -0
@@ -50,6 +50,7 @@ static struct { [ADVICE_AMBIGUOUS_FETCH_REFSPEC] = { "ambiguousFetchRefspec" }, [ADVICE_AM_WORK_DIR] = { "amWorkDir" }, [ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] = { "checkoutAmbiguousRemoteBranchName" }, + [ADVICE_CHECKOUT_ORPHANED_COMMIT_WARNING] = { "checkoutOrphanedCommitWarning" }, [ADVICE_COMMIT_BEFORE_MERGE] = { "commitBeforeMerge" }, [ADVICE_DEFAULT_BRANCH_NAME] = { "defaultBranchName" }, [ADVICE_DETACHED_HEAD] = { "detachedHead" },
advice.h +1 -0
@@ -17,6 +17,7 @@ enum advice_type {ADVICE_AMBIGUOUS_FETCH_REFSPEC ,ADVICE_AM_WORK_DIR ,ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME , +ADVICE_CHECKOUT_ORPHANED_COMMIT_WARNING ,ADVICE_COMMIT_BEFORE_MERGE ,ADVICE_DEFAULT_BRANCH_NAME ,/* To be retired sometime after Git 3.0 */ ADVICE_DETACHED_HEAD,
builtin/checkout.c +13 -0
@@ -35,6 +35,7 @@#include "strvec.h" #include "submodule.h" #include "symlinks.h" +#include "trace.h" #include "trace2.h" #include "tree.h" #include "tree-walk.h"
@@ -1133,6 +1134,8 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne {struct rev_info revs;struct object *object = &old_commit->object; +uint64_t t_begin =getnanotime (); +uint64_t t_delta_in_ms;trace2_region_enter ("checkout" ,"orphaned_commit_warning" , the_repository);repo_init_revisions (the_repository, &revs,NULL );
@@ -1159,6 +1162,16 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *nerepo_clear_commit_marks (the_repository,ALL_REV_FLAGS );release_revisions (&revs);trace2_region_leave ("checkout" ,"orphaned_commit_warning" , the_repository); + + t_delta_in_ms = (getnanotime () - t_begin) /1000000 ; +if (t_delta_in_ms >2000 || +git_env_bool ("GIT_TEST_CHECKOUT_DELAY_WARNING" ,0 )) +advise_if_enabled (ADVICE_CHECKOUT_ORPHANED_COMMIT_WARNING , +_ ("Checking for orphaned commits took %.2f seconds.\n" +"You can use 'git config set checkout.orphanedCommitWarning false'\n" +"to skip this check, but you will no longer be warned when leaving\n" +"commits behind that are not connected to any of your branches." ), + t_delta_in_ms /1000.0 ); } static int switch_branches(const struct checkout_opts *opts,
t/README +5 -0
@@ -371,6 +371,11 @@ test suite. Accept any boolean values that are accepted by git-config. GIT_TEST_PROTOCOL_VERSION=<n>, when set, makes 'protocol.version' default to n. +GIT_TEST_CHECKOUT_DELAY_WARNING=<boolean>, when true, forces the +slow orphaned-commit check advice to be considered regardless of the +elapsed time. It does not change the measured duration or override +advice settings. + GIT_TEST_FULL_IN_PACK_ARRAY=<boolean> exercises the uncommon pack-objects code path where there are more than 1024 packs even if the actual number of packs in repository is below this limit. Accept
t/t2020-checkout-detach.sh +40 -0
@@ -186,6 +186,46 @@ do check_not_detached ' + test_expect_success "$command advises when the orphan check is slow" ' + git checkout --detach "$orphan2" && + GIT_TEST_CHECKOUT_DELAY_WARNING=true \ + git $command main 2>stderr && + check_orphan_warning stderr "2 commits" && + test_grep -E "^hint: Checking for orphaned commits took [0-9]+[.][0-9]{2} seconds[.]$" stderr && + test_grep "git config set checkout.orphanedCommitWarning false" stderr && + test_grep "git config set advice.checkoutOrphanedCommitWarning false" stderr + ' + + test_expect_success "$command can suppress slow orphan check advice" ' + git checkout --detach "$orphan2" && + GIT_TEST_CHECKOUT_DELAY_WARNING=true \ + git -c advice.checkoutOrphanedCommitWarning=false \ + $command main 2>stderr && + check_orphan_warning stderr "2 commits" && + test_grep ! "^hint:" stderr && + + git checkout --detach "$orphan2" && + GIT_TEST_CHECKOUT_DELAY_WARNING=true GIT_ADVICE=0 \ + git $command main 2>stderr && + check_orphan_warning stderr "2 commits" && + test_grep ! "^hint:" stderr + ' + + test_expect_success "$command does not advise when the orphan check is skipped" ' + git checkout --detach "$orphan2" && + GIT_TEST_CHECKOUT_DELAY_WARNING=true \ + git -c checkout.orphanedCommitWarning=false \ + $command main 2>stderr && + check_no_orphan_warning stderr && + test_grep ! "^hint:" stderr && + + git checkout --detach "$orphan2" && + GIT_TEST_CHECKOUT_DELAY_WARNING=true \ + git $command --quiet main 2>stderr && + test_must_be_empty stderr && + check_not_detached + ' + test_expect_success "$command rejects invalid checkout.orphanedCommitWarning" ' test_must_fail git -c checkout.orphanedCommitWarning=invalid \ $command main 2>stderr &&
Signature · 1 line
gitgitgadget
PATCH 3/3 scalar: disable orphaned commit checks by default
In reply to Wyatt Holden via GitGitGadgetFrom: Wyatt Holden <[email protected]>
Checking for commits left behind when leaving a detached HEAD can be expensive in large repositories. Add checkout.orphanedCommitWarning=false to Scalar's recommended settings to avoid this foreground work while preserving normal checkout output.
Use the existing set-if-missing behavior so explicit user choices are preserved during clone, register, and reconfigure. Document the loss of the immediate orphan warning and the availability of reflog recovery. Existing Scalar tests cover configuration preservation, so no additional inventory of recommended settings is needed.
--- Documentation/scalar.adoc | 7 +++++++ scalar.c | 1 + 2 files changed, 8 insertions(+)
Documentation/scalar.adoc +7 -0
@@ -214,6 +214,13 @@ am.keepCR=true:: and non-Windows platforms and keeping carriage return (`\r`) characters in certain workflows. +checkout.orphanedCommitWarning=false:: + This disables the reachability check that warns about commits left + behind when `git checkout` or `git switch` leaves a detached `HEAD`. + The check can be expensive in large repositories. Other checkout + output is unchanged, and commits left behind can still be found + in the `HEAD` reflog until its entries expire. + commitGraph.changedPaths=true:: This setting helps the background maintenance steps that compute the serialized commit-graph to also store changed-path Bloom filters. This
scalar.c +1 -0
@@ -138,6 +138,7 @@ static int set_recommended_config(int reconfigure)*/ struct scalar_config config[] = { {"am.keepCR" ,"true" }, + {"checkout.orphanedCommitWarning" ,"false" }, {"commitGraph.changedPaths" ,"true" }, {"commitGraph.generationVersion" ,"1" }, {"core.autoCRLF" ,"false" },
Signature · 1 line
gitgitgadget
Re: [PATCH 2/3] checkout: advise when orphaned commit checks are slow
In reply to Wyatt Holden via GitGitGadgetadvice.c static struct { · line 54
Quoted 3 lines · Wyatt Holden via GitGitGadget
advice.c +1
-0[ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] = { "checkoutAmbiguousRemoteBranchName" }, + [ADVICE_CHECKOUT_ORPHANED_COMMIT_WARNING] = { "checkoutOrphanedCommitWarning" }, [ADVICE_COMMIT_BEFORE_MERGE] = { "commitBeforeMerge" },
It looks like we have a slight whitespace alignment problem here.
Thanks,
-Stolee
Quoted 4 lines · Wyatt Holden via GitGitGadget
Hi there - my name's Wyatt Holden and I'm a Software Engineer at Microsoft :) I've been working with Derrick Stolee over the past few weeks on optimizing git in the Office mono-repo. We found some issues around slow checkouts on mono-repos in an automated environment:
Welcome, Wyatt! (I was prepared for your series.)
Quoted 6 lines
When leaving a detached HEAD, git checkout and git switch
call orphaned_commit_warning() to warn about commits that are not reachable from any existing ref or the destination HEAD. This requires enumerating refs and walking commit history, which can be expensive in large repositories.
Quoted 7 lines
In an internal mono-repo, Trace2 showed tree unpacking finishing approximately 12 seconds into checkout, but the process continuing for roughly another 193 seconds. A stack dump captured the main thread inside
orphaned_commit_warning(), enumerating loose refs and opening ref files. Running checkout with --quiet, which bypasses this routine, reduced the observed runtime to approximately 14 seconds. However, --quiet also suppresses unrelated output.
This example shows a significant pain caused by this check, but we also want to be able to see progress indicators over time in our logs, so using --quiet isn't preferred, long term.
Quoted 2 lines
This three-commit series adds a targeted opt-out, advertises it when the check is slow, and enables it in Scalar:
I did some pre-review with Wyatt (but found one nitpick in my on-list re-review) so I'm biased. Further bias: I recommended that he follow the model of the status.aheadBehind config [1] and advice.statusAheadBehind
[2] patches as prior art.
And also I added status.aheadBehind to Scalar's recommended config in its very first version [3]. Wyatt has updated the Scalar config docs that justify the recommendation, which was introduced only recently [4].
So I give my support publicly for Wyatt's updates.
Thanks,
-Stolee
Re: [PATCH 1/3] checkout: make orphaned commit checks configurable
In reply to Wyatt Holden via GitGitGadgetQuoted 5 lines · Wyatt Holden via GitGitGadget
From: Wyatt Holden <[email protected]>
When leaving a detached HEAD, checkout enumerates refs and walks history to warn about commits left behind. This work can dominate checkout time even after the working tree has been updated.
Interesting.
I often am on a detached HEAD but never realized this particular check was expensive. The size of changes between the HEAD and the next branch would affect the time it needs to update the working tree (the larger the difference, the more expensive to complete the branch switching). What factors are involved in making this "orphaned commits" check more expensive? The total number of branches? The length of commit chain we are leaving behind? I am trying to see if this is something we can aim to optimize or if it is a lost cause and a cop-out to introduce an option to disable the check altogether is the best we can do.
Quoted 7 lines
In an automated checkout of an internal monorepo,
unpack_treesended about 12 seconds into a roughly 204-second run. Stack dumps captured the main thread inorphaned_commit_warning(), enumerating loose refs and opening ref files. Using --quiet reduced the observed checkout time to about 14 seconds, but also suppressed unrelated output. These observations motivate an opt-out; they do not establish how much of the delay came from enumeration versus history traversal.
So, in short, the answer to my earlier question is "we don't know".
Quoted 4 lines
Add checkout.orphanedCommitWarning, enabled by default, so checkout and switch can skip the check while retaining normal checkout output. Automated callers can opt out when they do not need a warning about leaving detached commits behind.
Sounds sensible.
Quoted 4 lines
Follow the configuration precedent of
06b324c1d71(status: add status.aheadbehind setting, 2019-06-18). Unlike status, introduce no dedicated command-line option; callers can use git -c for a one-off override.
I am not sure how justifiable the design decision to leave the command line option out is.
The automation can run with "-q" to omit the overhead altogether, as well as squelching any other output the automation would not read anyway, so I would not worry too much about them.
But for human users, what are they losing by passing "-q" when they do not want to pay the cost of this check because we are not giving them an easy to use command line option? What other output are they declining by "--quiet"? "The following paths have local changes" list? Anything else?
Quoted 4 lines
Add a Trace2 region around the check to make its cost visible in future investigations and to verify that disabling the setting skips the routine rather than merely hiding its warning. Document the setting and test both checkout and switch.
Sounds sensible.
Quoted 9 lines
builtin/checkout.c +1
-0@@ -1132,6 +1134,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *nestruct rev_info revs;struct object *object = &old_commit->object; +trace2_region_enter ("checkout" ,"orphaned_commit_warning" , the_repository);repo_init_revisions (the_repository, &revs,NULL );setup_revisions (0 ,NULL , &revs,NULL );
builtin/checkout.c static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne · line 1162
Quoted 5 lines
builtin/checkout.c +1
-0@@ -1155,6 +1158,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne/* Clean up objects used, as they will be reused. */ repo_clear_commit_marks (the_repository,ALL_REV_FLAGS );release_revisions (&revs); +trace2_region_leave ("checkout" ,"orphaned_commit_warning" , the_repository); }
Quite straightforward. There is no early return in the middle, so entering at the beginning with leaving at the end is the only change necessary.
Quoted 4 lines
builtin/checkout.c +0
-0@@ -1253,8 +1257,14 @@ static int switch_branches(const struct checkout_opts *opts, } }
builtin/checkout.c static int switch_branches(const struct checkout_opts *opts, · line 1267
Quoted 10 lines
builtin/checkout.c +8
-2-if (!opts->quiet && !old_branch_info.path && old_branch_info.commit && new_branch_info->commit != old_branch_info.commit) -orphaned_commit_warning (old_branch_info.commit, new_branch_info->commit); +if (!opts->quiet && !old_branch_info.path && old_branch_info.commit && + new_branch_info->commit != old_branch_info.commit) { +if (opts->orphaned_commit_warning) +orphaned_commit_warning (old_branch_info.commit, new_branch_info->commit); +else +describe_detached_head (_ ("Previous HEAD position was" ), + old_branch_info.commit); + }
OK. The call into suggest_reattach() via orphaned_commit_warning() is the more expensive part, and it also is where "Previous HEAD was" comes, which we do not want to lose. It makes sense.
Another thing I noticed is that the loop in suggest_reattach() stops showing each commit after showing ORPHAN_CUTOFF but keeps counting. I wonder if it becomes palatable to introduce a configuration to stop it early, instead of disabling the orphaned_commit_warning() altogether?
Re: [PATCH 2/3] checkout: advise when orphaned commit checks are slow
In reply to Derrick StoleeQuoted 10 lines · Derrick Stolee
advice.c static struct { · line 54
Quoted 3 lines · Wyatt Holden via GitGitGadget
advice.c +1
-0[ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] = { "checkoutAmbiguousRemoteBranchName" }, + [ADVICE_CHECKOUT_ORPHANED_COMMIT_WARNING] = { "checkoutOrphanedCommitWarning" }, [ADVICE_COMMIT_BEFORE_MERGE] = { "commitBeforeMerge" },It looks like we have a slight whitespace alignment problem here.
Thanks,
-Stolee
Good eyes. What the patch aims does sound sensible to me, though.
Quoted 5 lines · Wyatt Holden via GitGitGadget
When leaving a detached HEAD, git checkout and git switch
call orphaned_commit_warning() to warn about commits that are not reachable from any existing ref or the destination HEAD. This requires enumerating refs and walking commit history, which can be expensive in large repositories.
I found some design choices made in 1/3 questionable but overall the series is coherently done and very easy to understand _why_ it wants to do what it does. Nice work.