From patchwork Thu Oct 29 20:32:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elijah Newren X-Patchwork-Id: 11867505 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.6 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BEB91C2D0A3 for ; Thu, 29 Oct 2020 20:32:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3BE412072E for ; Thu, 29 Oct 2020 20:32:38 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="eMV7upRs" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726587AbgJ2Uch (ORCPT ); Thu, 29 Oct 2020 16:32:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37392 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726556AbgJ2Uce (ORCPT ); Thu, 29 Oct 2020 16:32:34 -0400 Received: from mail-wm1-x32c.google.com (mail-wm1-x32c.google.com [IPv6:2a00:1450:4864:20::32c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D1FF8C0613D5 for ; Thu, 29 Oct 2020 13:32:18 -0700 (PDT) Received: by mail-wm1-x32c.google.com with SMTP id k21so1983889wmi.1 for ; Thu, 29 Oct 2020 13:32:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=68sc9B/0csd629noV8jhACYBrCHiFf55lnL+7AxCs08=; b=eMV7upRslYVcAzpLh9N4CbjZ8P/LuiGKo/0Q2dgQn2IFoFrZ9SPdjf/h9Eai8YsOD7 Gt2AI6L42FWLYV4UhOKIpXV2Rounvc324XwDl4wll8FyiXXviOOBCMV9E4kQFChw8pqK Dem+V9RKJ3Y+VrsShue09Ey0lFlquxuP266hYsFn4IWOXWKSjO3EC2E4atSfWHOpetRc JMqAvvaU5qrILElLktshsCjOByExv7EKKoj0EffhlZGUsUzgjTedWh5ib+7jvmAayrPy pVy30ZSG9jhf1uyHMyxzN8wCFGaLjOPrUlqi075iVt6Cka3HAuTsVTJ9E35kmh5rv6qA 19UQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=68sc9B/0csd629noV8jhACYBrCHiFf55lnL+7AxCs08=; b=OgY0K6IrJElwqB8fm9zW8wmcIS6SYpycoF7aldwT4LpM341Av8aWQmef232uI7EnUF X7MZbuJmZRcrlCAP6AwGqVrSP1OZ/EY25AoEPBrIJxMSm0Z+yZSuF7jKp5dyOPwtaKgB lqPSgZs7ly4FWaz5KzL3MNfgRNEqCxwGrdndAEOqaJOdV3SMjQbLFVjoB4gp78szrFeT uPZ4+/qwKgQIJQSAzmyomFyy426Hdg23br15OASEMoAyLDmIG3gu3nlXU9wkbVdkz+67 TvH8Jj6/xTzzrdz5Zr3HARxBiAgdDYtbbqw+xEMevVbPyd72+FVQpqHcYRdBbM5mrmvn toWA== X-Gm-Message-State: AOAM531Sv8awNCvUN3V/qfwnbctkUXuRzZvXvZgv2xNP5V6a/l4z1gXo ihUVwSIOXIhvYj/U8X/J/dxgjfLvUcc= X-Google-Smtp-Source: ABdhPJyqyGM578AOIPcnTLHEu98asbLlMj0rupcnVUkFVU6IiNV6c68MYnPB52DtfeYTHw6dMoPxaQ== X-Received: by 2002:a1c:56c1:: with SMTP id k184mr732279wmb.14.1604003537347; Thu, 29 Oct 2020 13:32:17 -0700 (PDT) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id o197sm1636016wme.17.2020.10.29.13.32.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 29 Oct 2020 13:32:16 -0700 (PDT) Message-Id: <3357ea415e3437966f15bf73fbbeb21cda3df592.1604003535.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Thu, 29 Oct 2020 20:32:11 +0000 Subject: [PATCH v4 1/4] merge-ort: barebones API of new merge strategy with empty implementation Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Elijah Newren , Taylor Blau , Peter Baumann , Jonathan Tan , Eric Sunshine , SZEDER =?utf-8?b?R8OhYm9y?= , Elijah Newren , Elijah Newren Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Elijah Newren From: Elijah Newren This is the beginning of a new merge strategy. While there are some API differences, and the implementation has some differences in behavior, it is essentially meant as an eventual drop-in replacement for merge-recursive.c. However, it is being built to exist side-by-side with merge-recursive so that we have plenty of time to find out how those differences pan out in the real world while people can still fall back to merge-recursive. (Also, I intend to avoid modifying merge-recursive during this process, to keep it stable.) The primary difference noticable here is that the updating of the working tree and index is not done simultaneously with the merge algorithm, but is a separate post-processing step. The new API is designed so that one can do repeated merges (e.g. during a rebase or cherry-pick) and only update the index and working tree one time at the end instead of updating it with every intermediate result. Also, one can perform a merge between two branches, neither of which match the index or the working tree, without clobbering the index or working tree. The next three commits will demonstrate various uses of this new API. Signed-off-by: Elijah Newren --- Makefile | 1 + merge-ort.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ merge-ort.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 merge-ort.c create mode 100644 merge-ort.h diff --git a/Makefile b/Makefile index 95571ee3fc..088770c2ae 100644 --- a/Makefile +++ b/Makefile @@ -921,6 +921,7 @@ LIB_OBJS += mailmap.o LIB_OBJS += match-trees.o LIB_OBJS += mem-pool.o LIB_OBJS += merge-blobs.o +LIB_OBJS += merge-ort.o LIB_OBJS += merge-recursive.o LIB_OBJS += merge.o LIB_OBJS += mergesort.o diff --git a/merge-ort.c b/merge-ort.c new file mode 100644 index 0000000000..b487901d3e --- /dev/null +++ b/merge-ort.c @@ -0,0 +1,52 @@ +/* + * "Ostensibly Recursive's Twin" merge strategy, or "ort" for short. Meant + * as a drop-in replacement for the "recursive" merge strategy, allowing one + * to replace + * + * git merge [-s recursive] + * + * with + * + * git merge -s ort + * + * Note: git's parser allows the space between '-s' and its argument to be + * missing. (Should I have backronymed "ham", "alsa", "kip", "nap, "alvo", + * "cale", "peedy", or "ins" instead of "ort"?) + */ + +#include "cache.h" +#include "merge-ort.h" + +void merge_switch_to_result(struct merge_options *opt, + struct tree *head, + struct merge_result *result, + int update_worktree_and_index, + int display_update_msgs) +{ + die("Not yet implemented"); + merge_finalize(opt, result); +} + +void merge_finalize(struct merge_options *opt, + struct merge_result *result) +{ + die("Not yet implemented"); +} + +void merge_incore_nonrecursive(struct merge_options *opt, + struct tree *merge_base, + struct tree *side1, + struct tree *side2, + struct merge_result *result) +{ + die("Not yet implemented"); +} + +void merge_incore_recursive(struct merge_options *opt, + struct commit_list *merge_bases, + struct commit *side1, + struct commit *side2, + struct merge_result *result) +{ + die("Not yet implemented"); +} diff --git a/merge-ort.h b/merge-ort.h new file mode 100644 index 0000000000..74adccad16 --- /dev/null +++ b/merge-ort.h @@ -0,0 +1,58 @@ +#ifndef MERGE_ORT_H +#define MERGE_ORT_H + +#include "merge-recursive.h" + +struct commit; +struct tree; + +struct merge_result { + /* Whether the merge is clean */ + int clean; + + /* + * Result of merge. If !clean, represents what would go in worktree + * (thus possibly including files containing conflict markers). + */ + struct tree *tree; + + /* + * Additional metadata used by merge_switch_to_result() or future calls + * to merge_incore_*(). Includes data needed to update the index (if + * !clean) and to print "CONFLICT" messages. Not for external use. + */ + void *priv; +}; + +/* + * rename-detecting three-way merge with recursive ancestor consolidation. + * working tree and index are untouched. + */ +void merge_incore_recursive(struct merge_options *opt, + struct commit_list *merge_bases, + struct commit *side1, + struct commit *side2, + struct merge_result *result); + +/* + * rename-detecting three-way merge, no recursion. + * working tree and index are untouched. + */ +void merge_incore_nonrecursive(struct merge_options *opt, + struct tree *merge_base, + struct tree *side1, + struct tree *side2, + struct merge_result *result); + +/* Update the working tree and index from head to result after incore merge */ +void merge_switch_to_result(struct merge_options *opt, + struct tree *head, + struct merge_result *result, + int update_worktree_and_index, + int display_update_msgs); + +/* Do needed cleanup when not calling merge_switch_to_result() */ +void merge_finalize(struct merge_options *opt, + struct merge_result *result); + +#endif From patchwork Mon Nov 2 23:45:32 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elijah Newren X-Patchwork-Id: 11875549 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.6 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 96014C388F2 for ; Mon, 2 Nov 2020 23:45:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3FF9322268 for ; Mon, 2 Nov 2020 23:45:43 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="WuXPWOn0" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726546AbgKBXpl (ORCPT ); Mon, 2 Nov 2020 18:45:41 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51384 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726140AbgKBXpk (ORCPT ); Mon, 2 Nov 2020 18:45:40 -0500 Received: from mail-wr1-x441.google.com (mail-wr1-x441.google.com [IPv6:2a00:1450:4864:20::441]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6A81EC061A47 for ; Mon, 2 Nov 2020 15:45:39 -0800 (PST) Received: by mail-wr1-x441.google.com with SMTP id b8so16546875wrn.0 for ; Mon, 02 Nov 2020 15:45:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=+Y+9I9keb2Y2wQTpn35Y0+o6yoPUrfiqSOD46QH93r8=; b=WuXPWOn07ZHSlqE4amHVh8CLJobFNBylS9qg3YmW0nyxYLZkk+VCVcr3nBNImRq/u3 2lwvSSqMU6x++OhbA3uYPGLhqOUoTJ5r7Qyc5sIdAb2gkhEA7hVUylRtpSpbwwuX8Ija Z289xTO1VlOLPtQ7onaSatFTkB7iiaMXMRq3sZxxfhJ6svKI5jsS3gpc0Kl9ai5U16hA /Rawd62lHMnvVxMHUFYLlxqkQDslyHXZkz7fy8X8ulaxcHm93IGF+Hqv7XcmQI33JJhN fi8Dzc17H0bCIu2YbAqeN74AIRi6XEABEHPdZP7T2U8HK6K0KcK7ko+XBZRN/T2G7qv3 tsNA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=+Y+9I9keb2Y2wQTpn35Y0+o6yoPUrfiqSOD46QH93r8=; b=Ygk5H3ei+4OcHeIahpnGTuXDHAP2rcAEk0BYfLuUnYqegBanv85RYYkavW0J7rYLtk s9ZWVymRsbefk38/x5qRMVggbQBhO7Dm8mD4S59dHRnVcnfwC2Hol7AmrHf0VQhuxPao 9IGgrrhne0PfTVuj/wOFlWIjYJUmFX+bMiG1oifq5xufsO3/B0xmj8C+mTGpLrsMOt/W Z/NCOaZCGBCLiGzS4I7GY5+X7kR1zWHKZQb9O0cgSRGSazr6cCwfj66B3tx7m/DxT45+ DyiRBUGQhdi2+RR1KUxsXWa2n8M8htdnEogk9Zum6o3NE3/A9Qe+Wv0TPyZ1m9mdl9Xi 5BQA== X-Gm-Message-State: AOAM531CuYiYIGP0QtM3vjZYWpsC7pldtBL8SH5VXYCxSvEWOoTq+kJa 0SSdKOEhB1ooLGkwcel07j3+d30CZ54= X-Google-Smtp-Source: ABdhPJw6qk3Fy1Nc0scTpfg7CNrHci5NI0gjmUpi3esaOSZhAaN0p23Hk8s/fvk7iFpykMZqT2iSXQ== X-Received: by 2002:adf:a29e:: with SMTP id s30mr24443720wra.29.1604360738082; Mon, 02 Nov 2020 15:45:38 -0800 (PST) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id t2sm7466233wrw.95.2020.11.02.15.45.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Nov 2020 15:45:37 -0800 (PST) Message-Id: In-Reply-To: References: Date: Mon, 02 Nov 2020 23:45:32 +0000 Subject: [PATCH v5 2/4] merge-ort-wrappers: new convience wrappers to mimic the old merge API Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Elijah Newren , Taylor Blau , Peter Baumann , Jonathan Tan , Eric Sunshine , SZEDER =?utf-8?b?R8OhYm9y?= , Jacob Keller , Elijah Newren , Elijah Newren Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Elijah Newren From: Elijah Newren There are a few differences between the new API in merge-ort and the old API in merge-recursive. While the new API is more flexible, it might feel like more work at times than the old API. merge-ort-wrappers creates two convenience wrappers taking the exact same arguments as the old merge_trees() and merge_recursive() functions and implements them via the new API. This makes converting existing callsites easier, and serves to highlight some of the differences in the API. Signed-off-by: Elijah Newren --- Makefile | 1 + merge-ort-wrappers.c | 62 ++++++++++++++++++++++++++++++++++++++++++++ merge-ort-wrappers.h | 25 ++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 merge-ort-wrappers.c create mode 100644 merge-ort-wrappers.h diff --git a/Makefile b/Makefile index 088770c2ae..382fe73c76 100644 --- a/Makefile +++ b/Makefile @@ -922,6 +922,7 @@ LIB_OBJS += match-trees.o LIB_OBJS += mem-pool.o LIB_OBJS += merge-blobs.o LIB_OBJS += merge-ort.o +LIB_OBJS += merge-ort-wrappers.o LIB_OBJS += merge-recursive.o LIB_OBJS += merge.o LIB_OBJS += mergesort.o diff --git a/merge-ort-wrappers.c b/merge-ort-wrappers.c new file mode 100644 index 0000000000..7eec25f93a --- /dev/null +++ b/merge-ort-wrappers.c @@ -0,0 +1,62 @@ +#include "cache.h" +#include "merge-ort.h" +#include "merge-ort-wrappers.h" + +#include "commit.h" + +static int unclean(struct merge_options *opt, struct tree *head) +{ + /* Sanity check on repo state; index must match head */ + struct strbuf sb = STRBUF_INIT; + + if (head && repo_index_has_changes(opt->repo, head, &sb)) { + fprintf(stderr, _("Your local changes to the following files would be overwritten by merge:\n %s"), + sb.buf); + strbuf_release(&sb); + return -1; + } + + return 0; +} + +int merge_ort_nonrecursive(struct merge_options *opt, + struct tree *head, + struct tree *merge, + struct tree *merge_base) +{ + struct merge_result result; + + if (unclean(opt, head)) + return -1; + + if (oideq(&merge_base->object.oid, &merge->object.oid)) { + printf(_("Already up to date!")); + return 1; + } + + memset(&result, 0, sizeof(result)); + merge_incore_nonrecursive(opt, merge_base, head, merge, &result); + merge_switch_to_result(opt, head, &result, 1, 1); + + return result.clean; +} + +int merge_ort_recursive(struct merge_options *opt, + struct commit *side1, + struct commit *side2, + struct commit_list *merge_bases, + struct commit **result) +{ + struct tree *head = repo_get_commit_tree(opt->repo, side1); + struct merge_result tmp; + + if (unclean(opt, head)) + return -1; + + memset(&tmp, 0, sizeof(tmp)); + merge_incore_recursive(opt, merge_bases, side1, side2, &tmp); + merge_switch_to_result(opt, head, &tmp, 1, 1); + *result = NULL; + + return tmp.clean; +} diff --git a/merge-ort-wrappers.h b/merge-ort-wrappers.h new file mode 100644 index 0000000000..0c4c57adbb --- /dev/null +++ b/merge-ort-wrappers.h @@ -0,0 +1,25 @@ +#ifndef MERGE_ORT_WRAPPERS_H +#define MERGE_ORT_WRAPPERS_H + +#include "merge-recursive.h" + +/* + * rename-detecting three-way merge, no recursion. + * Wrapper mimicking the old merge_trees() function. + */ +int merge_ort_nonrecursive(struct merge_options *opt, + struct tree *head, + struct tree *merge, + struct tree *common); + +/* + * rename-detecting three-way merge with recursive ancestor consolidation. + * Wrapper mimicking the old merge_recursive() function. + */ +int merge_ort_recursive(struct merge_options *opt, + struct commit *h1, + struct commit *h2, + struct commit_list *ancestors, + struct commit **result); + +#endif From patchwork Mon Nov 2 23:45:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elijah Newren X-Patchwork-Id: 11875551 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.6 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BA734C2D0A3 for ; Mon, 2 Nov 2020 23:45:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7149622268 for ; Mon, 2 Nov 2020 23:45:42 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ROaMijPY" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726581AbgKBXpl (ORCPT ); Mon, 2 Nov 2020 18:45:41 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51390 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726380AbgKBXpk (ORCPT ); Mon, 2 Nov 2020 18:45:40 -0500 Received: from mail-wr1-x42c.google.com (mail-wr1-x42c.google.com [IPv6:2a00:1450:4864:20::42c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5DC42C061A48 for ; Mon, 2 Nov 2020 15:45:40 -0800 (PST) Received: by mail-wr1-x42c.google.com with SMTP id y12so16510771wrp.6 for ; Mon, 02 Nov 2020 15:45:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=3UOUvb0C/w+fD2FCxP9MhvlUcs/BBagXvRQQqA42S8E=; b=ROaMijPYMP8DLEH5cNUu3oHxlUvFS3uOzBQbq2iv0jpbFvC+stzqeetJCoEeUtm6Cv 8TDlygRobnq9sSVU66sQTE7RbCaUiIgTs8fTBvhpCAdu+XIAh663L+p2mVowWsneESem 7fGubHpzRZZMmJD1zEkm/7m04+I14vvFL9Y2HNAbrMNxzEp+yRrW2uvJu97tThZbSTeh 8Y7zj928X+0KDW9QlrkTbFTnr1+ZVPK4gTPlp9QDv5+8GbuZx6PTFbh1Pi82//qwNEWV zW/oq9c8gla1D2LuRRwbf776524+mE96XUtPtJCFzhYhOZeQs1yiw7bbhuueobNzHUku g1gw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=3UOUvb0C/w+fD2FCxP9MhvlUcs/BBagXvRQQqA42S8E=; b=Nh7AiU75QRA7N1krBd84cQTFdQxT1cbJbcOD2Gz55zrCRUnnaHIOcTqtvvjSAm5yhj el9bbGIlBDishaAyZ8Di0tFuJ2suG/xMw2+CBHx2DohyHGE+fT3u3fRlyWQYxybA9rDl hM3n5wmqayBTkGVF0wMDiPPv90p0pCcSvIE78TmyGfiguraX2JFHXyixLapbs1it74Qd zu9JtRQytsgDPBx9QvqZrjFngYprxqT9D+0l29kMIHHNPB2HpvZSRRzwN9AMBeow2oUe rdgVJfd6Cw0uhwRhueHRQXnHqAHfztxg5Ki31eEBrYhPonGkgbvUvjWbad3bVA/PJ3FS PP+w== X-Gm-Message-State: AOAM530eoXW34EVjpxTfktIC9IlQZEDZ+mqh8HaZex6ujJ/G+xgb/WR0 gwp0hk0eiWdFxTilkaZudD8EklVEUP0= X-Google-Smtp-Source: ABdhPJyGSPWhqUcYs9Hf265fs2K4zEr7ncNnuYxrSwiU4oq+huquX0eRoiEEycm9G+E3cxzsxbe5CQ== X-Received: by 2002:a05:6000:372:: with SMTP id f18mr18080501wrf.149.1604360738826; Mon, 02 Nov 2020 15:45:38 -0800 (PST) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id e11sm22931014wrj.75.2020.11.02.15.45.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Nov 2020 15:45:38 -0800 (PST) Message-Id: In-Reply-To: References: Date: Mon, 02 Nov 2020 23:45:33 +0000 Subject: [PATCH v5 3/4] fast-rebase: demonstrate merge-ort's API via new test-tool command Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Elijah Newren , Taylor Blau , Peter Baumann , Jonathan Tan , Eric Sunshine , SZEDER =?utf-8?b?R8OhYm9y?= , Jacob Keller , Elijah Newren , Elijah Newren Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Elijah Newren From: Elijah Newren Add a new test-tool command named 'fast-rebase', which is a super-slimmed down and nowhere near as capable version of 'git rebase'. 'test-tool fast-rebase' is not currently planned for usage in the testsuite, but is here for two purposes: 1) Demonstrate the desired API of merge-ort. In particular, fast-rebase takes advantage of the separation of the merging operation from the updating of the index and working tree, to allow it to pick N commits, but only update the index and working tree once at the end. Look for the calls to merge_incore_nonrecursive() and merge_switch_to_result(). 2) Provide a convenient benchmark that isn't polluted by the heavy disk writing and forking of unnecessary processes that comes from sequencer.c and merge-recursive.c. fast-rebase is not meant to replace sequencer.c, just give ideas on how sequencer.c can be changed. Updating sequencer.c with these goals is probably a large amount of work; writing a simple targeted command with no documentation, less-than-useful help messages, numerous limitations in terms of flags it can accept and situations it can handle, and which is flagged off from users is a much easier interim step. Signed-off-by: Elijah Newren --- Makefile | 1 + t/helper/test-fast-rebase.c | 211 ++++++++++++++++++++++++++++++++++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + 4 files changed, 214 insertions(+) create mode 100644 t/helper/test-fast-rebase.c diff --git a/Makefile b/Makefile index 382fe73c76..24b9fecc0f 100644 --- a/Makefile +++ b/Makefile @@ -704,6 +704,7 @@ TEST_BUILTINS_OBJS += test-dump-fsmonitor.o TEST_BUILTINS_OBJS += test-dump-split-index.o TEST_BUILTINS_OBJS += test-dump-untracked-cache.o TEST_BUILTINS_OBJS += test-example-decorate.o +TEST_BUILTINS_OBJS += test-fast-rebase.o TEST_BUILTINS_OBJS += test-genrandom.o TEST_BUILTINS_OBJS += test-genzeros.o TEST_BUILTINS_OBJS += test-hash-speed.o diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c new file mode 100644 index 0000000000..373212256a --- /dev/null +++ b/t/helper/test-fast-rebase.c @@ -0,0 +1,211 @@ +/* + * "git fast-rebase" builtin command + * + * FAST: Forking Any Subprocesses (is) Taboo + * + * This is meant SOLELY as a demo of what is possible. sequencer.c and + * rebase.c should be refactored to use the ideas here, rather than attempting + * to extend this file to replace those (unless Phillip or Dscho say that + * refactoring is too hard and we need a clean slate, but I'm guessing that + * refactoring is the better route). + */ + +#define USE_THE_INDEX_COMPATIBILITY_MACROS +#include "test-tool.h" + +#include "cache-tree.h" +#include "commit.h" +#include "lockfile.h" +#include "merge-ort.h" +#include "refs.h" +#include "revision.h" +#include "sequencer.h" +#include "strvec.h" +#include "tree.h" + +static const char *short_commit_name(struct commit *commit) +{ + return find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV); +} + +static struct commit *peel_committish(const char *name) +{ + struct object *obj; + struct object_id oid; + + if (get_oid(name, &oid)) + return NULL; + obj = parse_object(the_repository, &oid); + return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT); +} + +static char *get_author(const char *message) +{ + size_t len; + const char *a; + + a = find_commit_header(message, "author", &len); + if (a) + return xmemdupz(a, len); + + return NULL; +} + +static struct commit *create_commit(struct tree *tree, + struct commit *based_on, + struct commit *parent) +{ + struct object_id ret; + struct object *obj; + struct commit_list *parents = NULL; + char *author; + char *sign_commit = NULL; + struct commit_extra_header *extra; + struct strbuf msg = STRBUF_INIT; + const char *out_enc = get_commit_output_encoding(); + const char *message = logmsg_reencode(based_on, NULL, out_enc); + const char *orig_message = NULL; + const char *exclude_gpgsig[] = { "gpgsig", NULL }; + + commit_list_insert(parent, &parents); + extra = read_commit_extra_headers(based_on, exclude_gpgsig); + find_commit_subject(message, &orig_message); + strbuf_addstr(&msg, orig_message); + author = get_author(message); + reset_ident_date(); + if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents, + &ret, author, NULL, sign_commit, extra)) { + error(_("failed to write commit object")); + return NULL; + } + free(author); + strbuf_release(&msg); + + obj = parse_object(the_repository, &ret); + return (struct commit *)obj; +} + +int cmd__fast_rebase(int argc, const char **argv) +{ + struct commit *onto; + struct commit *last_commit = NULL, *last_picked_commit = NULL; + struct object_id head; + struct lock_file lock = LOCK_INIT; + int clean = 1; + struct strvec rev_walk_args = STRVEC_INIT; + struct rev_info revs; + struct commit *commit; + struct merge_options merge_opt; + struct tree *next_tree, *base_tree, *head_tree; + struct merge_result result; + struct strbuf reflog_msg = STRBUF_INIT; + struct strbuf branch_name = STRBUF_INIT; + + /* + * test-tool stuff doesn't set up the git directory by default; need to + * do that manually. + */ + setup_git_directory(); + + if (argc == 2 && !strcmp(argv[1], "-h")) { + printf("Sorry, I am not a psychiatrist; I can not give you the help you need. Oh, you meant usage...\n"); + exit(129); + } + + if (argc != 5 || strcmp(argv[1], "--onto")) + die("usage: read the code, figure out how to use it, then do so"); + + onto = peel_committish(argv[2]); + strbuf_addf(&branch_name, "refs/heads/%s", argv[4]); + + /* Sanity check */ + if (get_oid("HEAD", &head)) + die(_("Cannot read HEAD")); + assert(oideq(&onto->object.oid, &head)); + + hold_locked_index(&lock, LOCK_DIE_ON_ERROR); + assert(repo_read_index(the_repository) >= 0); + + repo_init_revisions(the_repository, &revs, NULL); + revs.verbose_header = 1; + revs.max_parents = 1; + revs.cherry_mark = 1; + revs.limited = 1; + revs.reverse = 1; + revs.right_only = 1; + revs.sort_order = REV_SORT_IN_GRAPH_ORDER; + revs.topo_order = 1; + strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL); + + if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) + return error(_("unhandled options")); + + strvec_clear(&rev_walk_args); + + if (prepare_revision_walk(&revs) < 0) + return error(_("error preparing revisions")); + + init_merge_options(&merge_opt, the_repository); + memset(&result, 0, sizeof(result)); + merge_opt.show_rename_progress = 1; + merge_opt.branch1 = "HEAD"; + head_tree = get_commit_tree(onto); + result.tree = head_tree; + last_commit = onto; + while ((commit = get_revision(&revs))) { + struct commit *base; + + fprintf(stderr, "Rebasing %s...\r", + oid_to_hex(&commit->object.oid)); + assert(commit->parents && !commit->parents->next); + base = commit->parents->item; + + next_tree = get_commit_tree(commit); + base_tree = get_commit_tree(base); + + merge_opt.branch2 = short_commit_name(commit); + merge_opt.ancestor = xstrfmt("parent of %s", merge_opt.branch2); + + merge_incore_nonrecursive(&merge_opt, + base_tree, + result.tree, + next_tree, + &result); + + free((char*)merge_opt.ancestor); + merge_opt.ancestor = NULL; + if (!result.clean) + die("Aborting: Hit a conflict and restarting is not implemented."); + last_picked_commit = commit; + last_commit = create_commit(result.tree, commit, last_commit); + } + fprintf(stderr, "\nDone.\n"); + /* TODO: There should be some kind of rev_info_free(&revs) call... */ + memset(&revs, 0, sizeof(revs)); + + merge_switch_to_result(&merge_opt, head_tree, &result, 1, !result.clean); + + if (result.clean < 0) + exit(128); + + strbuf_addf(&reflog_msg, "finish rebase %s onto %s", + oid_to_hex(&last_picked_commit->object.oid), + oid_to_hex(&last_commit->object.oid)); + if (update_ref(reflog_msg.buf, branch_name.buf, + &last_commit->object.oid, + &last_picked_commit->object.oid, + REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) { + error(_("could not update %s"), argv[4]); + die("Failed to update %s", argv[4]); + } + if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0) + die(_("unable to update HEAD")); + strbuf_release(&reflog_msg); + strbuf_release(&branch_name); + + prime_cache_tree(the_repository, the_repository->index, result.tree); + if (write_locked_index(&the_index, &lock, + COMMIT_LOCK | SKIP_IF_UNCHANGED)) + die(_("unable to write %s"), get_index_file()); + return (clean == 0); +} diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index a0d3966b29..8bce7db076 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -28,6 +28,7 @@ static struct test_cmd cmds[] = { { "dump-split-index", cmd__dump_split_index }, { "dump-untracked-cache", cmd__dump_untracked_cache }, { "example-decorate", cmd__example_decorate }, + { "fast-rebase", cmd__fast_rebase }, { "genrandom", cmd__genrandom }, { "genzeros", cmd__genzeros }, { "hashmap", cmd__hashmap }, diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index 07034d3f38..fd0cafe5ca 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -18,6 +18,7 @@ int cmd__dump_fsmonitor(int argc, const char **argv); int cmd__dump_split_index(int argc, const char **argv); int cmd__dump_untracked_cache(int argc, const char **argv); int cmd__example_decorate(int argc, const char **argv); +int cmd__fast_rebase(int argc, const char **argv); int cmd__genrandom(int argc, const char **argv); int cmd__genzeros(int argc, const char **argv); int cmd__hashmap(int argc, const char **argv); From patchwork Mon Nov 2 23:45:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elijah Newren X-Patchwork-Id: 11875555 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.6 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SIGNED_OFF_BY, SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id CBA2DC00A89 for ; Mon, 2 Nov 2020 23:45:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 75C3322268 for ; Mon, 2 Nov 2020 23:45:44 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="BGrTsznk" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726877AbgKBXpn (ORCPT ); Mon, 2 Nov 2020 18:45:43 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51392 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726140AbgKBXpl (ORCPT ); Mon, 2 Nov 2020 18:45:41 -0500 Received: from mail-wm1-x32e.google.com (mail-wm1-x32e.google.com [IPv6:2a00:1450:4864:20::32e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5A4C7C0617A6 for ; Mon, 2 Nov 2020 15:45:41 -0800 (PST) Received: by mail-wm1-x32e.google.com with SMTP id 205so3342799wma.4 for ; Mon, 02 Nov 2020 15:45:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=message-id:in-reply-to:references:from:date:subject:fcc :content-transfer-encoding:mime-version:to:cc; bh=crrilWx3bvKHO/x/02+iz/mTC9rEIdPqTxvt1snKMY0=; b=BGrTsznkjGW384sjeJqhmvUKrrTzg8WJ90aZlHwSloR0CveWoXUP61WGm4zq5lTW7s YaRinG4yWmxojv8FndSrDcxDAdz979LBecRYEzO97ZtEGnC7ncpWCve0gvSnHz29EOOT mY4i51Sz1+0EvNugVJHc8p9QlthEE3y0is9hFo77TBE3jQ8v+c/0pPRfcZ23NmtmjQtt 5K1VH3E2SdyCwaP1qM+qVF2JDBFApYRSwjTpxcPd3173Zph1w7/4jyqlth43oYMwylsH N/kUmdrvRwaq/QTBS5lcPj3DJNJH60eBcUlsJWMEuRftU0ZkWsTKrOc7yHKBRd/Sb+Dw 69sA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:in-reply-to:references:from:date :subject:fcc:content-transfer-encoding:mime-version:to:cc; bh=crrilWx3bvKHO/x/02+iz/mTC9rEIdPqTxvt1snKMY0=; b=gMj1bv9kbBXiMhkCT1/iX2ym4PlNCGvFW6MvjSXGQw1a3EBFGyxPF3Z6bwJfxbSbmW kdN9fKRPFLysEYum5TYyFmnvqwfRZ5p2NMeSAbQtjhe1QrAbv0/slqXFI98DLhNOf5Rv cUp/OqCzlkh++QRRVIWEOI1SX0ScwT01OVI7rBq9kALs2DBhVezLlcmxhVGM0UjRI+80 vB7C0jhAxDRN/uUB0ZjALDi2uSyxKSW8y6J2SsQw0B/b523OyZBsyOCdsEviUqvQZC9N 0d7OcAzXO2G7AT8HKXDAgV9Ndvg+FzZ+3nvQALGmbDvr9t4SnNQYyhMcCtegIaoLh+5S UYjA== X-Gm-Message-State: AOAM5334JDdtxyMqxy5dGMMj1mr0h+D391MJVXVeRCcR9ZX4PEDfskBc kr5KhLiARTJH0fTL9n8Rhg5jY3JIddo= X-Google-Smtp-Source: ABdhPJztYg1q0sTXgX3BnyvIEdtQmil5eg33IyFhNhBU/9k2d4SVvgBeEdPTlQOVLlP+m9SJUt2Pcw== X-Received: by 2002:a05:600c:28c1:: with SMTP id h1mr550103wmd.30.1604360739717; Mon, 02 Nov 2020 15:45:39 -0800 (PST) Received: from [127.0.0.1] ([13.74.141.28]) by smtp.gmail.com with ESMTPSA id b7sm23191139wrp.16.2020.11.02.15.45.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 02 Nov 2020 15:45:39 -0800 (PST) Message-Id: <61217a83bd7ff0ce9016eb4df9ded4fdf29a506c.1604360734.git.gitgitgadget@gmail.com> In-Reply-To: References: Date: Mon, 02 Nov 2020 23:45:34 +0000 Subject: [PATCH v5 4/4] merge,rebase,revert: select ort or recursive by config or environment Fcc: Sent MIME-Version: 1.0 To: git@vger.kernel.org Cc: Elijah Newren , Taylor Blau , Peter Baumann , Jonathan Tan , Eric Sunshine , SZEDER =?utf-8?b?R8OhYm9y?= , Jacob Keller , Elijah Newren , Elijah Newren Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org From: Elijah Newren From: Elijah Newren Allow the testsuite to run where it treats requests for "recursive" or the default merge algorithm via consulting the environment variable GIT_TEST_MERGE_ALGORITHM which is expected to either be "recursive" (the old traditional algorithm) or "ort" (the new algorithm). Also, allow folks to pick the new algorithm via config setting. It turns out builtin/merge.c already had a way to allow users to specify a different default merge algorithm: pull.twohead. Rather odd configuration name (especially to be in the 'pull' namespace rather than 'merge') but it's there. Add that same configuration to rebase, cherry-pick, and revert. This required updating the various callsites that called merge_trees() or merge_recursive() to conditionally call the new API, so this serves as another demonstration of what the new API looks and feels like. There are almost certainly some callsites that have not yet been modified to work with the new merge algorithm, but this represents the ones that I have been testing with thus far. Signed-off-by: Elijah Newren --- builtin/merge.c | 26 +++++++++++++++-- builtin/rebase.c | 13 ++++++++- builtin/revert.c | 7 +++++ sequencer.c | 72 ++++++++++++++++++++++++++++++++++++++++-------- sequencer.h | 1 + 5 files changed, 104 insertions(+), 15 deletions(-) diff --git a/builtin/merge.c b/builtin/merge.c index 9d5359edc2..87dfc9bc06 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -28,6 +28,7 @@ #include "rerere.h" #include "help.h" #include "merge-recursive.h" +#include "merge-ort-wrappers.h" #include "resolve-undo.h" #include "remote.h" #include "fmt-merge-msg.h" @@ -88,6 +89,7 @@ static int no_verify; static struct strategy all_strategy[] = { { "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL }, { "octopus", DEFAULT_OCTOPUS }, + { "ort", NO_TRIVIAL }, { "resolve", 0 }, { "ours", NO_FAST_FORWARD | NO_TRIVIAL }, { "subtree", NO_FAST_FORWARD | NO_TRIVIAL }, @@ -159,10 +161,17 @@ static struct strategy *get_strategy(const char *name) struct strategy *ret; static struct cmdnames main_cmds, other_cmds; static int loaded; + char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM"); if (!name) return NULL; + if (default_strategy && + !strcmp(default_strategy, "ort") && + !strcmp(name, "recursive")) { + name = "ort"; + } + for (i = 0; i < ARRAY_SIZE(all_strategy); i++) if (!strcmp(name, all_strategy[i].name)) return &all_strategy[i]; @@ -701,7 +710,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0) return error(_("Unable to write index.")); - if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) { + if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree") || + !strcmp(strategy, "ort")) { struct lock_file lock = LOCK_INIT; int clean, x; struct commit *result; @@ -732,8 +742,12 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, commit_list_insert(j->item, &reversed); hold_locked_index(&lock, LOCK_DIE_ON_ERROR); - clean = merge_recursive(&o, head, - remoteheads->item, reversed, &result); + if (!strcmp(strategy, "ort")) + clean = merge_ort_recursive(&o, head, remoteheads->item, + reversed, &result); + else + clean = merge_recursive(&o, head, remoteheads->item, + reversed, &result); if (clean < 0) exit(128); if (write_locked_index(&the_index, &lock, @@ -1264,6 +1278,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix) if (branch) skip_prefix(branch, "refs/heads/", &branch); + if (!pull_twohead) { + char *default_strategy = getenv("GIT_TEST_MERGE_ALGORITHM"); + if (default_strategy && !strcmp(default_strategy, "ort")) + pull_twohead = "ort"; + } + init_diff_ui_defaults(); git_config(git_merge_config, NULL); diff --git a/builtin/rebase.c b/builtin/rebase.c index eeca53382f..4ba5295ddf 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -119,6 +119,7 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) struct replay_opts replay = REPLAY_OPTS_INIT; replay.action = REPLAY_INTERACTIVE_REBASE; + replay.strategy = NULL; sequencer_init_config(&replay); replay.signoff = opts->signoff; @@ -136,7 +137,12 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) opts->committer_date_is_author_date; replay.ignore_date = opts->ignore_date; replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt); - replay.strategy = opts->strategy; + if (opts->strategy) + replay.strategy = opts->strategy; + else if (!replay.strategy && replay.default_strategy) { + replay.strategy = replay.default_strategy; + replay.default_strategy = NULL; + } if (opts->strategy_opts) parse_strategy_opts(&replay, opts->strategy_opts); @@ -1771,6 +1777,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.default_backend); } + if (options.type == REBASE_MERGE && + !options.strategy && + getenv("GIT_TEST_MERGE_ALGORITHM")) + options.strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM")); + switch (options.type) { case REBASE_MERGE: case REBASE_PRESERVE_MERGES: diff --git a/builtin/revert.c b/builtin/revert.c index f61cc5d82c..5b01a9baca 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -172,6 +172,11 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts) NULL); } + if (!opts->strategy && opts->default_strategy) { + opts->strategy = opts->default_strategy; + opts->default_strategy = NULL; + } + if (opts->allow_ff) verify_opt_compatible(me, "--ff", "--signoff", opts->signoff, @@ -202,6 +207,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts) /* These option values will be free()d */ opts->gpg_sign = xstrdup_or_null(opts->gpg_sign); opts->strategy = xstrdup_or_null(opts->strategy); + if (!opts->strategy && getenv("GIT_TEST_MERGE_ALGORITHM")) + opts->strategy = xstrdup(getenv("GIT_TEST_MERGE_ALGORITHM")); if (cmd == 'q') { int ret = sequencer_remove_state(opts); diff --git a/sequencer.c b/sequencer.c index 00acb12496..6b6c15c357 100644 --- a/sequencer.c +++ b/sequencer.c @@ -14,7 +14,8 @@ #include "diff.h" #include "revision.h" #include "rerere.h" -#include "merge-recursive.h" +#include "merge-ort.h" +#include "merge-ort-wrappers.h" #include "refs.h" #include "strvec.h" #include "quote.h" @@ -204,6 +205,20 @@ static int git_sequencer_config(const char *k, const char *v, void *cb) return 0; } + if (!opts->default_strategy && !strcmp(k, "pull.twohead")) { + int ret = git_config_string((const char**)&opts->default_strategy, k, v); + if (ret == 0) { + /* + * pull.twohead is allowed to be multi-valued; we only + * care about the first value. + */ + char *tmp = strchr(opts->default_strategy, ' '); + if (tmp) + *tmp = '\0'; + } + return ret; + } + status = git_gpg_config(k, v, NULL); if (status) return status; @@ -317,6 +332,7 @@ int sequencer_remove_state(struct replay_opts *opts) free(opts->committer_name); free(opts->committer_email); free(opts->gpg_sign); + free(opts->default_strategy); free(opts->strategy); for (i = 0; i < opts->xopts_nr; i++) free(opts->xopts[i]); @@ -595,8 +611,9 @@ static int do_recursive_merge(struct repository *r, struct replay_opts *opts) { struct merge_options o; + struct merge_result result; struct tree *next_tree, *base_tree, *head_tree; - int clean; + int clean, show_output; int i; struct lock_file index_lock = LOCK_INIT; @@ -620,12 +637,27 @@ static int do_recursive_merge(struct repository *r, for (i = 0; i < opts->xopts_nr; i++) parse_merge_opt(&o, opts->xopts[i]); - clean = merge_trees(&o, - head_tree, - next_tree, base_tree); - if (is_rebase_i(opts) && clean <= 0) - fputs(o.obuf.buf, stdout); - strbuf_release(&o.obuf); + if (opts->strategy && !strcmp(opts->strategy, "ort")) { + memset(&result, 0, sizeof(result)); + merge_incore_nonrecursive(&o, base_tree, head_tree, next_tree, + &result); + show_output = !is_rebase_i(opts) || !result.clean; + /* + * TODO: merge_switch_to_result will update index/working tree; + * we only really want to do that if !result.clean || this is + * the final patch to be picked. But determining this is the + * final patch would take some work, and "head_tree" would need + * to be replace with the tree the index matched before we + * started doing any picks. + */ + merge_switch_to_result(&o, head_tree, &result, 1, show_output); + clean = result.clean; + } else { + clean = merge_trees(&o, head_tree, next_tree, base_tree); + if (is_rebase_i(opts) && clean <= 0) + fputs(o.obuf.buf, stdout); + strbuf_release(&o.obuf); + } if (clean < 0) { rollback_lock_file(&index_lock); return clean; @@ -1991,7 +2023,10 @@ static int do_pick_commit(struct repository *r, if (is_rebase_i(opts) && write_author_script(msg.message) < 0) res = -1; - else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) { + else if (!opts->strategy || + !strcmp(opts->strategy, "recursive") || + !strcmp(opts->strategy, "ort") || + command == TODO_REVERT) { res = do_recursive_merge(r, base, next, base_label, next_label, &head, &msgbuf, opts); if (res < 0) @@ -3485,7 +3520,9 @@ static int do_merge(struct repository *r, struct commit_list *bases, *j, *reversed = NULL; struct commit_list *to_merge = NULL, **tail = &to_merge; const char *strategy = !opts->xopts_nr && - (!opts->strategy || !strcmp(opts->strategy, "recursive")) ? + (!opts->strategy || + !strcmp(opts->strategy, "recursive") || + !strcmp(opts->strategy, "ort")) ? NULL : opts->strategy; struct merge_options o; int merge_arg_len, oneline_offset, can_fast_forward, ret, k; @@ -3722,7 +3759,20 @@ static int do_merge(struct repository *r, o.branch2 = ref_name.buf; o.buffer_output = 2; - ret = merge_recursive(&o, head_commit, merge_commit, reversed, &i); + if (opts->strategy && !strcmp(opts->strategy, "ort")) { + /* + * TODO: Should use merge_incore_recursive() and + * merge_switch_to_result(), skipping the call to + * merge_switch_to_result() when we don't actually need to + * update the index and working copy immediately. + */ + ret = merge_ort_recursive(&o, + head_commit, merge_commit, reversed, + &i); + } else { + ret = merge_recursive(&o, head_commit, merge_commit, reversed, + &i); + } if (ret <= 0) fputs(o.obuf.buf, stdout); strbuf_release(&o.obuf); diff --git a/sequencer.h b/sequencer.h index b2a501e445..020b7fa118 100644 --- a/sequencer.h +++ b/sequencer.h @@ -57,6 +57,7 @@ struct replay_opts { int explicit_cleanup; /* Merge strategy */ + char *default_strategy; /* from config options */ char *strategy; char **xopts; size_t xopts_nr, xopts_alloc;