From mboxrd@z Thu Jan 1 00:00:00 1970 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 D13EAEC874B for ; Thu, 7 Sep 2023 18:20:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235585AbjIGSUx (ORCPT ); Thu, 7 Sep 2023 14:20:53 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46918 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231728AbjIGSUw (ORCPT ); Thu, 7 Sep 2023 14:20:52 -0400 Received: from mail-ed1-x52a.google.com (mail-ed1-x52a.google.com [IPv6:2a00:1450:4864:20::52a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9A1E2A1 for ; Thu, 7 Sep 2023 11:20:46 -0700 (PDT) Received: by mail-ed1-x52a.google.com with SMTP id 4fb4d7f45d1cf-52683da3f5cso1693705a12.3 for ; Thu, 07 Sep 2023 11:20:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1694110845; x=1694715645; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=rJrJWQDU2m/w6E3SJI2I77wg3wEKZd/lVjpGH3femMg=; b=TUdSGPnkv6Gk+3ZmRQhiiBM1me/Hp3AWW8O0hy0z1weMx1nD0Q/44N5UiYG47mPbgt JsrF06azCjf5+wufX45uQsAE44kg07xybX3vWf3PQ7S5uHLkZ5nx06dd6XhWUKzI2SDO dKiFh0NEUzcupYScl+fWfYppyl4rASHH1Tdfvp1mRqdlLwLOB/TcS+/6VUDMenUUON3n qfEj7n274x9C631YJQHjWbeU7jo9p0ZaXG+J/C7MhB9/ECA1WZytGP0lFwVubOhtRcFX GM67+KcrXLpTYB6kNjI9ss0bFnJAx0+86pUVJDI3zjm6H64Rq74SuSvHdKx0HIangvi3 sHqQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1694110845; x=1694715645; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=rJrJWQDU2m/w6E3SJI2I77wg3wEKZd/lVjpGH3femMg=; b=qh5opwAcY1jVWB2XUvlU5JLK/op3YPjOZpnc4z5+ct4HSTAO8Pvaa0ZgA5EHu3eBOV JPEOea1xpcGymQIoX6adVQ2Dlf4tEoEWNt4LMDAFoeHIW2nGQqcMv7doQJNuvfeHBpFA PAcp7g0TlzzP5iZkBDZdE6n2t19BTYW8d0vuoIqcQ4wmnntjC0N3BYoAYg63QjukDTfv YLhgdyIq5pfpmg4me3TnAIPqiK40ptg3ptOU6sWXX7ZboXpKNCVTjkc7rCVl1sBdH15C XHhT7QH5KrJA8qR1lNgs+af/a4s6kl5uXOpWNjrk2X8qq/WN+WuNFP9mjFk/TMFwjcUH aVcw== X-Gm-Message-State: AOJu0YyZEm9Vt/r5/5EwsIskBO6SkxBTD4//m9PvUqHgz9vO+ZFgtOMi ahmiFUnu88lC/z2aPU4+Vvs1huiD1uM= X-Google-Smtp-Source: AGHT+IGyRTMh/iiNkDZCrz/RmNESnCaA6Hp/HGPwSmCE5gGDfEN++aiQRxYTPtIzbsVOT1cU4FMfiw== X-Received: by 2002:a5d:650c:0:b0:317:5e5e:60e0 with SMTP id x12-20020a5d650c000000b003175e5e60e0mr4397640wru.28.1694078979788; Thu, 07 Sep 2023 02:29:39 -0700 (PDT) Received: from localhost.localdomain ([2001:861:3f04:7ca0:3385:ce2d:69dd:303e]) by smtp.gmail.com with ESMTPSA id y8-20020adff148000000b00317c742ca9asm22491522wro.43.2023.09.07.02.29.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 07 Sep 2023 02:29:39 -0700 (PDT) From: Christian Couder To: git@vger.kernel.org Cc: Junio C Hamano , Patrick Steinhardt , Johannes Schindelin , Elijah Newren , John Cai , Derrick Stolee , Phillip Wood , Calvin Wan , Toon Claes , Christian Couder Subject: [PATCH v4 15/15] replay: stop assuming replayed branches do not diverge Date: Thu, 7 Sep 2023 11:25:21 +0200 Message-ID: <20230907092521.733746-16-christian.couder@gmail.com> X-Mailer: git-send-email 2.42.0.126.gcf8c984877 In-Reply-To: <20230907092521.733746-1-christian.couder@gmail.com> References: <20230602102533.876905-1-christian.couder@gmail.com> <20230907092521.733746-1-christian.couder@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Elijah Newren The replay command is able to replay multiple branches but when some of them are based on other replayed branches, their commit should be replayed onto already replayed commits. For this purpose, let's store the replayed commit and its original commit in a key value store, so that we can easily find and reuse a replayed commit instead of the original one. Co-authored-by: Christian Couder Signed-off-by: Elijah Newren Signed-off-by: Christian Couder --- builtin/replay.c | 44 ++++++++++++++++++++++++++-------- t/t3650-replay-basics.sh | 52 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 10 deletions(-) diff --git a/builtin/replay.c b/builtin/replay.c index a7d36a639c..5479406863 100644 --- a/builtin/replay.c +++ b/builtin/replay.c @@ -223,20 +223,33 @@ static void determine_replay_mode(struct rev_cmdline_info *cmd_info, strset_clear(&rinfo.positive_refs); } +static struct commit *mapped_commit(kh_oid_map_t *replayed_commits, + struct commit *commit, + struct commit *fallback) +{ + khint_t pos = kh_get_oid_map(replayed_commits, commit->object.oid); + if (pos == kh_end(replayed_commits)) + return fallback; + return kh_value(replayed_commits, pos); +} + static struct commit *pick_regular_commit(struct commit *pickme, - struct commit *last_commit, + kh_oid_map_t *replayed_commits, + struct commit *onto, struct merge_options *merge_opt, struct merge_result *result) { - struct commit *base; + struct commit *base, *replayed_base; struct tree *pickme_tree, *base_tree; base = pickme->parents->item; + replayed_base = mapped_commit(replayed_commits, base, onto); + result->tree = repo_get_commit_tree(the_repository, replayed_base); pickme_tree = repo_get_commit_tree(the_repository, pickme); base_tree = repo_get_commit_tree(the_repository, base); - merge_opt->branch1 = short_commit_name(last_commit); + merge_opt->branch1 = short_commit_name(replayed_base); merge_opt->branch2 = short_commit_name(pickme); merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2); @@ -250,7 +263,7 @@ static struct commit *pick_regular_commit(struct commit *pickme, merge_opt->ancestor = NULL; if (!result->clean) return NULL; - return create_commit(result->tree, pickme, last_commit); + return create_commit(result->tree, pickme, replayed_base); } int cmd_replay(int argc, const char **argv, const char *prefix) @@ -266,6 +279,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix) struct merge_options merge_opt; struct merge_result result; struct strset *update_refs = NULL; + kh_oid_map_t *replayed_commits; int ret = 0, i; const char * const replay_usage[] = { @@ -348,21 +362,30 @@ int cmd_replay(int argc, const char **argv, const char *prefix) init_merge_options(&merge_opt, the_repository); memset(&result, 0, sizeof(result)); merge_opt.show_rename_progress = 0; - - result.tree = repo_get_commit_tree(the_repository, onto); last_commit = onto; + replayed_commits = kh_init_oid_map(); while ((commit = get_revision(&revs))) { const struct name_decoration *decoration; + khint_t pos; + int hr; if (!commit->parents) die(_("replaying down to root commit is not supported yet!")); if (commit->parents->next) die(_("replaying merge commits is not supported yet!")); - last_commit = pick_regular_commit(commit, last_commit, &merge_opt, &result); + last_commit = pick_regular_commit(commit, replayed_commits, onto, + &merge_opt, &result); if (!last_commit) break; + /* Record commit -> last_commit mapping */ + pos = kh_put_oid_map(replayed_commits, commit->object.oid, &hr); + if (hr == 0) + BUG("Duplicate rewritten commit: %s\n", + oid_to_hex(&commit->object.oid)); + kh_value(replayed_commits, pos) = last_commit; + /* Update any necessary branches */ if (advance_name) continue; @@ -391,13 +414,14 @@ int cmd_replay(int argc, const char **argv, const char *prefix) } merge_finalize(&merge_opt, &result); - ret = result.clean; - -cleanup: + kh_destroy_oid_map(replayed_commits); if (update_refs) { strset_clear(update_refs); free(update_refs); } + ret = result.clean; + +cleanup: release_revisions(&revs); /* Return */ diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh index 57d2ef9ea4..fe5ae0391d 100755 --- a/t/t3650-replay-basics.sh +++ b/t/t3650-replay-basics.sh @@ -159,4 +159,56 @@ test_expect_success 'using replay on bare repo to also rebase a contained branch test_cmp expect result-bare ' +test_expect_success 'using replay to rebase multiple divergent branches' ' + git replay --onto main ^topic1 topic2 topic4 >result && + + test_line_count = 2 result && + cut -f 3 -d " " result >new-branch-tips && + + git log --format=%s $(head -n 1 new-branch-tips) >actual && + test_write_lines E D M L B A >expect && + test_cmp expect actual && + + git log --format=%s $(tail -n 1 new-branch-tips) >actual && + test_write_lines J I M L B A >expect && + test_cmp expect actual && + + printf "update refs/heads/topic2 " >expect && + printf "%s " $(head -n 1 new-branch-tips) >>expect && + git rev-parse topic2 >>expect && + printf "update refs/heads/topic4 " >>expect && + printf "%s " $(tail -n 1 new-branch-tips) >>expect && + git rev-parse topic4 >>expect && + + test_cmp expect result +' + +test_expect_success 'using replay on bare repo to rebase multiple divergent branches, including contained ones' ' + git -C bare replay --contained --onto main ^main topic2 topic3 topic4 >result && + + test_line_count = 4 result && + cut -f 3 -d " " result >new-branch-tips && + + >expect && + for i in 2 1 3 4 + do + printf "update refs/heads/topic$i " >>expect && + printf "%s " $(grep topic$i result | cut -f 3 -d " ") >>expect && + git -C bare rev-parse topic$i >>expect || return 1 + done && + + test_cmp expect result && + + test_write_lines F C M L B A >expect1 && + test_write_lines E D C M L B A >expect2 && + test_write_lines H G F C M L B A >expect3 && + test_write_lines J I M L B A >expect4 && + + for i in 1 2 3 4 + do + git -C bare log --format=%s $(grep topic$i result | cut -f 3 -d " ") >actual && + test_cmp expect$i actual || return 1 + done +' + test_done -- 2.42.0.126.gcf8c984877