From patchwork Fri Jun 3 13:37:49 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Derrick Stolee X-Patchwork-Id: 12869073 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1D9DAC43334 for ; Fri, 3 Jun 2022 13:38:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244681AbiFCNiH (ORCPT ); Fri, 3 Jun 2022 09:38:07 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54046 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229958AbiFCNh7 (ORCPT ); Fri, 3 Jun 2022 09:37:59 -0400 Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 70DDD1274A for ; Fri, 3 Jun 2022 06:37:57 -0700 (PDT) Received: by mail-wr1-x42d.google.com with SMTP id a15so1917262wrh.2 for ; Fri, 03 Jun 2022 06:37:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=C3VeTaSpsyjNjv445FZ5ATeKLyQuS87OAVtCJM+JQzE=; b=gwz9fhnrdoKLIUT0oAESv6azER0Z19kGSMr+5boJZslpIgxkPPe+xryMTjdtVu0Lw8 WJT+2bCl7NxIOf4EM3+YMelv8UhwIhuCCAerNDVD5MlP4n1ejAPD7bztPFshNc2KEvwn QNAzHtgmNpm5iIJ+ZhWKbXLOaHgS4e2lSD/e4maBrTHVfi0iFXAAsgdnQFAwVKLH6ipC HFJ3ecNVM/dr9HCyRTuI/w/hKHmpCP6HuagA0n0OxklI2rgxjMiLRnzCLKxaHcDy5FBc iZqiezvCscYWGKzEHSLDDJzSuq2AFrvDxbMif+ecvyLp8iHsjFOa6kL9YYOW2w4FctZZ HcEQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=C3VeTaSpsyjNjv445FZ5ATeKLyQuS87OAVtCJM+JQzE=; b=dGD6iqirJlv2A3YU8vnOAN4rbDwv87HPPSkmuDV9ivRNUhqTafGKntl+7gk6qoMnbW S73I+GQSScbCEikehcyHnJWG+x5bYVcLy1KpvTeob51FdXxkm87zYLRONW5zx4KwTPO2 pYWOzoI5iYSinrktgqkMARsZnJrnS659SZ2nAw0Z0vNTfXefrBckbMrXHKfSeX8EDek+ wEMrm4sQ45KQQP7uhUeAfL49+I8sx5aW5C66oxh/BpIz6SvuDRC0G59X7X63BsjxjxAb Pc7wkXNja920c1/0u8yY+zneUNNliC46qwmZow7pujyZWkSXBzAddVvKi/KEiTQFMIuY ZCkQ== X-Gm-Message-State: AOAM530bLj+ORrx7kgJ+PvBSK1XuLggrI0HlBEeVGu8DhMAyNgJmdyK+ 4zNNMGha/WJQ0kNFCgFdGsI+NO3/in+MxkbJ X-Google-Smtp-Source: ABdhPJw2f2bJrNlqgUr2Q4UNxjuqoKfbNjtYc2uxLt+3zRvQDbbpov4KdPeXK5oDxIPlj1nD3bAOuA== X-Received: by 2002:adf:fe52:0:b0:210:12ab:76e6 with SMTP id m18-20020adffe52000000b0021012ab76e6mr8402205wrs.120.1654263475466; Fri, 03 Jun 2022 06:37:55 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id u16-20020a5d4690000000b00213b5fb3798sm3381228wrq.15.2022.06.03.06.37.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 03 Jun 2022 06:37:54 -0700 (PDT) Message-Id: <4f9f34876413927d819313a70fcdefcad5b35689.1654263472.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Fri, 03 Jun 2022 13:37:49 +0000 Subject: [PATCH 1/4] log-tree: create for_each_decoration() Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: gitster@pobox.com, johannes.schindelin@gmx.de, me@ttaylorr.com, Derrick Stolee , Derrick Stolee Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Derrick Stolee From: Derrick Stolee It can be helpful to iterate through all the decorations on a commit without necessarily writing them to a stream. Implement for_each_decoration() and reimplement format_decorations_extended() to use that iterator. Signed-off-by: Derrick Stolee --- log-tree.c | 111 ++++++++++++++++++++++++++++++++++++----------------- log-tree.h | 4 ++ 2 files changed, 80 insertions(+), 35 deletions(-) diff --git a/log-tree.c b/log-tree.c index d0ac0a6327a..b15a7c9db22 100644 --- a/log-tree.c +++ b/log-tree.c @@ -282,6 +282,54 @@ static void show_name(struct strbuf *sb, const struct name_decoration *decoratio strbuf_addstr(sb, decoration->name); } +struct format_decorations_context { + struct strbuf *sb; + int use_color; + const char *prefix; + const char *separator; + const char *suffix; + const char *color_commit; + const char *color_reset; + const struct name_decoration *current_and_HEAD; +}; + +static int append_decoration(const struct name_decoration *d, + void *data) +{ + struct format_decorations_context *ctx = data; + /* + * When both current and HEAD are there, only + * show HEAD->current where HEAD would have + * appeared, skipping the entry for current. + */ + if (d != ctx->current_and_HEAD) { + strbuf_addstr(ctx->sb, ctx->color_commit); + strbuf_addstr(ctx->sb, ctx->prefix); + strbuf_addstr(ctx->sb, ctx->color_reset); + strbuf_addstr(ctx->sb, decorate_get_color(ctx->use_color, d->type)); + if (d->type == DECORATION_REF_TAG) + strbuf_addstr(ctx->sb, "tag: "); + + show_name(ctx->sb, d); + + if (ctx->current_and_HEAD && + d->type == DECORATION_REF_HEAD) { + strbuf_addstr(ctx->sb, " -> "); + strbuf_addstr(ctx->sb, ctx->color_reset); + strbuf_addstr(ctx->sb, + decorate_get_color( + ctx->use_color, + ctx->current_and_HEAD->type)); + show_name(ctx->sb, ctx->current_and_HEAD); + } + strbuf_addstr(ctx->sb, ctx->color_reset); + + ctx->prefix = ctx->separator; + } + + return 0; +} + /* * The caller makes sure there is no funny color before calling. * format_decorations_extended makes sure the same after return. @@ -294,49 +342,42 @@ void format_decorations_extended(struct strbuf *sb, const char *suffix) { const struct name_decoration *decoration; - const struct name_decoration *current_and_HEAD; - const char *color_commit = - diff_get_color(use_color, DIFF_COMMIT); - const char *color_reset = - decorate_get_color(use_color, DECORATION_NONE); + struct format_decorations_context ctx = { + .sb = sb, + .use_color = use_color, + .prefix = prefix, + .separator = separator, + .suffix = suffix, + .color_commit = diff_get_color(use_color, DIFF_COMMIT), + .color_reset = decorate_get_color(use_color, DECORATION_NONE), + }; decoration = get_name_decoration(&commit->object); if (!decoration) return; - current_and_HEAD = current_pointed_by_HEAD(decoration); - while (decoration) { - /* - * When both current and HEAD are there, only - * show HEAD->current where HEAD would have - * appeared, skipping the entry for current. - */ - if (decoration != current_and_HEAD) { - strbuf_addstr(sb, color_commit); - strbuf_addstr(sb, prefix); - strbuf_addstr(sb, color_reset); - strbuf_addstr(sb, decorate_get_color(use_color, decoration->type)); - if (decoration->type == DECORATION_REF_TAG) - strbuf_addstr(sb, "tag: "); - - show_name(sb, decoration); - - if (current_and_HEAD && - decoration->type == DECORATION_REF_HEAD) { - strbuf_addstr(sb, " -> "); - strbuf_addstr(sb, color_reset); - strbuf_addstr(sb, decorate_get_color(use_color, current_and_HEAD->type)); - show_name(sb, current_and_HEAD); - } - strbuf_addstr(sb, color_reset); + ctx.current_and_HEAD = current_pointed_by_HEAD(decoration); - prefix = separator; - } + for_each_decoration(commit, append_decoration, &ctx); + + strbuf_addstr(sb, ctx.color_commit); + strbuf_addstr(sb, ctx.suffix); + strbuf_addstr(sb, ctx.color_reset); +} + +int for_each_decoration(const struct commit *c, decoration_fn fn, void *data) +{ + const struct name_decoration *decoration; + + decoration = get_name_decoration(&c->object); + while (decoration) { + int res; + if ((res = fn(decoration, data))) + return res; decoration = decoration->next; } - strbuf_addstr(sb, color_commit); - strbuf_addstr(sb, suffix); - strbuf_addstr(sb, color_reset); + + return 0; } void show_decorations(struct rev_info *opt, struct commit *commit) diff --git a/log-tree.h b/log-tree.h index e7e4641cf83..ea07da2625b 100644 --- a/log-tree.h +++ b/log-tree.h @@ -35,4 +35,8 @@ void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *); void fmt_output_subject(struct strbuf *, const char *subject, struct rev_info *); void fmt_output_email_subject(struct strbuf *, struct rev_info *); +typedef int decoration_fn(const struct name_decoration *d, + void *data); +int for_each_decoration(const struct commit *c, decoration_fn fn, void *data); + #endif From patchwork Fri Jun 3 13:37:50 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Derrick Stolee X-Patchwork-Id: 12869072 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 96DDEC433EF for ; Fri, 3 Jun 2022 13:38:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244673AbiFCNiG (ORCPT ); Fri, 3 Jun 2022 09:38:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54048 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S244662AbiFCNh7 (ORCPT ); Fri, 3 Jun 2022 09:37:59 -0400 Received: from mail-wr1-x435.google.com (mail-wr1-x435.google.com [IPv6:2a00:1450:4864:20::435]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 651641276B for ; Fri, 3 Jun 2022 06:37:58 -0700 (PDT) Received: by mail-wr1-x435.google.com with SMTP id q7so10453108wrg.5 for ; Fri, 03 Jun 2022 06:37:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=GZXWE3ow9ADaKIj0DAE4htB4Ma746HVMJhy/AKVLrMg=; b=e//ND44HNBhyMeCqb9e67+f+0zQ5jK9bfjfatYRsnXIomigcD6ZbRMLviA/iNHfh/B Vfbm/VPA+V5vrGhlu/xDsaoOs80JTCjWZba9iyUPzS51SVVYZ2KBWT3+QcU3vrf2MaLs +xAG6eloO1YIdEpwL01iW+Ho7ZobK7AkBhdBs+4Mt2ePHDxaM90RgN3ImjgEwKlU9VF6 jUFG+9DnTr5yWLa+fGkRud66qB+UHn6gR5MAjGu8rnsVsovEQ2Zce2Z1lIlCcPaXXNWg GZzDmDkb33RPBIZPp7l7ZSnsL4jXx0UyZeORM/PZ225WyCisM90wtwEtxhVubPYG9t9r 579g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=GZXWE3ow9ADaKIj0DAE4htB4Ma746HVMJhy/AKVLrMg=; b=UCMtgsVu1SI8gqdfonj4kYkpr7UBwbRvWy+RJRT+DztLgk4+mWQeWa0itcldCQ2/4h PLTrrBAAe18nx/6v2NxiH9L2qWZgFUTa8Rr0ZfP/uOggTA31GbRWQPm7eVoccHeyfegD O10Cg80nLKWcjLha3lyiuGZtV+EBaFe3yuNV4QSinudUSZ0vh3ZubMSIym9PtlFfJH2S iq4ZnQ5LogQsIjLhbW+l3zquzVW2pq4sRJJfLvDOs1MBBOkUrYm2bYBAFEgxx211GRrQ FMQ4nggeFjVdQi3x44pAZjWpL19GrDNkArkxXPVTxNWJjwvwpcL/C15N5CO1KTxYG8Hq wPug== X-Gm-Message-State: AOAM5304alfmyu26H98tTwWfPPQ2cL0LDvRW6CH18YxPokNYrTggaWvd xF7hOQ01v4iO7EwRlBgc9q4vOPVVxRkQ6ZDT X-Google-Smtp-Source: ABdhPJwU9X9NZ3NjPnNF+k7cZb7JMOnGuv1OpeJvUCtrdLR4bxNYsA6s2dZisHVztwv5iygSVhov1g== X-Received: by 2002:a5d:5903:0:b0:210:316f:7f40 with SMTP id v3-20020a5d5903000000b00210316f7f40mr8329157wrd.624.1654263476663; Fri, 03 Jun 2022 06:37:56 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id k14-20020a5d518e000000b0021350f7b22esm4522458wrv.109.2022.06.03.06.37.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 03 Jun 2022 06:37:56 -0700 (PDT) Message-Id: <5f54766e1032ebf3a331516a6dd696b997bdfdd8.1654263472.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Fri, 03 Jun 2022 13:37:50 +0000 Subject: [PATCH 2/4] branch: add branch_checked_out() helper Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: gitster@pobox.com, johannes.schindelin@gmx.de, me@ttaylorr.com, Derrick Stolee , Derrick Stolee Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Derrick Stolee From: Derrick Stolee The validate_new_branchname() method contains a check to see if a branch is checked out in any non-bare worktree. This is intended to prevent a force push that will mess up an existing checkout. This helper is not suitable to performing just that check, because the method will die() when the branch is checked out instead of returning an error code. Extract branch_checked_out() and use it within validate_new_branchname(). Another caller will be added in a coming change. Signed-off-by: Derrick Stolee --- branch.c | 24 ++++++++++++++++-------- branch.h | 8 ++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/branch.c b/branch.c index 2d6569b0c62..2e6419cdfa5 100644 --- a/branch.c +++ b/branch.c @@ -369,6 +369,19 @@ int validate_branchname(const char *name, struct strbuf *ref) return ref_exists(ref->buf); } +int branch_checked_out(const char *refname, char **path) +{ + struct worktree **worktrees = get_worktrees(); + const struct worktree *wt = find_shared_symref(worktrees, "HEAD", refname); + int result = wt && !wt->is_bare; + + if (result && path) + *path = xstrdup(wt->path); + + free_worktrees(worktrees); + return result; +} + /* * Check if a branch 'name' can be created as a new branch; die otherwise. * 'force' can be used when it is OK for the named branch already exists. @@ -377,9 +390,7 @@ int validate_branchname(const char *name, struct strbuf *ref) */ int validate_new_branchname(const char *name, struct strbuf *ref, int force) { - struct worktree **worktrees; - const struct worktree *wt; - + char *path; if (!validate_branchname(name, ref)) return 0; @@ -387,13 +398,10 @@ int validate_new_branchname(const char *name, struct strbuf *ref, int force) die(_("a branch named '%s' already exists"), ref->buf + strlen("refs/heads/")); - worktrees = get_worktrees(); - wt = find_shared_symref(worktrees, "HEAD", ref->buf); - if (wt && !wt->is_bare) + if (branch_checked_out(ref->buf, &path)) die(_("cannot force update the branch '%s' " "checked out at '%s'"), - ref->buf + strlen("refs/heads/"), wt->path); - free_worktrees(worktrees); + ref->buf + strlen("refs/heads/"), path); return 1; } diff --git a/branch.h b/branch.h index 560b6b96a8f..5ea93d217b1 100644 --- a/branch.h +++ b/branch.h @@ -101,6 +101,14 @@ void create_branches_recursively(struct repository *r, const char *name, const char *tracking_name, int force, int reflog, int quiet, enum branch_track track, int dry_run); + +/* + * Returns true if the branch at 'refname' is checked out at any + * non-bare worktree. The path of the worktree is stored in the + * given 'path', if provided. + */ +int branch_checked_out(const char *refname, char **path); + /* * Check if 'name' can be a valid name for a branch; die otherwise. * Return 1 if the named branch already exists; return 0 otherwise. From patchwork Fri Jun 3 13:37:51 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Derrick Stolee X-Patchwork-Id: 12869074 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0CCB7C433EF for ; Fri, 3 Jun 2022 13:38:10 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244675AbiFCNiJ (ORCPT ); Fri, 3 Jun 2022 09:38:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54270 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S244670AbiFCNiF (ORCPT ); Fri, 3 Jun 2022 09:38:05 -0400 Received: from mail-wr1-x42f.google.com (mail-wr1-x42f.google.com [IPv6:2a00:1450:4864:20::42f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D130512D28 for ; Fri, 3 Jun 2022 06:37:59 -0700 (PDT) Received: by mail-wr1-x42f.google.com with SMTP id t6so10461612wra.4 for ; Fri, 03 Jun 2022 06:37:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=LEsswD3junsSh5IVs1Gc/4QAldD5XQcbwI2OtZ/tiXU=; b=ZcByN87XYzfuL7Re2PDs+WDpoDKFGxB5mkXICsuauQhkWVvoNLY0Ry1z7O2g5iXJOb o6fO5UoWqde6P4NGPU4AeYxmQq2ZB415MIp5saQVUdNZVU84zUe5HNbxT6T6HirZ3Z78 toMrGOdBzXerUfmiXbPRV2w14bKao6qq8+2qSyiRhB3veCbgRoPlFdW8vZLiKDKT1M9d sE/6+cEaulPAIll+9jeEs4spzKRNWGm4F4IyuDZFAKD+xx1f+5/5HevlhkpGEz+uIkKN JgFBREC0ryVTNPj1OvNCGnJoYeIYy9OZ42j4uW2cOshDyf/VOhnb6gtD04x/sCvWLI0B k1Bg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=LEsswD3junsSh5IVs1Gc/4QAldD5XQcbwI2OtZ/tiXU=; b=Hx35GjjkJYH6H8y2jvF6R+RNSKCqBxhnZA5OG71qqAt/mPhhLxpvcl6T8EI5I1q+s+ OIZLr9xrlwrrq2X8FBEnKF2gb3u+7VYxi25ihk8O+b4MuqocBfoJiBsIJHQ6xhRZdY31 iMFgGpH4GXiv8F8xC3CXZlNh8qI+ujSApzl57/tpdMy+gs3xz4KVEdJVTIdjArc4bt9X JvIB+eMW44zdMDR+2q9pKqFHRFvus5iQKnbYUvIX6guxQxvAp8M1UAchqFltii1HlZlL IwQIU+EBHCrXIAI7vA2PQ7CctU8KdDFXWhUUb5Pox/075917f8XtQBnSELbbhY6bndB0 IsUA== X-Gm-Message-State: AOAM530Hn0glStS5PaBC9RD4M3dKddSzmYgOE4jh1WCvdW6rg4IPxS/m nxwUCqf4UOLhcVWZf76R+aupm+M8lSdB8qez X-Google-Smtp-Source: ABdhPJzfRy0CV3CjIKSbgUlXpyWgZKkdhMc9svNxmKnAhKiBzmSLnmZ3mmw5nLxXVmiipleTZvy+FA== X-Received: by 2002:adf:f750:0:b0:210:2ef5:7c5c with SMTP id z16-20020adff750000000b002102ef57c5cmr8063632wrp.416.1654263477758; Fri, 03 Jun 2022 06:37:57 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id t10-20020a7bc3ca000000b003942a244f47sm11548231wmj.32.2022.06.03.06.37.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 03 Jun 2022 06:37:57 -0700 (PDT) Message-Id: <23fa663886405596370e5fc95f679ef299f0725c.1654263472.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Fri, 03 Jun 2022 13:37:51 +0000 Subject: [PATCH 3/4] rebase: add --update-refs option Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: gitster@pobox.com, johannes.schindelin@gmx.de, me@ttaylorr.com, Derrick Stolee , Derrick Stolee Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Derrick Stolee From: Derrick Stolee When working on a large feature, it can be helpful to break that feature into multiple smaller parts that become reviewed in sequence. During development or during review, a change to one part of the feature could affect multiple of these parts. An interactive rebase can help adjust the multi-part "story" of the branch. However, if there are branches tracking the different parts of the feature, then rebasing the entire list of commits can create commits not reachable from those "sub branches". It can take a manual step to update those branches. Add a new --update-refs option to 'git rebase -i' that adds 'git update-ref' exec steps to the todo file whenever a commit that is being rebased is decorated with that . This allows the user to rebase a long list of commits in a multi-part feature and keep all of their pointers to those parts. Use the new for_each_decoration() while iterating over the existing todo list. Be sure to iterate after any squashing or fixups are placed. Update the branch only after those squashes and fixups are complete. This allows a --fixup commit at the tip of the feature to apply correctly to the sub branch, even if it is fixing up the most-recent commit in that part. One potential problem here is that refs decorating commits that are already marked as "fixup!" or "squash!" will not be included in this list. Generally, the reordering of the "fixup!" and "squash!" is likely to change the relative order of these refs, so it is not recommended. The workflow here is intended to allow these kinds of commits at the tip of the rebased branch while the other sub branches come along for the ride without intervention. Be careful to not attempt updating any branch that is checked out. The most common example is the branch being rebased is checked out and decorates the tip commit. If the user is rebasing commits reachable from a different branch that is checked out in a different worktree, then they may be surprised to not see that ref update. However, it's probably best to not optimize for this scenario and do the safest thing that will result in a successful rebase. A comment is left in the TODO list that signals that these refs are currently checked out. Signed-off-by: Derrick Stolee --- Documentation/git-rebase.txt | 7 +++ builtin/rebase.c | 5 ++ sequencer.c | 99 +++++++++++++++++++++++++++++++++++ sequencer.h | 1 + t/t3404-rebase-interactive.sh | 29 ++++++++++ 5 files changed, 141 insertions(+) diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 262fb01aec0..866554fc978 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -609,6 +609,13 @@ provided. Otherwise an explicit `--no-reschedule-failed-exec` at the start would be overridden by the presence of `rebase.rescheduleFailedExec=true` configuration. +--update-refs:: +--no-update-refs:: + Automatically force-update any branches that point to commits that + are being rebased. Any branches that are checked out in a worktree + or point to a `squash! ...` or `fixup! ...` commit are not updated + in this way. + INCOMPATIBLE OPTIONS -------------------- diff --git a/builtin/rebase.c b/builtin/rebase.c index 7ab50cda2ad..56d82a52106 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -102,6 +102,7 @@ struct rebase_options { int reschedule_failed_exec; int reapply_cherry_picks; int fork_point; + int update_refs; }; #define REBASE_OPTIONS_INIT { \ @@ -298,6 +299,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) ret = complete_action(the_repository, &replay, flags, shortrevisions, opts->onto_name, opts->onto, &opts->orig_head, &commands, opts->autosquash, + opts->update_refs, &todo_list); } @@ -1124,6 +1126,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "autosquash", &options.autosquash, N_("move commits that begin with " "squash!/fixup! under -i")), + OPT_BOOL(0, "update-refs", &options.update_refs, + N_("update local refs that point to commits " + "that are being rebased")), { OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"), N_("GPG-sign commits"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, diff --git a/sequencer.c b/sequencer.c index 8c3ed3532ac..d6151af9849 100644 --- a/sequencer.c +++ b/sequencer.c @@ -35,6 +35,8 @@ #include "commit-reach.h" #include "rebase-interactive.h" #include "reset.h" +#include "branch.h" +#include "log-tree.h" #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -5603,10 +5605,104 @@ static int skip_unnecessary_picks(struct repository *r, return 0; } +struct todo_add_branch_context { + struct todo_list new_list; + struct strbuf *buf; + struct commit *commit; +}; + +static int add_branch_for_decoration(const struct name_decoration *d, void *data) +{ + struct todo_add_branch_context *ctx = data; + size_t base_offset = ctx->buf->len; + int i = ctx->new_list.nr; + struct todo_item *item; + char *path; + + ALLOC_GROW(ctx->new_list.items, + ctx->new_list.nr + 1, + ctx->new_list.alloc); + item = &ctx->new_list.items[i]; + + /* If the branch is checked out, then leave a comment instead. */ + if (branch_checked_out(d->name, &path)) { + item->command = TODO_COMMENT; + strbuf_addf(ctx->buf, "# Ref %s checked out at '%s'\n", + d->name, path); + free(path); + } else { + item->command = TODO_EXEC; + strbuf_addf(ctx->buf, "git update-ref %s HEAD %s\n", + d->name, oid_to_hex(&ctx->commit->object.oid)); + } + + item->commit = NULL; + item->offset_in_buf = base_offset; + item->arg_offset = base_offset; + item->arg_len = ctx->buf->len - base_offset; + ctx->new_list.nr++; + + return 0; +} + +/* + * For each 'pick' command, find out if the commit has a decoration in + * refs/heads/. If so, then add a 'git branch -f' exec command after + * that 'pick' (plus any following 'squash' or 'fixup' commands). + */ +static int todo_list_add_branch_commands(struct todo_list *todo_list) +{ + int i; + static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP; + static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP; + static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP; + struct decoration_filter decoration_filter = { + .include_ref_pattern = &decorate_refs_include, + .exclude_ref_pattern = &decorate_refs_exclude, + .exclude_ref_config_pattern = &decorate_refs_exclude_config + }; + struct todo_add_branch_context ctx = { + .new_list = TODO_LIST_INIT, + .buf = &todo_list->buf, + }; + + string_list_append(&decorate_refs_include, "refs/heads/"); + load_ref_decorations(&decoration_filter, 0); + + for (i = 0; i < todo_list->nr; ) { + struct todo_item *item = &todo_list->items[i]; + + do { + /* insert ith item into new list */ + ALLOC_GROW(ctx.new_list.items, + ctx.new_list.nr + 1, + ctx.new_list.alloc); + + memcpy(&ctx.new_list.items[ctx.new_list.nr++], + &todo_list->items[i], + sizeof(struct todo_item)); + + i++; + } while (i < todo_list->nr && + todo_list->items[i].command != TODO_PICK); + + ctx.commit = item->commit; + for_each_decoration(item->commit, add_branch_for_decoration, &ctx); + } + + free(todo_list->items); + todo_list->items = ctx.new_list.items; + todo_list->nr = ctx.new_list.nr; + todo_list->alloc = ctx.new_list.alloc; + + return 0; +} + int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags, const char *shortrevisions, const char *onto_name, struct commit *onto, const struct object_id *orig_head, struct string_list *commands, unsigned autosquash, + unsigned keep_decorations, struct todo_list *todo_list) { char shortonto[GIT_MAX_HEXSZ + 1]; @@ -5628,6 +5724,9 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla if (autosquash && todo_list_rearrange_squash(todo_list)) return -1; + if (keep_decorations && todo_list_add_branch_commands(todo_list)) + return -1; + if (commands->nr) todo_list_add_exec_commands(todo_list, commands); diff --git a/sequencer.h b/sequencer.h index da64473636b..1add3c7119f 100644 --- a/sequencer.h +++ b/sequencer.h @@ -166,6 +166,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla const char *shortrevisions, const char *onto_name, struct commit *onto, const struct object_id *orig_head, struct string_list *commands, unsigned autosquash, + unsigned keep_decorations, struct todo_list *todo_list); int todo_list_rearrange_squash(struct todo_list *todo_list); diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index f31afd4a547..38c7ef95e0e 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -1743,6 +1743,35 @@ test_expect_success 'ORIG_HEAD is updated correctly' ' test_cmp_rev ORIG_HEAD test-orig-head@{1} ' +test_expect_success '--update-refs adds git branch commands' ' + git checkout -b update-refs no-conflict-branch && + test_commit extra fileX && + git commit --amend --fixup=L && + ( + set_cat_todo_editor && + git branch -f base A && + git branch -f first J && + git branch -f second J && + git branch -f third L && + + test_must_fail git rebase -i --autosquash --update-refs primary >todo && + + cat >expect <<-EOF && + pick $(git log -1 --format=%h J) J + exec git update-ref refs/heads/second HEAD $(git rev-parse J) + exec git update-ref refs/heads/first HEAD $(git rev-parse J) + pick $(git log -1 --format=%h K) K + pick $(git log -1 --format=%h L) L + fixup $(git log -1 --format=%h update-refs) fixup! L + exec git update-ref refs/heads/third HEAD $(git rev-parse L) + pick $(git log -1 --format=%h M) M + exec git update-ref refs/heads/no-conflict-branch HEAD $(git rev-parse M) + EOF + + test_cmp expect todo + ) +' + # This must be the last test in this file test_expect_success '$EDITOR and friends are unchanged' ' test_editor_unchanged From patchwork Fri Jun 3 13:37:52 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Derrick Stolee X-Patchwork-Id: 12869075 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4C0E7C433EF for ; Fri, 3 Jun 2022 13:38:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244616AbiFCNiK (ORCPT ); Fri, 3 Jun 2022 09:38:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54156 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S244669AbiFCNiB (ORCPT ); Fri, 3 Jun 2022 09:38:01 -0400 Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0164912D38 for ; Fri, 3 Jun 2022 06:37:59 -0700 (PDT) Received: by mail-wm1-x332.google.com with SMTP id r129so4106552wmr.3 for ; Fri, 03 Jun 2022 06:37:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=ttWhpEGetrPXdh5WVpHkNj8LET5/tVmBOlacKztqzp4=; b=inpka5pGBaMND7BxSY3FScTvoc2y5tEZg4HGStBvxcgu7zze6HthJ1+gPF65GtARom ZBLFAJV9ByJ0zAYLaKgJTbUQPwtlu3rRjAVaEKuBC4pg8Fj7nERT5AglKg+Y6hpXVFgt 8cSOKWXkK51ZDSuutq5dXfm5BAH4yvMDPbY+7vu4LV1hE5pnUS4UvdoVkFVFLMs4K4+H H/PV6Y0usChKr3H95NNSromNuY/bxUMUszeTPqFOXDMcV+m70aUtJbaU39KO4bJzc1WR GT5yvyQp45dzrz/vxE7trDl00PBJ1/y+Hsj6h1VvHM14hDb7RuAhRoNYrntgm0HX4lnR 1Rcw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=ttWhpEGetrPXdh5WVpHkNj8LET5/tVmBOlacKztqzp4=; b=wPM/T3sFqqJlW+9ot7Bb6IUUNQjhpxX+G/NBeid/O7leDf0v8oeNh5Dr5Uj/RFHZGg CXRF6+U8wTmDR/7xaoeMtlwfFw6li5aeIsEk2hDwQeOjMCuc+LSwJjaHN3mIJGv+QKd7 SX758O/Q120zn9XG4RrCL56/tI92ExmUXZLk6xAcufTnFZr/uzCZ4HOQqE+RXjp609S7 hGIFOn1uMyF8XNw2eXMCxTcSyycIvOzXpBku17pCTx1UVzO0fbbItyvkkw+jjvtkvOc5 UURVtUzUMK+hZ5ub9idUwmWKmKS01nis7dwca0yJeG47bvik4e7a/qMFaDDwubDy3nbt HswQ== X-Gm-Message-State: AOAM530c/FDRnODAkmiQEDPaZGbdbvo/ouVS7p/y7bRuUv8PtFik0Yy6 F8KzQ+09vWPW/hAdogaHXAXkUO0EwGfhs6i+ X-Google-Smtp-Source: ABdhPJxB0jtIl8+3+y8VX+ufQjUETaqJwvn5qUIiSl6juPhMgLFBp0QSrD4D+zcDw/kAjxUGKrZRlw== X-Received: by 2002:a05:600c:3646:b0:397:326d:eac7 with SMTP id y6-20020a05600c364600b00397326deac7mr8919981wmq.43.1654263478868; Fri, 03 Jun 2022 06:37:58 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id f22-20020a1c6a16000000b003942a244eebsm8010635wmc.48.2022.06.03.06.37.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 03 Jun 2022 06:37:58 -0700 (PDT) Message-Id: In-Reply-To: References: Date: Fri, 03 Jun 2022 13:37:52 +0000 Subject: [PATCH 4/4] rebase: add rebase.updateRefs config option Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: gitster@pobox.com, johannes.schindelin@gmx.de, me@ttaylorr.com, Derrick Stolee , Derrick Stolee Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Derrick Stolee From: Derrick Stolee The previous change added the --update-refs command-line option. For users who always want this mode, create the rebase.updateRefs config option which behaves the same way as rebase.autoSquash does with the --autosquash option. Signed-off-by: Derrick Stolee --- Documentation/config/rebase.txt | 3 +++ Documentation/git-rebase.txt | 4 ++++ builtin/rebase.c | 5 +++++ t/t3404-rebase-interactive.sh | 5 +++++ 4 files changed, 17 insertions(+) diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt index 8c979cb20f2..f19bd0e0407 100644 --- a/Documentation/config/rebase.txt +++ b/Documentation/config/rebase.txt @@ -21,6 +21,9 @@ rebase.autoStash:: `--autostash` options of linkgit:git-rebase[1]. Defaults to false. +rebase.updateRefs:: + If set to true enable `--update-refs` option by default. + rebase.missingCommitsCheck:: If set to "warn", git rebase -i will print a warning if some commits are removed (e.g. a line was deleted), however the diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 866554fc978..cb1b498bd99 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -615,6 +615,10 @@ start would be overridden by the presence of are being rebased. Any branches that are checked out in a worktree or point to a `squash! ...` or `fixup! ...` commit are not updated in this way. ++ +If the `--update-refs` option is enabled by default using the +configuration variable `rebase.updateRefs`, this option can be +used to override and disable this setting. INCOMPATIBLE OPTIONS -------------------- diff --git a/builtin/rebase.c b/builtin/rebase.c index 56d82a52106..8ebc98ea505 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -802,6 +802,11 @@ static int rebase_config(const char *var, const char *value, void *data) return 0; } + if (!strcmp(var, "rebase.updaterefs")) { + opts->update_refs = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "rebase.reschedulefailedexec")) { opts->reschedule_failed_exec = git_config_bool(var, value); return 0; diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 38c7ef95e0e..1fa9f78d40d 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -1768,6 +1768,11 @@ test_expect_success '--update-refs adds git branch commands' ' exec git update-ref refs/heads/no-conflict-branch HEAD $(git rev-parse M) EOF + test_cmp expect todo && + + test_must_fail git -c rebase.autosquash=true \ + -c rebase.updaterefs=true \ + rebase -i primary >todo && test_cmp expect todo ) '