From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.161.41 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793090332; b=fCKTVGQbFqDUmsmP747k0EZK3cl4w2ElWGWslJYa6Wp619DpM4YC4CbcXpDXUH3kFB6T XHfq3oPp+IC6n4CpqTJE6XyRq/Wwrcx1zRcetl52jAHAcL3GrJrWhsm0hTG2/nU0rekPu ft8mOdyiOrUlRS6RQ9uZSNbhz79tAIkEX69/OZFZlUHK09AwbtWb2nI0dzf5KZCX3b+MG CCopICZ29DueJJgjmS1hCMm86PIKH6lpx1w0KYj5iHzkvSWa6k53q4GUtBGsICK5efY2Y ILxZ+Oc5CfNktRnk5f/tqsDV0Cr6B+HfX+KhdaAE6wTUMNgDyZ4jBfpSplpWIBizJww== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793090332; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Gm-Message-State:X-Gm-Gg:X-Received:Received:Message-Id:In-Reply-To: References:From:Date:Subject:Fcc:Content-Type: Content-Transfer-Encoding:MIME-Version:To:Cc; bh=dyCzdSZE9byzCLwEptS/6nKXLiDwQxdxKs++6j/xEjs=; b=nnbJSUIm8Ll6yrOQm7gRUQh94kf2WOEpFeWQAiHHqVKle5l6HzTiRNjHQDgb4CzlII44 vCa5ijHPTfdGWQQzDrPyLtasqtBwya1Vy9OvcEUMAN49TLoBj3IFRT0HZj1yC9Bk+TtWD TpZy+qXOjK7XKBLp+HOkFzWvWPiPcX/eo+u/62rferoZ76bcjqN4iLap+CkK5hdSDSMtD mJWhYxcsceJ569fxTjZQDrpA4FiWLiyAswfcKPkKOaAcM98lExQtgscr4j90Mvev4Xgnf KOyZ/cK1WwvO1QOBrcMN596fsfygb8b4yYN4hbEwrq2rU0XfWeG18NCGbIjrV4IbApQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=FYmeozXH; arc=none smtp.remote-ip=209.85.161.41 Received: from mail-oo1-f41.google.com (mail-oo1-f41.google.com [209.85.161.41]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CC06C606A33F for ; Tue, 27 Oct 2026 08:38:51 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org CC06C606A33F Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=FYmeozXH Received: by mail-oo1-f41.google.com with SMTP id 006d021491bc7-6d7e06dbcccso2236332eaf.3 for ; Tue, 27 Oct 2026 01:38:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1793090331; x=1793695131; darn=vger.kernel.org; h=cc:to:mime-version:content-transfer-encoding:content-type:fcc :subject:date:from:references:in-reply-to:message-id:from:to:cc :subject:date:message-id:reply-to:content-type; bh=dyCzdSZE9byzCLwEptS/6nKXLiDwQxdxKs++6j/xEjs=; b=FYmeozXHJIe8dUV1gMUHiz/SmKO5gZ0sJmJ41IQAbauJJ0PYswu0cejTZgluDvH+Lb 58DVGEFqUGKIgwqSdCYX05Bxq/EAlgbkV2YAkXHEByzGgTqeoo99LFEoNX6+4O4xlRnR 3TqLLlKDT6YLusCH3ByHvLk2rf6/+mkxKTSxbh7KpsWY1QLcllPkscWZF1aVLeN/7ue0 gHBs788oK2ncphyNfF1HDl3/DxX9KfuXYVzpnb76bgWG2lRlDnAU5R/lGeTh8mavm6wU bfujVVbnfYDjR9W7DJlGeECrOR7txAj/R2midLEvaDeIeqYi2HMOPGdihgeG3aBKCGmO ykHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793090331; x=1793695131; h=cc:to:mime-version:content-transfer-encoding:content-type:fcc :subject:date:from:references:in-reply-to:message-id:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=dyCzdSZE9byzCLwEptS/6nKXLiDwQxdxKs++6j/xEjs=; b=IIUUBrU8ChdBl4spLN+nAFa9hQzcFnboOEzw8tVOx93aFH4NMhj1qrYN8hIHVK0eUg 1mbO9ktbg+pSui4Aa8G502I1epKVllPcuOIHp7Yh2rFl3bKs/4KBofYF2ITlzjEhZH93 km/7iIyB8lTBh9frKd2egwOymyPfRtJAxizm/jE7o+U74DbDsoGVwc1JHXCoAT7xwC6Y SuV024MjGcgGdrXd1KrC7Lo4l2VsWzJakdSIlWDMLuEaUuuY6+UTakzidDQONMuDN+Dm bBSB4ped2QKYXh7jiIsyxuhsX7NeP9DqzQA/aCN6NyZVWgq7WLoRvpg5NxT61UqHsdiE D0zg== X-Gm-Message-State: AFq9FYJxG8KWgLkIoQBlfNNczfVzRhsz7X1lYFghupUxJaQVmRezZTJx wfhJY7q7y1T3z7tJ1njBNLbZi2G4jAU1kcr5lqjyIr5UDvhY3HV6yCDTFQLmeybAzKE= X-Gm-Gg: AYBFou1pkEMz5UCVEXEK2jxSe4PJ2IGMoEnFFde6z/KUPj3hdEcwnC2KBa9RB9fdvRt fGsLE+2p92ReU7xrVWdeZLTjBrsCDcUQ/WiQlbSgRyWrxhcKVUFpGwCRokBkjb8w05Z8gFJXGNF OLdsmagZfLHcfduq2+EkRLfjB9K/0VEhKxEfpicxyEwQKaU6Im4cFwYpVqBadFHJgZ1yTvAN5Gu P1t+kIws4fFOE2B1dTsCkRiPC11lcakF0jCM+8eNK12BI5kv4hUCRTHt/WS/L8VXYMl2Rt/GaYG 0aspImsaZq7Bi7bsbSNZkRqAmUTQ/dJupo8ahSnkTK9lhDXc+emztK1nBFKSx8N34uvmbgt0XeH jANtAy4yWftq7ETTDFSvaI49r9fVKDS72mvE9D6TPttLBuzF+fcqNChKhihmTUIB1VUHJBJbELA /GMfTGHuTB/Ok/DFUfx2n5UspAnY3qL58MYVce4TNnfppdnHi3L5ItfXeF04jbRWKxjoCk02swb 3fW/YAFT1Fp2CtxUh4BWR+ivCGJP1oYel5vYuwVcaPYQ/JvVfL1gx/j X-Received: by 2002:a05:6820:1503:b0:6dd:9701:6305 with SMTP id 006d021491bc7-70ba0ec52b6mr5096034eaf.65.1793090330487; Tue, 27 Oct 2026 01:38:50 -0700 (PDT) Received: from [127.0.0.1] ([4.150.62.85]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-70e1dd8712bsm5236635eaf.1.2026.10.27.01.38.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 27 Oct 2026 01:38:49 -0700 (PDT) Message-Id: In-Reply-To: References: From: "Harald Nordgren via GitGitGadget" Date: Tue, 27 Oct 2026 08:38:45 +0000 Subject: [PATCH v3 0/2] reflog: add --operations and --historical-branch Fcc: Sent Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Mailing-List: git@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 To: git@vger.kernel.org Cc: Kristoffer Haugsbakk , "D. Ben Knoble" , Phillip Wood , Harald Nordgren Create a better reflog. Follows the discussion in https://lore.kernel.org/git/akZfm-igZKeHaDST@pks.im/. The reflog of HEAD records the work done on every branch, so "git reflog" mixes them and does not say which entry was made on which branch. This adds a "%gb" placeholder that names the branch HEAD was on when each entry was recorded: $ git log -g --format='%h %gd: (%gb) %gs' 9c8d7e6 HEAD@{0}: (topic) rebase (finish): returning to refs/heads/topic 1a2b3c4 HEAD@{2}: (topic) rebase (start): checkout main 1a2b3c4 HEAD@{3}: (main) commit: fix typo It follows branch switches, branch renames and rebases, including "git rebase ", rebases of a detached HEAD and rebases of a branch that was already up to date. When HEAD was detached, the entry shows "detached at ". Changes in v3: * Dropped --operations. * Replaced --historical-branch with a %gb placeholder for --format, e.g. git log -g --format='%h %gd: (%gb) %gs'. * %gb names the right branch after git rebase , a rebase of a detached HEAD and an up-to-date rebase. * Named the returning to and checkout reflog strings in reflog.h too. Changes in v2: * Move the "historical branch" rightward in the display for cleaner alignment. * Improve commit messages. Harald Nordgren (2): reflog: name the strings that mark switches, renames and rebase steps pretty: add %gb to name the branch of each reflog entry Documentation/pretty-formats.adoc | 3 + builtin/branch.c | 3 +- builtin/checkout.c | 3 +- builtin/rebase.c | 19 ++-- object-name.c | 3 +- pretty.c | 4 + reflog-walk.c | 156 ++++++++++++++++++++++++++++-- reflog-walk.h | 2 + reflog.h | 8 ++ sequencer.c | 13 +-- t/t1414-reflog-walk.sh | 122 +++++++++++++++++++++++ wt-status.c | 3 +- 12 files changed, 314 insertions(+), 25 deletions(-) base-commit: 7e34d8174dcda3435d1404c9a30bef46afe6c13e Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2448%2FHaraldNordgren%2Freflog-operations-v3 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2448/HaraldNordgren/reflog-operations-v3 Pull-Request: https://github.com/git/git/pull/2448 Range-diff vs v2: 1: 745929bd80 ! 1: 4d779e8da3 reflog: name the strings that mark switches, renames and rebase steps @@ Metadata ## Commit message ## reflog: name the strings that mark switches, renames and rebase steps - The next commit adds "git reflog --operations", which finds branch - switches, branch renames and the start and end of a rebase by the - messages they leave in the reflog. The commands that write these - messages spell out the strings by hand, and "@{-}" and "git status" - each keep their own copy of the switch prefix to read it back. Reading - them in one more place would add yet another copy that has to stay in - sync. + The next commit adds a "%gb" placeholder that shows which branch HEAD + was on for each reflog entry. It finds the branch from the messages + that branch switches, branch renames and rebases leave in the reflog. + The commands that write these messages spell out the strings by hand, + and "@{-}" and "git status" each keep their own copy of the switch + prefix to read it back. Reading them in one more place would add yet + another copy that has to stay in sync. Name the strings once in reflog.h and use the names wherever the messages are written or read: the "checkout: moving from " prefix written by "git checkout", the "Branch: renamed " prefix written by - "git branch -m", and the "start", "finish" and "abort" steps written - by "git rebase". + "git branch -m", and the "start", "finish" and "abort" steps, the + "returning to " before the branch a rebase goes back to and the + "checkout " before what it checks out, all written by "git rebase". No change in behavior. @@ builtin/rebase.c: static int move_to_original_branch(struct rebase_options *opts opts->head_name, oid_to_hex(&opts->onto->object.oid)); - strbuf_addf(&head_reflog, "%s (finish): returning to %s", - opts->reflog_action, opts->head_name); -+ strbuf_addf(&head_reflog, "%s (%s): returning to %s", ++ strbuf_addf(&head_reflog, "%s (%s): " REFLOG_RETURNING_TO "%s", + opts->reflog_action, REFLOG_STEP_FINISH, opts->head_name); ropts.branch = opts->head_name; ropts.flags = RESET_WORKING_TREE_REFS_ONLY | RESET_WORKING_TREE_UPDATE_HEAD; +@@ builtin/rebase.c: static int checkout_up_to_date(struct rebase_options *options) + struct reset_working_tree_options ropts = { 0 }; + int ret = 0; + +- strbuf_addf(&buf, "%s: checkout %s", ++ strbuf_addf(&buf, "%s: " REFLOG_CHECKOUT "%s", + options->reflog_action, options->switch_to); + ropts.oid = &options->orig_head->object.oid; + ropts.branch = options->head_name; @@ builtin/rebase.c: int cmd_rebase(int argc, if (read_basic_state(&options)) exit(1); - strbuf_addf(&head_msg, "%s (abort): returning to %s", - options.reflog_action, -+ strbuf_addf(&head_msg, "%s (%s): returning to %s", ++ strbuf_addf(&head_msg, "%s (%s): " REFLOG_RETURNING_TO "%s", + options.reflog_action, REFLOG_STEP_ABORT, options.head_name ? options.head_name : oid_to_hex(&options.orig_head->object.oid)); @@ builtin/rebase.c: int cmd_rebase(int argc, - strbuf_addf(&msg, "%s (start): checkout %s", - options.reflog_action, options.onto_name); -+ strbuf_addf(&msg, "%s (%s): checkout %s", options.reflog_action, -+ REFLOG_STEP_START, options.onto_name); ++ strbuf_addf(&msg, "%s (%s): " REFLOG_CHECKOUT "%s", ++ options.reflog_action, REFLOG_STEP_START, options.onto_name); ropts.oid = &options.onto->object.oid; ropts.orig_head = &options.orig_head->object.oid; ropts.flags = RESET_WORKING_TREE_DETACH | @@ reflog.h +#define REFLOG_STEP_START "start" +#define REFLOG_STEP_FINISH "finish" +#define REFLOG_STEP_ABORT "abort" ++#define REFLOG_RETURNING_TO "returning to " ++#define REFLOG_CHECKOUT "checkout " + #define REFLOG_EXPIRE_TOTAL (1 << 0) #define REFLOG_EXPIRE_UNREACH (1 << 1) @@ sequencer.c: static int checkout_onto(struct repository *r, struct replay_opts * - .head_msg = reflog_message(opts, "start", "checkout %s", - onto_name), + .head_msg = reflog_message(opts, REFLOG_STEP_START, -+ "checkout %s", onto_name), ++ REFLOG_CHECKOUT "%s", onto_name), .default_reflog_action = sequencer_reflog_action(opts) }; if (reset_working_tree(r, &ropts)) { @@ sequencer.c: cleanup_head_ref: - msg = reflog_message(opts, "finish", "returning to %s", - head_ref.buf); + msg = reflog_message(opts, REFLOG_STEP_FINISH, -+ "returning to %s", head_ref.buf); ++ REFLOG_RETURNING_TO "%s", head_ref.buf); if (refs_update_symref(get_main_ref_store(the_repository), "HEAD", head_ref.buf, msg)) { res = error(_("could not update HEAD to %s"), head_ref.buf); 2: 1c47ba225e < -: ---------- reflog: add --operations to show one entry per command 3: aa5a98b80d ! 2: dee548f9b1 reflog: add --historical-branch to name the branch of each entry @@ Metadata Author: Harald Nordgren ## Commit message ## - reflog: add --historical-branch to name the branch of each entry + pretty: add %gb to name the branch of each reflog entry - "git reflog" does not say which branch HEAD was on when an entry was - recorded. After switching between a few branches, telling which - commits and rebases belong to which branch means reading back to the - last "checkout: moving from" entry for each of them. Commit - decorations do not help either, since they only name branches that - still point at that commit, and a rebased or amended commit has none. + The reflog of HEAD records the work done on every branch, so the + output of "git reflog" mixes them and does not say which entry was + made on which branch. Working that out by hand means looking back for + the branch switch before each entry, and a rebase can move HEAD to + another branch without writing one, as "git rebase + " does. - Teach "git reflog" and "git log -g" a "--historical-branch" option - that shows the branch each entry was made on before its message, in - place of the refs that point at the commit now. The hashes and - selectors stay lined up in their columns, and the name is colored like - a branch decoration: + Add a "%gb" placeholder that names the branch HEAD was on when each + entry was recorded: - $ git reflog --historical-branch - 4e5f6a7 HEAD@{0}: (topic) commit: add tests - 1a2b3c4 HEAD@{1}: (topic) checkout: moving from main to topic - 8c9d0e1 HEAD@{2}: (main) commit: fix typo + $ git log -g --format='%h %gd: (%gb) %gs' + 9c8d7e6 HEAD@{0}: (topic) rebase (finish): returning to refs/heads/topic + 9c8d7e6 HEAD@{1}: (topic) rebase (pick): add tests + 1a2b3c4 HEAD@{2}: (topic) rebase (start): checkout main + 1a2b3c4 HEAD@{3}: (main) commit: fix typo + 5d6e7f8 HEAD@{4}: (main) checkout: moving from topic to main - Every entry gets a name, including ones whose commits are no longer on - any branch. Entries from before a branch was renamed show its old name, - and entries made on a detached HEAD show "detached at ". - Combined with "--operations", which leaves out the branch switches - themselves, it lists what was done to each branch. + The branch is read from the entries that branch switches, branch + renames and rebases leave behind. The entries of a rebase are named + after the branch being rebased. When HEAD was detached, the entry + shows "detached at " with the commit HEAD was at when it + moved on. When nothing older in the reflog says which branch a rebase + started on, the rebase is taken to have started on the branch it + rebased. For the reflog of any other ref, "%gb" is the name of that + ref. Signed-off-by: Harald Nordgren - ## Documentation/git-reflog.adoc ## -@@ Documentation/git-reflog.adoc: actions, and in addition the `HEAD` reflog records branch switching. - --pretty=oneline`; see linkgit:git-log[1] for more information. - With `--operations`, branch switches and other entries that change no - branch are left out, and a rebase is shown as a single entry instead --of one entry for each commit it replayed. -+of one entry for each commit it replayed. With `--historical-branch`, -+each entry shows the branch it was made on before its message. - - The "list" subcommand lists all refs which have a corresponding reflog. - + ## Documentation/pretty-formats.adoc ## +@@ Documentation/pretty-formats.adoc: endif::git-rev-list[] + +%gE+:: reflog identity email (respecting .mailmap, see + linkgit:git-shortlog[1] or linkgit:git-blame[1]) + +%gs+:: reflog subject +++%gb+:: reflog branch, the branch `HEAD` was on when the entry was ++ recorded, e.g., `topic` or `detached at 1a2b3c4`. For the reflog ++ of any other ref, this is the shortened refname. + ++%(trailers++`[: