Hi,

On Mon, 12 Nov 2018, Johannes Schindelin via GitGitGadget wrote:

> In our tests with large repositories, we noticed a serious regression of the
> performance of git rebase when using the built-in vs the shell script
> version. It boils down to an incorrect conversion of a git checkout -q:
> instead of using a twoway_merge as git checkout does, we used a oneway_merge 
> as git reset does. The latter, however, calls lstat() on all files listed in
> the index, while the former essentially looks only at the files that are
> different between the given two revisions.
> 
> Let's reinstate the original behavior by introducing a flag to the 
> reset_head() function to indicate whether we want to emulate reset --hard 
> (in which case we use the oneway_merge, otherwise we use twoway_merge).
> 
> Johannes Schindelin (3):
>   rebase: consolidate clean-up code before leaving reset_head()
>   rebase: prepare reset_head() for more flags
>   built-in rebase: reinstate `checkout -q` behavior where appropriate
> 
>  builtin/rebase.c | 79 ++++++++++++++++++++++++++++--------------------
>  1 file changed, 46 insertions(+), 33 deletions(-)

I forgot to specify the changes vs v1:

- More error paths are not consolidated via `goto leave_reset_head`.
- The `desc` array is not initialized to all-zero, to avoid bogus
  addresses being passed to `free()`.
- The `detach_head` and `reset_hard` parameters have been consolidated
  into a `flags` parameter.
- The `reset_head()` function once again only initializes `head_oid`
  when needed.

Sorry for the omission,
Johannes

> 
> 
> base-commit: 8858448bb49332d353febc078ce4a3abcc962efe
> Published-As: 
> https://github.com/gitgitgadget/git/releases/tags/pr-72%2Fdscho%2Fbuiltin-rebase-perf-regression-v2
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git 
> pr-72/dscho/builtin-rebase-perf-regression-v2
> Pull-Request: https://github.com/gitgitgadget/git/pull/72
> 
> Range-diff vs v1:
> 
>  1:  64597fe827 ! 1:  28e24d98ab rebase: consolidate clean-up code before 
> leaving reset_head()
>      @@ -11,6 +11,33 @@
>       --- a/builtin/rebase.c
>       +++ b/builtin/rebase.c
>       @@
>      +        if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
>      +                BUG("Not a fully qualified branch: '%s'", 
> switch_to_branch);
>      + 
>      +-       if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)
>      +-               return -1;
>      ++       if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
>      ++               ret = -1;
>      ++               goto leave_reset_head;
>      ++       }
>      + 
>      +        if (!oid) {
>      +                if (get_oid("HEAD", &head_oid)) {
>      +-                       rollback_lock_file(&lock);
>      +-                       return error(_("could not determine HEAD 
> revision"));
>      ++                       ret = error(_("could not determine HEAD 
> revision"));
>      ++                       goto leave_reset_head;
>      +                }
>      +                oid = &head_oid;
>      +        }
>      +@@
>      +                unpack_tree_opts.reset = 1;
>      + 
>      +        if (read_index_unmerged(the_repository->index) < 0) {
>      +-               rollback_lock_file(&lock);
>      +-               return error(_("could not read index"));
>      ++               ret = error(_("could not read index"));
>      ++               goto leave_reset_head;
>               }
>        
>               if (!fill_tree_descriptor(&desc, oid)) {
>      @@ -31,15 +58,17 @@
>               }
>        
>               tree = parse_tree_indirect(oid);
>      -@@
>      +        prime_cache_tree(the_repository->index, tree);
>        
>      -        if (write_locked_index(the_repository->index, &lock, 
> COMMIT_LOCK) < 0)
>      +-       if (write_locked_index(the_repository->index, &lock, 
> COMMIT_LOCK) < 0)
>      ++       if (write_locked_index(the_repository->index, &lock, 
> COMMIT_LOCK) < 0) {
>                       ret = error(_("could not write index"));
>       -       free((void *)desc.buffer);
>      - 
>      -        if (ret)
>      +-
>      +-       if (ret)
>       -               return ret;
>       +               goto leave_reset_head;
>      ++       }
>        
>               reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
>               strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : 
> "rebase");
>  -:  ---------- > 2:  db963b2094 rebase: prepare reset_head() for more flags
>  2:  070092b430 ! 3:  a7360b856f built-in rebase: reinstate `checkout -q` 
> behavior where appropriate
>      @@ -20,15 +20,18 @@
>       @@
>        #define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
>        
>      + #define RESET_HEAD_DETACH (1<<0)
>      ++#define RESET_HEAD_HARD (1<<1)
>      + 
>        static int reset_head(struct object_id *oid, const char *action,
>      --                     const char *switch_to_branch, int detach_head,
>      -+                     const char *switch_to_branch,
>      -+                     int detach_head, int reset_hard,
>      +                      const char *switch_to_branch, unsigned flags,
>                             const char *reflog_orig_head, const char 
> *reflog_head)
>        {
>      +        unsigned detach_head = flags & RESET_HEAD_DETACH;
>      ++       unsigned reset_hard = flags & RESET_HEAD_HARD;
>               struct object_id head_oid;
>       -       struct tree_desc desc;
>      -+       struct tree_desc desc[2];
>      ++       struct tree_desc desc[2] = { { NULL }, { NULL } };
>               struct lock_file lock = LOCK_INIT;
>               struct unpack_trees_options unpack_tree_opts;
>               struct tree *tree;
>      @@ -42,18 +45,18 @@
>               if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
>                       BUG("Not a fully qualified branch: '%s'", 
> switch_to_branch);
>       @@
>      -        if (hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)
>      -                return -1;
>      +                goto leave_reset_head;
>      +        }
>        
>       -       if (!oid) {
>       -               if (get_oid("HEAD", &head_oid)) {
>      --                       rollback_lock_file(&lock);
>      --                       return error(_("could not determine HEAD 
> revision"));
>      +-                       ret = error(_("could not determine HEAD 
> revision"));
>      +-                       goto leave_reset_head;
>       -               }
>       -               oid = &head_oid;
>      -+       if (get_oid("HEAD", &head_oid)) {
>      -+               rollback_lock_file(&lock);
>      -+               return error(_("could not determine HEAD revision"));
>      ++       if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) {
>      ++               ret = error(_("could not determine HEAD revision"));
>      ++               goto leave_reset_head;
>               }
>        
>       +       if (!oid)
>      @@ -70,7 +73,7 @@
>               unpack_tree_opts.merge = 1;
>               if (!detach_head)
>       @@
>      -                return error(_("could not read index"));
>      +                goto leave_reset_head;
>               }
>        
>       -       if (!fill_tree_descriptor(&desc, oid)) {
>      @@ -104,7 +107,8 @@
>                       string_list_clear(&merge_rr, 1);
>        
>       -               if (reset_head(NULL, "reset", NULL, 0, NULL, NULL) < 0)
>      -+               if (reset_head(NULL, "reset", NULL, 0, 1, NULL, NULL) < 
> 0)
>      ++               if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
>      ++                              NULL, NULL) < 0)
>                               die(_("could not discard worktree changes"));
>                       if (read_basic_state(&options))
>                               exit(1);
>      @@ -113,7 +117,8 @@
>                               exit(1);
>                       if (reset_head(&options.orig_head, "reset",
>       -                              options.head_name, 0, NULL, NULL) < 0)
>      -+                              options.head_name, 0, 1, NULL, NULL) < 0)
>      ++                              options.head_name, RESET_HEAD_HARD,
>      ++                              NULL, NULL) < 0)
>                               die(_("could not move back to %s"),
>                                   oid_to_hex(&options.orig_head));
>                       ret = finish_rebase(&options);
>      @@ -122,34 +127,7 @@
>                               printf(_("Created autostash: %s\n"), buf.buf);
>                               if (reset_head(&head->object.oid, "reset 
> --hard",
>       -                                      NULL, 0, NULL, NULL) < 0)
>      -+                                      NULL, 0, 1, NULL, NULL) < 0)
>      ++                                      NULL, RESET_HEAD_HARD, NULL, 
> NULL) < 0)
>                                       die(_("could not reset --hard"));
>                               printf(_("HEAD is now at %s"),
>                                      find_unique_abbrev(&head->object.oid,
>      -@@
>      -                                strbuf_addf(&buf, "rebase: checkout %s",
>      -                                            options.switch_to);
>      -                                if (reset_head(&oid, "checkout",
>      --                                              options.head_name, 0,
>      -+                                              options.head_name, 0, 0,
>      -                                               NULL, NULL) < 0) {
>      -                                        ret = !!error(_("could not 
> switch to "
>      -                                                        "%s"),
>      -@@
>      -                         "it...\n"));
>      - 
>      -        strbuf_addf(&msg, "rebase: checkout %s", options.onto_name);
>      --       if (reset_head(&options.onto->object.oid, "checkout", NULL, 1,
>      -+       if (reset_head(&options.onto->object.oid, "checkout", NULL, 1, 
> 0,
>      -            NULL, msg.buf))
>      -                die(_("Could not detach HEAD"));
>      -        strbuf_release(&msg);
>      -@@
>      -                strbuf_addf(&msg, "rebase finished: %s onto %s",
>      -                        options.head_name ? options.head_name : 
> "detached HEAD",
>      -                        oid_to_hex(&options.onto->object.oid));
>      --               reset_head(NULL, "Fast-forwarded", options.head_name, 0,
>      -+               reset_head(NULL, "Fast-forwarded", options.head_name, 
> 0, 0,
>      -                           "HEAD", msg.buf);
>      -                strbuf_release(&msg);
>      -                ret = !!finish_rebase(&options);
> 
> -- 
> gitgitgadget
> 

Reply via email to