From fd104ef15deb16fae9ed48ad548f1d7a40e17222 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Fri, 13 Feb 2026 19:54:55 +0000 Subject: [PATCH 1/6] trace2: add macOS process ancestry tracing In 353d3d77f4 (trace2: collect Windows-specific process information, 2019-02-22) Windows-specific process ancestry information was added as a data_json event to TRACE2. Furthermore in 2f732bf15e (tr2: log parent process name, 2021-07-21) similar functionality was added for Linux-based systems, using procfs. Teach Git to also log process ancestry on macOS using the sysctl with KERN_PROC to get process information (PPID and process name). Like the Linux implementation, we use the cmd_ancestry TRACE2 event rather than using a data_json event and creating another custom data point. Signed-off-by: Matthew John Cheetham Signed-off-by: Junio C Hamano --- compat/darwin/procinfo.c | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 compat/darwin/procinfo.c diff --git a/compat/darwin/procinfo.c b/compat/darwin/procinfo.c new file mode 100644 index 0000000000..c8954f02d7 --- /dev/null +++ b/compat/darwin/procinfo.c @@ -0,0 +1,97 @@ +#include "git-compat-util.h" +#include "strbuf.h" +#include "strvec.h" +#include "trace2.h" +#include + +/* + * An arbitrarily chosen value to limit the depth of the ancestor chain. + */ +#define NR_PIDS_LIMIT 10 + +/* + * Get the process name and parent PID for a given PID using sysctl(). + * Returns 0 on success, -1 on failure. + */ +static int get_proc_info(pid_t pid, struct strbuf *name, pid_t *ppid) +{ + int mib[4]; + struct kinfo_proc proc; + size_t size = sizeof(proc); + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = pid; + + if (sysctl(mib, 4, &proc, &size, NULL, 0) < 0) + return -1; + + if (size == 0) + return -1; + + strbuf_addstr(name, proc.kp_proc.p_comm); + *ppid = proc.kp_eproc.e_ppid; + + return 0; +} + +/* + * Recursively push process names onto the ancestry array. + * We guard against cycles by limiting the depth to NR_PIDS_LIMIT. + */ +static void push_ancestry_name(struct strvec *names, pid_t pid, int depth) +{ + struct strbuf name = STRBUF_INIT; + pid_t ppid; + + if (depth >= NR_PIDS_LIMIT) + return; + + if (pid <= 0) + return; + + if (get_proc_info(pid, &name, &ppid) < 0) + goto cleanup; + + strvec_push(names, name.buf); + + /* + * Recurse to the parent process. Stop if ppid not valid + * or if we've reached ourselves (cycle). + */ + if (ppid && ppid != pid) + push_ancestry_name(names, ppid, depth + 1); + +cleanup: + strbuf_release(&name); +} + +void trace2_collect_process_info(enum trace2_process_info_reason reason) +{ + struct strvec names = STRVEC_INIT; + + if (!trace2_is_enabled()) + return; + + switch (reason) { + case TRACE2_PROCESS_INFO_STARTUP: + push_ancestry_name(&names, getppid(), 0); + if (names.nr) + trace2_cmd_ancestry(names.v); + + strvec_clear(&names); + break; + + case TRACE2_PROCESS_INFO_EXIT: + /* + * The Windows version of this calls its + * get_peak_memory_info() here. We may want to insert + * similar process-end statistics here in the future. + */ + break; + + default: + BUG("trace2_collect_process_info: unknown reason '%d'", reason); + } +} From 088aaf1d416e5ea1f6986d82df9ba63d7bf1197f Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Fri, 13 Feb 2026 19:54:56 +0000 Subject: [PATCH 2/6] build: include procinfo.c impl for macOS Include an implementation of trace2_collect_process_info for macOS. Signed-off-by: Matthew John Cheetham Signed-off-by: Junio C Hamano --- config.mak.uname | 2 ++ contrib/buildsystems/CMakeLists.txt | 2 ++ meson.build | 2 ++ 3 files changed, 6 insertions(+) diff --git a/config.mak.uname b/config.mak.uname index 1691c6ae6e..baa5018461 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -148,6 +148,8 @@ ifeq ($(uname_S),Darwin) HAVE_NS_GET_EXECUTABLE_PATH = YesPlease CSPRNG_METHOD = arc4random USE_ENHANCED_BASIC_REGULAR_EXPRESSIONS = YesPlease + HAVE_PLATFORM_PROCINFO = YesPlease + COMPAT_OBJS += compat/darwin/procinfo.o # Workaround for `gettext` being keg-only and not even being linked via # `brew link --force gettext`, should be obsolete as of diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt index edb0fc04ad..d489f0cada 100644 --- a/contrib/buildsystems/CMakeLists.txt +++ b/contrib/buildsystems/CMakeLists.txt @@ -274,6 +274,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows") elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") add_compile_definitions(PROCFS_EXECUTABLE_PATH="/proc/self/exe" HAVE_DEV_TTY ) list(APPEND compat_SOURCES unix-socket.c unix-stream-server.c compat/linux/procinfo.c) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + list(APPEND compat_SOURCES compat/darwin/procinfo.c) endif() if(CMAKE_SYSTEM_NAME STREQUAL "Windows") diff --git a/meson.build b/meson.build index 1f95a06edb..32d470e4f7 100644 --- a/meson.build +++ b/meson.build @@ -1292,6 +1292,8 @@ if host_machine.system() == 'linux' libgit_sources += 'compat/linux/procinfo.c' elif host_machine.system() == 'windows' libgit_sources += 'compat/win32/trace2_win32_process_info.c' +elif host_machine.system() == 'darwin' + libgit_sources += 'compat/darwin/procinfo.c' else libgit_sources += 'compat/stub/procinfo.c' endif From c2a473b2b1b3b79e308a7cbed1eee46fb67aacd8 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Fri, 13 Feb 2026 19:54:57 +0000 Subject: [PATCH 3/6] trace2: refactor Windows process ancestry trace2 event In 353d3d77f4 (trace2: collect Windows-specific process information, 2019-02-22) we added process ancestry information for Windows to TRACE2 via a data_json event. It was only later in 2f732bf15e (tr2: log parent process name, 2021-07-21) that the specific cmd_ancestry event was added to TRACE2. In a future commit we will emit the ancestry information with the newer cmd_ancestry TRACE2 event. Right now, we rework this implementation of trace2_collect_process_info to separate the calculation of ancestors from building and emiting the JSON array via a data_json event. Signed-off-by: Matthew John Cheetham Signed-off-by: Junio C Hamano --- compat/win32/trace2_win32_process_info.c | 50 ++++++++++++------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c index f147da706a..aceea05430 100644 --- a/compat/win32/trace2_win32_process_info.c +++ b/compat/win32/trace2_win32_process_info.c @@ -3,6 +3,7 @@ #include "../../git-compat-util.h" #include "../../json-writer.h" #include "../../repository.h" +#include "../../strvec.h" #include "../../trace2.h" #include "lazyload.h" #include @@ -32,12 +33,7 @@ static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32) } /* - * Accumulate JSON array of our parent processes: - * [ - * exe-name-parent, - * exe-name-grand-parent, - * ... - * ] + * Accumulate array of our parent process names. * * Note: we only report the filename of the process executable; the * only way to get its full pathname is to use OpenProcess() @@ -73,7 +69,7 @@ static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32) * simple and avoid the alloc/realloc overhead. It is OK if we * truncate the search and return a partial answer. */ -static void get_processes(struct json_writer *jw, HANDLE hSnapshot) +static void get_processes(struct strvec *names, HANDLE hSnapshot) { PROCESSENTRY32 pe32; DWORD pid; @@ -82,19 +78,19 @@ static void get_processes(struct json_writer *jw, HANDLE hSnapshot) pid = GetCurrentProcessId(); while (find_pid(pid, hSnapshot, &pe32)) { - /* Only report parents. Omit self from the JSON output. */ + /* Only report parents. Omit self from the output. */ if (nr_pids) - jw_array_string(jw, pe32.szExeFile); + strvec_push(names, pe32.szExeFile); /* Check for cycle in snapshot. (Yes, it happened.) */ for (k = 0; k < nr_pids; k++) if (pid == pid_list[k]) { - jw_array_string(jw, "(cycle)"); + strvec_push(names, "(cycle)"); return; } if (nr_pids == NR_PIDS_LIMIT) { - jw_array_string(jw, "(truncated)"); + strvec_push(names, "(truncated)"); return; } @@ -105,24 +101,14 @@ static void get_processes(struct json_writer *jw, HANDLE hSnapshot) } /* - * Emit JSON data for the current and parent processes. Individual - * trace2 targets can decide how to actually print it. + * Collect the list of parent process names. */ -static void get_ancestry(void) +static void get_ancestry(struct strvec *names) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot != INVALID_HANDLE_VALUE) { - struct json_writer jw = JSON_WRITER_INIT; - - jw_array_begin(&jw, 0); - get_processes(&jw, hSnapshot); - jw_end(&jw); - - trace2_data_json("process", the_repository, "windows/ancestry", - &jw); - - jw_release(&jw); + get_processes(names, hSnapshot); CloseHandle(hSnapshot); } } @@ -176,13 +162,27 @@ static void get_peak_memory_info(void) void trace2_collect_process_info(enum trace2_process_info_reason reason) { + struct strvec names = STRVEC_INIT; + if (!trace2_is_enabled()) return; switch (reason) { case TRACE2_PROCESS_INFO_STARTUP: get_is_being_debugged(); - get_ancestry(); + get_ancestry(&names); + if (names.nr) { + struct json_writer jw = JSON_WRITER_INIT; + jw_array_begin(&jw, 0); + for (size_t i = 0; i < names.nr; i++) + jw_array_string(&jw, names.v[i]); + jw_end(&jw); + trace2_data_json("process", the_repository, + "windows/ancestry", &jw); + jw_release(&jw); + } + + strvec_clear(&names); return; case TRACE2_PROCESS_INFO_EXIT: From d7301487a38766109589b02309dac1be6490fa95 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Fri, 13 Feb 2026 19:54:58 +0000 Subject: [PATCH 4/6] trace2: emit cmd_ancestry data for Windows Since 2f732bf15e (tr2: log parent process name, 2021-07-21) it is now now possible to emit a specific process ancestry event in TRACE2. We should emit the Windows process ancestry data with the correct event type. To not break existing consumers of the data_json "windows/ancestry" event, we continue to emit the ancestry data as a JSON event. Signed-off-by: Matthew John Cheetham Signed-off-by: Junio C Hamano --- compat/win32/trace2_win32_process_info.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c index aceea05430..6a6a396078 100644 --- a/compat/win32/trace2_win32_process_info.c +++ b/compat/win32/trace2_win32_process_info.c @@ -172,6 +172,11 @@ void trace2_collect_process_info(enum trace2_process_info_reason reason) get_is_being_debugged(); get_ancestry(&names); if (names.nr) { + /* + Emit the ancestry data as a data_json event to + maintain compatibility for consumers of the older + "windows/ancestry" event. + */ struct json_writer jw = JSON_WRITER_INIT; jw_array_begin(&jw, 0); for (size_t i = 0; i < names.nr; i++) @@ -180,6 +185,9 @@ void trace2_collect_process_info(enum trace2_process_info_reason reason) trace2_data_json("process", the_repository, "windows/ancestry", &jw); jw_release(&jw); + + /* Emit the ancestry data with the new event. */ + trace2_cmd_ancestry(names.v); } strvec_clear(&names); From b6a125b936bfb05de704243e1fa568d4a9ad9a11 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Fri, 13 Feb 2026 19:54:59 +0000 Subject: [PATCH 5/6] test-tool: extend trace2 helper with 400ancestry Add a new test helper "400ancestry" to the trace2 test-tool that spawns a child process with a controlled trace2 environment, capturing only the child's trace2 output (including cmd_ancestry events) in isolation. The helper clears all inherited GIT_TRACE2* variables in the child and enables only the requested target (normal, perf, or event), directing output to a specified file. This gives the test suite a reliable way to capture cmd_ancestry events: the child always sees "test-tool" as its immediate parent in the process ancestry, providing a predictable value to verify in tests. Signed-off-by: Matthew John Cheetham Signed-off-by: Junio C Hamano --- t/helper/test-trace2.c | 59 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c index 415df078c1..3b12f4173e 100644 --- a/t/helper/test-trace2.c +++ b/t/helper/test-trace2.c @@ -466,6 +466,63 @@ static int ut_303redact_def_param(int argc, const char **argv) return 0; } +/* + * Run a child process with specific trace2 environment settings so that + * we can capture its trace2 output (including cmd_ancestry) in isolation. + * + * test-tool trace2 400ancestry [] + * + * is one of: normal, perf, event + * + * For example: + * test-tool trace2 400ancestry normal out.normal test-tool trace2 001return 0 + * + * The child process inherits a controlled trace2 environment where only + * the specified target is directed to . The parent's trace2 + * environment variables are cleared in the child so that only the child's + * events are captured. + * + * This is used by t0213-trace2-ancestry.sh to test cmd_ancestry events. + * The child process will see "test-tool" as its immediate parent in the + * process ancestry, giving us a predictable value to verify. + */ +static int ut_400ancestry(int argc, const char **argv) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + const char *target; + const char *outfile; + int result; + + if (argc < 3) + die("expect "); + + target = argv[0]; + outfile = argv[1]; + argv += 2; + argc -= 2; + + /* Clear all trace2 environment variables in the child. */ + strvec_push(&cmd.env, "GIT_TRACE2="); + strvec_push(&cmd.env, "GIT_TRACE2_PERF="); + strvec_push(&cmd.env, "GIT_TRACE2_EVENT="); + strvec_push(&cmd.env, "GIT_TRACE2_BRIEF=1"); + + /* Set only the requested target. */ + if (!strcmp(target, "normal")) + strvec_pushf(&cmd.env, "GIT_TRACE2=%s", outfile); + else if (!strcmp(target, "perf")) + strvec_pushf(&cmd.env, "GIT_TRACE2_PERF=%s", outfile); + else if (!strcmp(target, "event")) + strvec_pushf(&cmd.env, "GIT_TRACE2_EVENT=%s", outfile); + else + die("invalid target '%s', expected: normal, perf, event", + target); + + strvec_pushv(&cmd.args, argv); + result = run_command(&cmd); + exit(result); +} + /* * Usage: * test-tool trace2 @@ -497,6 +554,8 @@ static struct unit_test ut_table[] = { { ut_301redact_child_start, "301redact_child_start", "" }, { ut_302redact_exec, "302redact_exec", " " }, { ut_303redact_def_param, "303redact_def_param", " " }, + + { ut_400ancestry, "400ancestry", " []" }, }; /* clang-format on */ From 3c8c638df66d6a77d280a04321aeb18b2ad61338 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Fri, 13 Feb 2026 19:55:00 +0000 Subject: [PATCH 6/6] t0213: add trace2 cmd_ancestry tests Add a new test script t0213-trace2-ancestry.sh that verifies cmd_ancestry events across all three trace2 output formats (normal, perf, and event). The tests use the "400ancestry" test helper to spawn child processes with controlled trace2 environments. Git alias resolution (which spawns a child git process) creates a predictable multi-level process tree. Filter functions extract cmd_ancestry events from each format, truncating the ancestor list at the outermost "test-tool" so that only the controlled portion of the tree is verified, regardless of the test runner environment. A runtime prerequisite (TRACE2_ANCESTRY) is used to detect whether the platform has a real procinfo implementation; platforms with only the stub are skipped. We must pay attention to an extra ancestor on Windows (MINGW) when running without the bin-wrappers (such as we do in CI). In this situation we see an extra "sh.exe" ancestor after "test-tool.exe". Also update the comment in t0210-trace2-normal.sh to reflect that ancestry testing now has its own dedicated test script. Signed-off-by: Matthew John Cheetham Signed-off-by: Junio C Hamano --- t/meson.build | 1 + t/t0210-trace2-normal.sh | 5 +- t/t0213-trace2-ancestry.sh | 180 +++++++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 2 deletions(-) create mode 100755 t/t0213-trace2-ancestry.sh diff --git a/t/meson.build b/t/meson.build index a5531df415..551c3036c0 100644 --- a/t/meson.build +++ b/t/meson.build @@ -131,6 +131,7 @@ integration_tests = [ 't0210-trace2-normal.sh', 't0211-trace2-perf.sh', 't0212-trace2-event.sh', + 't0213-trace2-ancestry.sh', 't0300-credentials.sh', 't0301-credential-cache.sh', 't0302-credential-store.sh', diff --git a/t/t0210-trace2-normal.sh b/t/t0210-trace2-normal.sh index 96c68f65df..7e1e7af862 100755 --- a/t/t0210-trace2-normal.sh +++ b/t/t0210-trace2-normal.sh @@ -74,8 +74,9 @@ scrub_normal () { # This line is only emitted when RUNTIME_PREFIX is defined, # so just omit it for testing purposes. # - # 4. 'cmd_ancestry' is not implemented everywhere, so for portability's - # sake, skip it when parsing normal. + # 4. 'cmd_ancestry' output depends on how the test is run and + # is not relevant to the features we are testing here. + # Ancestry tests are covered in t0213-trace2-ancestry.sh instead. sed \ -e 's/elapsed:[0-9]*\.[0-9][0-9]*\([eE][-+]\{0,1\}[0-9][0-9]*\)\{0,1\}/elapsed:_TIME_/g' \ -e "s/^start '[^']*' \(.*\)/start _EXE_ \1/" \ diff --git a/t/t0213-trace2-ancestry.sh b/t/t0213-trace2-ancestry.sh new file mode 100755 index 0000000000..a2b9536da8 --- /dev/null +++ b/t/t0213-trace2-ancestry.sh @@ -0,0 +1,180 @@ +#!/bin/sh + +test_description='test trace2 cmd_ancestry event' + +. ./test-lib.sh + +# Turn off any inherited trace2 settings for this test. +sane_unset GIT_TRACE2 GIT_TRACE2_PERF GIT_TRACE2_EVENT +sane_unset GIT_TRACE2_BRIEF +sane_unset GIT_TRACE2_CONFIG_PARAMS + +# Add t/helper directory to PATH so that we can use a relative +# path to run nested instances of test-tool.exe (see 004child). +# This helps with HEREDOC comparisons later. +TTDIR="$GIT_BUILD_DIR/t/helper/" && export TTDIR +PATH="$TTDIR:$PATH" && export PATH + +# The 400ancestry helper spawns a child process so that the child +# sees "test-tool" in its process ancestry. We capture only the +# child's trace2 output to a file. +# +# The tests use git commands that spawn child git processes (e.g., +# alias resolution) to create a controlled multi-level process tree. +# Because cmd_ancestry walks the real process tree, processes will +# also report ancestors above "test-tool" that depend on the test +# runner environment (e.g., bash, make, tmux). The filter functions +# below truncate the ancestry at "test-tool", discarding anything +# above it, so only the controlled portion is verified. +# +# On platforms without a real procinfo implementation (the stub), +# no cmd_ancestry event is emitted. We detect this at runtime and +# skip the format-specific tests accordingly. + +# Determine if cmd_ancestry is supported on this platform. +test_expect_success 'detect cmd_ancestry support' ' + test_when_finished "rm -f trace.detect" && + GIT_TRACE2_BRIEF=1 GIT_TRACE2="$(pwd)/trace.detect" \ + test-tool trace2 001return 0 && + if grep -q "^cmd_ancestry" trace.detect + then + test_set_prereq TRACE2_ANCESTRY + fi +' + +# Filter functions for each trace2 target format. +# +# Each extracts cmd_ancestry events, strips format-specific syntax, +# and truncates the ancestor list at the outermost "test-tool" +# (or "test-tool.exe" on Windows), discarding any higher-level +# (uncontrolled) ancestors. +# +# Output is a space-separated list of ancestor names, one line per +# cmd_ancestry event, with the immediate parent listed first: +# +# test-tool (or: test-tool.exe) +# git test-tool (or: git.exe test-tool.exe) +# git test-tool test-tool (or: git.exe test-tool.exe test-tool.exe) + +if test_have_prereq MINGW +then + TT=test-tool$X +else + TT=test-tool +fi + +filter_ancestry_normal () { + sed -n '/^cmd_ancestry/{ + s/^cmd_ancestry // + s/ <- / /g + s/\(.*'"$TT"'\) .*/\1/ + p + }' +} + +filter_ancestry_perf () { + sed -n '/cmd_ancestry/{ + s/.*ancestry:\[// + s/\]// + s/\(.*'"$TT"'\) .*/\1/ + p + }' +} + +filter_ancestry_event () { + sed -n '/"cmd_ancestry"/{ + s/.*"ancestry":\[// + s/\].*// + s/"//g + s/,/ /g + s/\(.*'"$TT"'\) .*/\1/ + p + }' +} + +# On Windows (MINGW) when running with the bin-wrappers, we also see "sh.exe" in +# the ancestry. We must therefore account for this expected ancestry element in +# the expected output of the tests. +if test_have_prereq MINGW && test -z "$no_bin_wrappers"; then + SH_TT="sh$X $TT" +else + SH_TT="$TT" +fi + +# Git alias resolution spawns the target command as a child process. +# Using "git -c alias.xyz=version xyz" creates a two-level chain: +# +# test-tool (400ancestry) +# -> git (resolves alias xyz -> version) +# -> git (version) +# +# Both git processes are instrumented and emit cmd_ancestry. After +# filtering out ancestors above test-tool, we get: +# +# test-tool (from git alias resolver) +# git test-tool (from git version) + +test_expect_success TRACE2_ANCESTRY 'normal: git alias chain, 2 levels' ' + test_when_finished "rm -f trace.normal actual expect" && + test-tool trace2 400ancestry normal "$(pwd)/trace.normal" \ + git -c alias.xyz=version xyz && + filter_ancestry_normal actual && + cat >expect <<-EOF && + $SH_TT + git$X $SH_TT + EOF + test_cmp expect actual +' + +test_expect_success TRACE2_ANCESTRY 'perf: git alias chain, 2 levels' ' + test_when_finished "rm -f trace.perf actual expect" && + test-tool trace2 400ancestry perf "$(pwd)/trace.perf" \ + git -c alias.xyz=version xyz && + filter_ancestry_perf actual && + cat >expect <<-EOF && + $SH_TT + git$X $SH_TT + EOF + test_cmp expect actual +' + +test_expect_success TRACE2_ANCESTRY 'event: git alias chain, 2 levels' ' + test_when_finished "rm -f trace.event actual expect" && + test-tool trace2 400ancestry event "$(pwd)/trace.event" \ + git -c alias.xyz=version xyz && + filter_ancestry_event actual && + cat >expect <<-EOF && + $SH_TT + git$X $SH_TT + EOF + test_cmp expect actual +' + +# Use 004child to add a test-tool layer, creating a three-level chain: +# +# test-tool (400ancestry) +# -> test-tool (004child) +# -> git (resolves alias xyz -> version) +# -> git (version) +# +# Three instrumented processes emit cmd_ancestry. After filtering: +# +# test-tool (from test-tool 004child) +# test-tool test-tool (from git alias resolver) +# git test-tool test-tool (from git version) + +test_expect_success TRACE2_ANCESTRY 'normal: deeper chain, 3 levels' ' + test_when_finished "rm -f trace.normal actual expect" && + test-tool trace2 400ancestry normal "$(pwd)/trace.normal" \ + test-tool trace2 004child \ + git -c alias.xyz=version xyz && + filter_ancestry_normal actual && + cat >expect <<-EOF && + $TT + $SH_TT $TT + git$X $SH_TT $TT + EOF + test_cmp expect actual +' + +test_done