Merge branch 'jh/trace2'
authorJunio C Hamano <gitster@pobox.com>
Thu, 7 Mar 2019 00:59:56 +0000 (09:59 +0900)
committerJunio C Hamano <gitster@pobox.com>
Thu, 7 Mar 2019 00:59:56 +0000 (09:59 +0900)
A more structured way to obtain execution trace has been added.

* jh/trace2:
trace2: add for_each macros to clang-format
trace2: t/helper/test-trace2, t0210.sh, t0211.sh, t0212.sh
trace2:data: add subverb for rebase
trace2:data: add subverb to reset command
trace2:data: add subverb to checkout command
trace2:data: pack-objects: add trace2 regions
trace2:data: add trace2 instrumentation to index read/write
trace2:data: add trace2 hook classification
trace2:data: add trace2 transport child classification
trace2:data: add trace2 sub-process classification
trace2:data: add editor/pager child classification
trace2:data: add trace2 regions to wt-status
trace2: collect Windows-specific process information
trace2: create new combined trace facility
trace2: Documentation/technical/api-trace2.txt

1  2 
builtin/am.c
builtin/checkout.c
cache.h
config.c
read-cache.c
sequencer.c
diff --combined builtin/am.c
index cd051fecdfc8e178496a698d9c7e7df5a6dd894e,cca494d9c3bac6d256dc9d0e42161975541a1357..86e33495b039bd44d4b59625388d55cd624ccd26
@@@ -453,6 -453,7 +453,7 @@@ static int run_post_rewrite_hook(const 
  
        cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
        cp.stdout_to_stderr = 1;
+       cp.trace2_hook_name = "post-rewrite";
  
        ret = run_command(&cp);
  
@@@ -1579,7 -1580,6 +1580,7 @@@ static void do_commit(const struct am_s
        }
  
        author = fmt_ident(state->author_name, state->author_email,
 +              WANT_AUTHOR_IDENT,
                        state->ignore_date ? NULL : state->author_date,
                        IDENT_STRICT);
  
diff --combined builtin/checkout.c
index bea08ef959189cff99e34170a2999a52953a3020,c14e8c5610bf6046ae484c52bb19136f98dd492b..0e6037b2968f326ca2781fa141d74683e52c983c
@@@ -46,7 -46,6 +46,7 @@@ struct checkout_opts 
        int ignore_other_worktrees;
        int show_progress;
        int count_checkout_paths;
 +      int overlay_mode;
        /*
         * If new checkout options are added, skip_merge_working_tree
         * should be updated accordingly.
@@@ -136,8 -135,7 +136,8 @@@ static int skip_same_name(const struct 
        return pos;
  }
  
 -static int check_stage(int stage, const struct cache_entry *ce, int pos)
 +static int check_stage(int stage, const struct cache_entry *ce, int pos,
 +                     int overlay_mode)
  {
        while (pos < active_nr &&
               !strcmp(active_cache[pos]->name, ce->name)) {
                        return 0;
                pos++;
        }
 +      if (!overlay_mode)
 +              return 0;
        if (stage == 2)
                return error(_("path '%s' does not have our version"), ce->name);
        else
@@@ -172,8 -168,7 +172,8 @@@ static int check_stages(unsigned stages
  }
  
  static int checkout_stage(int stage, const struct cache_entry *ce, int pos,
 -                        const struct checkout *state, int *nr_checkouts)
 +                        const struct checkout *state, int *nr_checkouts,
 +                        int overlay_mode)
  {
        while (pos < active_nr &&
               !strcmp(active_cache[pos]->name, ce->name)) {
                                              NULL, nr_checkouts);
                pos++;
        }
 +      if (!overlay_mode) {
 +              unlink_entry(ce);
 +              return 0;
 +      }
        if (stage == 2)
                return error(_("path '%s' does not have our version"), ce->name);
        else
@@@ -260,59 -251,6 +260,59 @@@ static int checkout_merged(int pos, con
        return status;
  }
  
 +static void mark_ce_for_checkout_overlay(struct cache_entry *ce,
 +                                       char *ps_matched,
 +                                       const struct checkout_opts *opts)
 +{
 +      ce->ce_flags &= ~CE_MATCHED;
 +      if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
 +              return;
 +      if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
 +              /*
 +               * "git checkout tree-ish -- path", but this entry
 +               * is in the original index but is not in tree-ish
 +               * or does not match the pathspec; it will not be
 +               * checked out to the working tree.  We will not do
 +               * anything to this entry at all.
 +               */
 +              return;
 +      /*
 +       * Either this entry came from the tree-ish we are
 +       * checking the paths out of, or we are checking out
 +       * of the index.
 +       *
 +       * If it comes from the tree-ish, we already know it
 +       * matches the pathspec and could just stamp
 +       * CE_MATCHED to it from update_some(). But we still
 +       * need ps_matched and read_tree_recursive (and
 +       * eventually tree_entry_interesting) cannot fill
 +       * ps_matched yet. Once it can, we can avoid calling
 +       * match_pathspec() for _all_ entries when
 +       * opts->source_tree != NULL.
 +       */
 +      if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched))
 +              ce->ce_flags |= CE_MATCHED;
 +}
 +
 +static void mark_ce_for_checkout_no_overlay(struct cache_entry *ce,
 +                                          char *ps_matched,
 +                                          const struct checkout_opts *opts)
 +{
 +      ce->ce_flags &= ~CE_MATCHED;
 +      if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
 +              return;
 +      if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched)) {
 +              ce->ce_flags |= CE_MATCHED;
 +              if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
 +                      /*
 +                       * In overlay mode, but the path is not in
 +                       * tree-ish, which means we should remove it
 +                       * from the index and the working tree.
 +                       */
 +                      ce->ce_flags |= CE_REMOVE | CE_WT_REMOVE;
 +      }
 +}
 +
  static int checkout_paths(const struct checkout_opts *opts,
                          const char *revision)
  {
        struct lock_file lock_file = LOCK_INIT;
        int nr_checkouts = 0, nr_unmerged = 0;
  
+       trace2_cmd_mode(opts->patch_mode ? "patch" : "path");
        if (opts->track != BRANCH_TRACK_UNSPECIFIED)
                die(_("'%s' cannot be used with updating paths"), "--track");
  
         * Make sure all pathspecs participated in locating the paths
         * to be checked out.
         */
 -      for (pos = 0; pos < active_nr; pos++) {
 -              struct cache_entry *ce = active_cache[pos];
 -              ce->ce_flags &= ~CE_MATCHED;
 -              if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
 -                      continue;
 -              if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
 -                      /*
 -                       * "git checkout tree-ish -- path", but this entry
 -                       * is in the original index; it will not be checked
 -                       * out to the working tree and it does not matter
 -                       * if pathspec matched this entry.  We will not do
 -                       * anything to this entry at all.
 -                       */
 -                      continue;
 -              /*
 -               * Either this entry came from the tree-ish we are
 -               * checking the paths out of, or we are checking out
 -               * of the index.
 -               *
 -               * If it comes from the tree-ish, we already know it
 -               * matches the pathspec and could just stamp
 -               * CE_MATCHED to it from update_some(). But we still
 -               * need ps_matched and read_tree_recursive (and
 -               * eventually tree_entry_interesting) cannot fill
 -               * ps_matched yet. Once it can, we can avoid calling
 -               * match_pathspec() for _all_ entries when
 -               * opts->source_tree != NULL.
 -               */
 -              if (ce_path_match(&the_index, ce, &opts->pathspec, ps_matched))
 -                      ce->ce_flags |= CE_MATCHED;
 -      }
 +      for (pos = 0; pos < active_nr; pos++)
 +              if (opts->overlay_mode)
 +                      mark_ce_for_checkout_overlay(active_cache[pos],
 +                                                   ps_matched,
 +                                                   opts);
 +              else
 +                      mark_ce_for_checkout_no_overlay(active_cache[pos],
 +                                                      ps_matched,
 +                                                      opts);
  
        if (report_path_error(ps_matched, &opts->pathspec, opts->prefix)) {
                free(ps_matched);
                        if (opts->force) {
                                warning(_("path '%s' is unmerged"), ce->name);
                        } else if (opts->writeout_stage) {
 -                              errs |= check_stage(opts->writeout_stage, ce, pos);
 +                              errs |= check_stage(opts->writeout_stage, ce, pos, opts->overlay_mode);
                        } else if (opts->merge) {
                                errs |= check_stages((1<<2) | (1<<3), ce, pos);
                        } else {
                        if (opts->writeout_stage)
                                errs |= checkout_stage(opts->writeout_stage,
                                                       ce, pos,
 -                                                     &state, &nr_checkouts);
 +                                                     &state,
 +                                                     &nr_checkouts, opts->overlay_mode);
                        else if (opts->merge)
                                errs |= checkout_merged(pos, &state,
                                                        &nr_unmerged);
                        pos = skip_same_name(ce, pos) - 1;
                }
        }
 +      remove_marked_cache_entries(&the_index, 1);
 +      remove_scheduled_dirs();
        errs |= finish_delayed_checkout(&state, &nr_checkouts);
  
        if (opts->count_checkout_paths) {
@@@ -614,11 -573,6 +616,11 @@@ static int skip_merge_working_tree(cons
         * opts->show_progress only impacts output so doesn't require a merge
         */
  
 +      /*
 +       * opts->overlay_mode cannot be used with switching branches so is
 +       * not tested here
 +       */
 +
        /*
         * If we aren't creating a new branch any changes or updates will
         * happen in the existing branch.  Since that could only be updating
@@@ -1014,6 -968,9 +1016,9 @@@ static int switch_branches(const struc
        void *path_to_free;
        struct object_id rev;
        int flag, writeout_error = 0;
+       trace2_cmd_mode("branch");
        memset(&old_branch_info, 0, sizeof(old_branch_info));
        old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
        if (old_branch_info.path)
@@@ -1251,6 -1208,8 +1256,8 @@@ static int switch_unborn_to_new_branch(
        int status;
        struct strbuf branch_ref = STRBUF_INIT;
  
+       trace2_cmd_mode("unborn");
        if (!opts->new_branch)
                die(_("You are on a branch yet to be born"));
        strbuf_addf(&branch_ref, "refs/heads/%s", opts->new_branch);
@@@ -1272,10 -1231,6 +1279,10 @@@ static int checkout_branch(struct check
                die(_("'%s' cannot be used with switching branches"),
                    "--patch");
  
 +      if (!opts->overlay_mode)
 +              die(_("'%s' cannot be used with switching branches"),
 +                  "--no-overlay");
 +
        if (opts->writeout_stage)
                die(_("'%s' cannot be used with switching branches"),
                    "--ours/--theirs");
@@@ -1364,7 -1319,6 +1371,7 @@@ int cmd_checkout(int argc, const char *
                            "checkout", "control recursive updating of submodules",
                            PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
                OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
 +              OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
                OPT_END(),
        };
  
        opts.overwrite_ignore = 1;
        opts.prefix = prefix;
        opts.show_progress = -1;
 +      opts.overlay_mode = -1;
  
        git_config(git_checkout_config, &opts);
  
        if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
                die(_("-b, -B and --orphan are mutually exclusive"));
  
 +      if (opts.overlay_mode == 1 && opts.patch_mode)
 +              die(_("-p and --overlay are mutually exclusive"));
 +
        /*
         * From here on, new_branch will contain the branch to be checked out,
         * and new_branch_force and new_orphan_branch will tell us which one of
diff --combined cache.h
index bff556e7720b2aec7c2824b7101a0c5bd175551b,c156fa998e72402700583e32222f8be3b36e0727..abd518a9a23394fd47b572c6957c712abac139a2
+++ b/cache.h
@@@ -9,6 -9,7 +9,7 @@@
  #include "gettext.h"
  #include "convert.h"
  #include "trace.h"
+ #include "trace2.h"
  #include "string-list.h"
  #include "pack-revindex.h"
  #include "hash.h"
@@@ -758,7 -759,7 +759,7 @@@ extern void rename_index_entry_at(struc
  /* Remove entry, return true if there are more entries to go. */
  extern int remove_index_entry_at(struct index_state *, int pos);
  
 -extern void remove_marked_cache_entries(struct index_state *istate);
 +extern void remove_marked_cache_entries(struct index_state *istate, int invalidate);
  extern int remove_file_from_index(struct index_state *, const char *path);
  #define ADD_CACHE_VERBOSE 1
  #define ADD_CACHE_PRETEND 2
@@@ -1506,19 -1507,10 +1507,19 @@@ int date_overflows(timestamp_t date)
  #define IDENT_STRICT         1
  #define IDENT_NO_DATE        2
  #define IDENT_NO_NAME        4
 +
 +enum want_ident {
 +      WANT_BLANK_IDENT,
 +      WANT_AUTHOR_IDENT,
 +      WANT_COMMITTER_IDENT
 +};
 +
  extern const char *git_author_info(int);
  extern const char *git_committer_info(int);
 -extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
 -extern const char *fmt_name(const char *name, const char *email);
 +extern const char *fmt_ident(const char *name, const char *email,
 +              enum want_ident whose_ident,
 +              const char *date_str, int);
 +extern const char *fmt_name(enum want_ident);
  extern const char *ident_default_name(void);
  extern const char *ident_default_email(void);
  extern const char *git_editor(void);
@@@ -1578,11 -1570,6 +1579,11 @@@ struct checkout 
  extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath, int *nr_checkouts);
  extern void enable_delayed_checkout(struct checkout *state);
  extern int finish_delayed_checkout(struct checkout *state, int *nr_checkouts);
 +/*
 + * Unlink the last component and schedule the leading directories for
 + * removal, such that empty directories get removed.
 + */
 +extern void unlink_entry(const struct cache_entry *ce);
  
  struct cache_def {
        struct strbuf path;
diff --combined config.c
index 921f73dc962e578e9aed7ae11ae6746f4f6b75c8,5c69966e016f201f40306b2f002830ba823ade38..0f0cdd8c0fc9ee40c1f56d1935c1816eb03601ad
+++ b/config.c
@@@ -1445,9 -1445,7 +1445,9 @@@ int git_default_config(const char *var
        if (starts_with(var, "core."))
                return git_default_core_config(var, value, cb);
  
 -      if (starts_with(var, "user."))
 +      if (starts_with(var, "user.") ||
 +          starts_with(var, "author.") ||
 +          starts_with(var, "committer."))
                return git_ident_config(var, value, cb);
  
        if (starts_with(var, "i18n."))
@@@ -2657,6 -2655,8 +2657,8 @@@ int git_config_set_gently(const char *k
  void git_config_set(const char *key, const char *value)
  {
        git_config_set_multivar(key, value, NULL, 0);
+       trace2_cmd_set_config(key, value);
  }
  
  /*
diff --combined read-cache.c
index 441537032f65bcbb0182f8c7f82d78e86afc3022,eee8351d8e9f5f8d13188dc046b36433185e7064..4dc6de1b55b0a6047e49f188b28c431f0167d7ef
@@@ -588,19 -588,13 +588,19 @@@ int remove_index_entry_at(struct index_
   * CE_REMOVE is set in ce_flags.  This is much more effective than
   * calling remove_index_entry_at() for each entry to be removed.
   */
 -void remove_marked_cache_entries(struct index_state *istate)
 +void remove_marked_cache_entries(struct index_state *istate, int invalidate)
  {
        struct cache_entry **ce_array = istate->cache;
        unsigned int i, j;
  
        for (i = j = 0; i < istate->cache_nr; i++) {
                if (ce_array[i]->ce_flags & CE_REMOVE) {
 +                      if (invalidate) {
 +                              cache_tree_invalidate_path(istate,
 +                                                         ce_array[i]->name);
 +                              untracked_cache_remove_from_index(istate,
 +                                                                ce_array[i]->name);
 +                      }
                        remove_name_hash(istate, ce_array[i]);
                        save_or_free_index_entry(istate, ce_array[i]);
                }
@@@ -2226,6 -2220,16 +2226,16 @@@ int do_read_index(struct index_state *i
                load_index_extensions(&p);
        }
        munmap((void *)mmap, mmap_size);
+       /*
+        * TODO trace2: replace "the_repository" with the actual repo instance
+        * that is associated with the given "istate".
+        */
+       trace2_data_intmax("index", the_repository, "read/version",
+                          istate->version);
+       trace2_data_intmax("index", the_repository, "read/cache_nr",
+                          istate->cache_nr);
        return istate->cache_nr;
  
  unmap:
@@@ -2257,9 -2261,17 +2267,17 @@@ int read_index_from(struct index_state 
        if (istate->initialized)
                return istate->cache_nr;
  
+       /*
+        * TODO trace2: replace "the_repository" with the actual repo instance
+        * that is associated with the given "istate".
+        */
+       trace2_region_enter_printf("index", "do_read_index", the_repository,
+                                  "%s", path);
        trace_performance_enter();
        ret = do_read_index(istate, path, 0);
        trace_performance_leave("read cache %s", path);
+       trace2_region_leave_printf("index", "do_read_index", the_repository,
+                                  "%s", path);
  
        split_index = istate->split_index;
        if (!split_index || is_null_oid(&split_index->base_oid)) {
  
        base_oid_hex = oid_to_hex(&split_index->base_oid);
        base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex);
+       trace2_region_enter_printf("index", "shared/do_read_index",
+                                  the_repository, "%s", base_path);
        ret = do_read_index(split_index->base, base_path, 1);
+       trace2_region_leave_printf("index", "shared/do_read_index",
+                                  the_repository, "%s", base_path);
        if (!oideq(&split_index->base_oid, &split_index->base->oid))
                die(_("broken index, expect %s in %s, got %s"),
                    base_oid_hex, base_path,
@@@ -2900,8 -2916,7 +2922,8 @@@ static int do_write_index(struct index_
                        return -1;
        }
  
 -      if (!strip_extensions && istate->split_index) {
 +      if (!strip_extensions && istate->split_index &&
 +          !is_null_oid(&istate->split_index->base_oid)) {
                struct strbuf sb = STRBUF_INIT;
  
                err = write_link_extension(&sb, istate) < 0 ||
        istate->timestamp.sec = (unsigned int)st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
        trace_performance_since(start, "write index, changed mask = %x", istate->cache_changed);
+       /*
+        * TODO trace2: replace "the_repository" with the actual repo instance
+        * that is associated with the given "istate".
+        */
+       trace2_data_intmax("index", the_repository, "write/version",
+                          istate->version);
+       trace2_data_intmax("index", the_repository, "write/cache_nr",
+                          istate->cache_nr);
        return 0;
  }
  
@@@ -3002,7 -3027,18 +3034,18 @@@ static int commit_locked_index(struct l
  static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
                                 unsigned flags)
  {
-       int ret = do_write_index(istate, lock->tempfile, 0);
+       int ret;
+       /*
+        * TODO trace2: replace "the_repository" with the actual repo instance
+        * that is associated with the given "istate".
+        */
+       trace2_region_enter_printf("index", "do_write_index", the_repository,
+                                  "%s", lock->tempfile->filename.buf);
+       ret = do_write_index(istate, lock->tempfile, 0);
+       trace2_region_leave_printf("index", "do_write_index", the_repository,
+                                  "%s", lock->tempfile->filename.buf);
        if (ret)
                return ret;
        if (flags & COMMIT_LOCK)
@@@ -3087,7 -3123,13 +3130,13 @@@ static int write_shared_index(struct in
        int ret;
  
        move_cache_to_base_index(istate);
+       trace2_region_enter_printf("index", "shared/do_write_index",
+                                  the_repository, "%s", (*temp)->filename.buf);
        ret = do_write_index(si->base, *temp, 1);
+       trace2_region_enter_printf("index", "shared/do_write_index",
+                                  the_repository, "%s", (*temp)->filename.buf);
        if (ret)
                return ret;
        ret = adjust_shared_perm(get_tempfile_path(*temp));
@@@ -3196,7 -3238,7 +3245,7 @@@ int write_locked_index(struct index_sta
        ret = write_split_index(istate, lock, flags);
  
        /* Freshen the shared index only if the split-index was written */
 -      if (!ret && !new_shared_index) {
 +      if (!ret && !new_shared_index && !is_null_oid(&si->base_oid)) {
                const char *shared_index = git_path("sharedindex.%s",
                                                    oid_to_hex(&si->base_oid));
                freshen_shared_index(shared_index, 1);
diff --combined sequencer.c
index 3209cdefd1d9287ecd8c077cc254fd63a600112c,1625f48f833e139a38c34034e067d1799c2e1e11..95dda23eee450d1d60a70434d254071c6c05146b
@@@ -837,7 -837,7 +837,7 @@@ static const char *read_author_ident(st
        }
  
        strbuf_reset(&out);
 -      strbuf_addstr(&out, fmt_ident(name, email, date, 0));
 +      strbuf_addstr(&out, fmt_ident(name, email, WANT_AUTHOR_IDENT, date, 0));
        strbuf_swap(buf, &out);
        strbuf_release(&out);
        free(name);
@@@ -1103,6 -1103,7 +1103,7 @@@ static int run_rewrite_hook(const struc
        proc.argv = argv;
        proc.in = -1;
        proc.stdout_to_stderr = 1;
+       proc.trace2_hook_name = "post-rewrite";
  
        code = start_command(&proc);
        if (code)
@@@ -3786,6 -3787,7 +3787,7 @@@ cleanup_head_ref
                                hook.in = open(rebase_path_rewritten_list(),
                                        O_RDONLY);
                                hook.stdout_to_stderr = 1;
+                               hook.trace2_hook_name = "post-rewrite";
                                argv_array_push(&hook.args, post_rewrite_hook);
                                argv_array_push(&hook.args, "rebase");
                                /* we don't care if this hook failed */
@@@ -4098,7 -4100,8 +4100,7 @@@ void append_signoff(struct strbuf *msgb
        int has_footer;
  
        strbuf_addstr(&sob, sign_off_header);
 -      strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
 -                              getenv("GIT_COMMITTER_EMAIL")));
 +      strbuf_addstr(&sob, fmt_name(WANT_COMMITTER_IDENT));
        strbuf_addch(&sob, '\n');
  
        if (!ignore_footer)