From patchwork Thu Mar 10 17:32:31 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "brian m. carlson" X-Patchwork-Id: 12776820 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 BDEB3C433EF for ; Thu, 10 Mar 2022 17:35:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245131AbiCJRg1 (ORCPT ); Thu, 10 Mar 2022 12:36:27 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36976 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232651AbiCJRgY (ORCPT ); Thu, 10 Mar 2022 12:36:24 -0500 Received: from ring.crustytoothpaste.net (ring.crustytoothpaste.net [172.105.110.227]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BC6D518A783 for ; Thu, 10 Mar 2022 09:35:22 -0800 (PST) Received: from camp.crustytoothpaste.net (unknown [IPv6:2001:470:b056:101:a6ae:7d13:8741:9028]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by ring.crustytoothpaste.net (Postfix) with ESMTPSA id 7BAA65A106; Thu, 10 Mar 2022 17:35:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=crustytoothpaste.net; s=default; t=1646933721; bh=ffTBjIq2TATxaKesvFVM7WoW68LWkYhRoAEgMcYi+mo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Reply-To: Subject:Date:To:CC:Resent-Date:Resent-From:Resent-To:Resent-Cc: In-Reply-To:References:Content-Type:Content-Disposition; b=FC/KZ+GtWuyl04ff4aEoGV6bzVaszmF2pJERru6ZKwLVNTMdV8rz/7QxpFo1q/6XQ PlVLz2miqH+2EfqF0JeZa1sxsgfPzeSvURd+iYSZYiAuJWprqioJpHyNbAlZSfq2E/ 3c8bwOjW77hjgRl0G2E6hBBVfoarN3zILVrEd7gX0pVKkrWp5Mg8in9UBf1/f2H1+Y Cqrqkug3B5gEhFy1+tYK4KDYNyZBUjFlVPaEg9px1U7DKjAYkgd9u2Q6YvQ7tt92tR xLm29a3UvN8NazwkCM4QYINdELeKeg/bZyBfp1CIbJ4jXEIc2S/8BOPbxnGbH6xtei zWXTVpyRRmoCD67joQcS6vddRxNt3TRrTuO6KtoO85BahUjjeTMgtJwLVj88l4KYkY dfCnaVPZ+6N+dGuy5WWFVfT7A9Qxik33rygygcS1bnAkJk9qtqIw0Z3BJtCxUilW2w /VBrJyrY8vKvuUEkSOk/nw2W8g03Sabd8nHPG3VYuszGWckFQK6 From: "brian m. carlson" To: Cc: Junio C Hamano , Derrick Stolee , Thomas Gummerer Subject: [PATCH 1/6] builtin/stash: factor out generic function to look up stash info Date: Thu, 10 Mar 2022 17:32:31 +0000 Message-Id: <20220310173236.4165310-2-sandals@crustytoothpaste.net> X-Mailer: git-send-email 2.35.1.473.g83b2b277ed In-Reply-To: <20220310173236.4165310-1-sandals@crustytoothpaste.net> References: <20220310173236.4165310-1-sandals@crustytoothpaste.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org We have a function called get_stash_info that looks up this data based on a set of command-line arguments and produces diagnostics to the user on failure. While this is helpful in the existing use cases, we'd like to make use of this logic in a more programmatic way in the future. Split out much of the function into a function which can be used internally and which knows how to suppress these error messages with a quiet parameter. Wire up the rest of the function to call this internal function to preserve the existing behavior. Signed-off-by: brian m. carlson --- builtin/stash.c | 50 ++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/builtin/stash.c b/builtin/stash.c index 5897febfbe..2aa06cc91d 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -130,38 +130,21 @@ static void assert_stash_like(struct stash_info *info, const char *revision) die(_("'%s' is not a stash-like commit"), revision); } -static int get_stash_info(struct stash_info *info, int argc, const char **argv) +static int get_stash_info_1(struct stash_info *info, const char *commit, int quiet) { int ret; char *end_of_rev; char *expanded_ref; const char *revision; - const char *commit = NULL; struct object_id dummy; struct strbuf symbolic = STRBUF_INIT; - if (argc > 1) { - int i; - struct strbuf refs_msg = STRBUF_INIT; - - for (i = 0; i < argc; i++) - strbuf_addf(&refs_msg, " '%s'", argv[i]); - - fprintf_ln(stderr, _("Too many revisions specified:%s"), - refs_msg.buf); - strbuf_release(&refs_msg); - - return -1; - } - - if (argc == 1) - commit = argv[0]; - strbuf_init(&info->revision, 0); if (!commit) { if (!ref_exists(ref_stash)) { free_stash_info(info); - fprintf_ln(stderr, _("No stash entries found.")); + if (!quiet) + fprintf_ln(stderr, _("No stash entries found.")); return -1; } @@ -175,7 +158,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv) revision = info->revision.buf; if (get_oid(revision, &info->w_commit)) { - error(_("%s is not a valid reference"), revision); + if (!quiet) + error(_("%s is not a valid reference"), revision); free_stash_info(info); return -1; } @@ -204,6 +188,30 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv) return !(ret == 0 || ret == 1); } +static int get_stash_info(struct stash_info *info, int argc, const char **argv) +{ + const char *commit = NULL; + + if (argc > 1) { + int i; + struct strbuf refs_msg = STRBUF_INIT; + + for (i = 0; i < argc; i++) + strbuf_addf(&refs_msg, " '%s'", argv[i]); + + fprintf_ln(stderr, _("Too many revisions specified:%s"), + refs_msg.buf); + strbuf_release(&refs_msg); + + return -1; + } + + if (argc == 1) + commit = argv[0]; + + return get_stash_info_1(info, commit, 0); +} + static int do_clear_stash(void) { struct object_id obj; From patchwork Thu Mar 10 17:32:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "brian m. carlson" X-Patchwork-Id: 12776819 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 71393C433FE for ; Thu, 10 Mar 2022 17:35:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245112AbiCJRg0 (ORCPT ); Thu, 10 Mar 2022 12:36:26 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36988 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233724AbiCJRgY (ORCPT ); Thu, 10 Mar 2022 12:36:24 -0500 Received: from ring.crustytoothpaste.net (ring.crustytoothpaste.net [172.105.110.227]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 13E6418C795 for ; Thu, 10 Mar 2022 09:35:22 -0800 (PST) Received: from camp.crustytoothpaste.net (unknown [IPv6:2001:470:b056:101:a6ae:7d13:8741:9028]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by ring.crustytoothpaste.net (Postfix) with ESMTPSA id 88A3B5A12B; Thu, 10 Mar 2022 17:35:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=crustytoothpaste.net; s=default; t=1646933721; bh=7VLueFEbbs0vhyNcvB8Ou4O9wtpDEONFhHScpNaOm+o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Reply-To: Subject:Date:To:CC:Resent-Date:Resent-From:Resent-To:Resent-Cc: In-Reply-To:References:Content-Type:Content-Disposition; b=DCCh9AjLuEqwPWFYNI1th31J8XodGSv23do35mW680bsF+BvO4hd1eBXE0qLSFrYn 1ZW12hzjIi/xnJo3UPpHFGEZzo8Nqqc2vjSnunDxl7ysFdVjgbopSMgBikTti079M7 koNGkJMPjAgJCQ/lqdIuwUE6/3v9pVLLYO9dXHVjz7brLbivw0ywtmihA9Zr2cnT+E scGQXzYNxfRinoCl7mOAwjTaqgnxug0WopSWxoRu5mgcCh7ZEV/8sfGHfym3NcwvOn 7zi2P907nYwOBMYMYW/SgIeJR/Gigquq5DC1VTx8/HWEDavq3XwNYv5txhqVb5QItl yTv/XnkRsYz5NlZjz6hx+UdNkDkk5eUGWmVYPUMIfKajB7sQGXqyFBWEcouMrRS+Tk 3HYFojQFAqnN+67SixGAdMTyyXKDDT53MyjcC4nkIi/CKwKg7ZzhZRo0ijI9N/phyF tTYRik8Ce8iM6RSvVPyT3Ppanq3msn/glbpQl60xdNJLorBekVM From: "brian m. carlson" To: Cc: Junio C Hamano , Derrick Stolee , Thomas Gummerer Subject: [PATCH 2/6] builtin/stash: fill in all commit data Date: Thu, 10 Mar 2022 17:32:32 +0000 Message-Id: <20220310173236.4165310-3-sandals@crustytoothpaste.net> X-Mailer: git-send-email 2.35.1.473.g83b2b277ed In-Reply-To: <20220310173236.4165310-1-sandals@crustytoothpaste.net> References: <20220310173236.4165310-1-sandals@crustytoothpaste.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org get_stash_info doesn't ensure that all entries are filled in in all cases. However, we'll want to use this information to write new commits, and when we do so, we'll need all the information to be present. Fill in all the commit information whenever we call this function. Note that the behavior of info->has_u doesn't change here. If we previously read a tree a refs/stash^3:, then refs/stash^3 must be a treeish. We already here assume that the other parents specifically commits, so it should be safe to do so here as well. Signed-off-by: brian m. carlson --- builtin/stash.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builtin/stash.c b/builtin/stash.c index 2aa06cc91d..128f0a01ef 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -124,6 +124,7 @@ static void free_stash_info(struct stash_info *info) static void assert_stash_like(struct stash_info *info, const char *revision) { if (get_oidf(&info->b_commit, "%s^1", revision) || + get_oidf(&info->i_commit, "%s^2", revision) || get_oidf(&info->w_tree, "%s:", revision) || get_oidf(&info->b_tree, "%s^1:", revision) || get_oidf(&info->i_tree, "%s^2:", revision)) @@ -166,7 +167,8 @@ static int get_stash_info_1(struct stash_info *info, const char *commit, int qui assert_stash_like(info, revision); - info->has_u = !get_oidf(&info->u_tree, "%s^3:", revision); + info->has_u = !get_oidf(&info->u_commit, "%s^3", revision) && + !get_oidf(&info->u_tree, "%s^3:", revision); end_of_rev = strchrnul(revision, '@'); strbuf_add(&symbolic, revision, end_of_rev - revision); From patchwork Thu Mar 10 17:32:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "brian m. carlson" X-Patchwork-Id: 12776821 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 61BD3C433F5 for ; Thu, 10 Mar 2022 17:35:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245136AbiCJRg3 (ORCPT ); Thu, 10 Mar 2022 12:36:29 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36992 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S245074AbiCJRgZ (ORCPT ); Thu, 10 Mar 2022 12:36:25 -0500 Received: from ring.crustytoothpaste.net (ring.crustytoothpaste.net [172.105.110.227]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 141E418F22A for ; Thu, 10 Mar 2022 09:35:22 -0800 (PST) Received: from camp.crustytoothpaste.net (unknown [IPv6:2001:470:b056:101:a6ae:7d13:8741:9028]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by ring.crustytoothpaste.net (Postfix) with ESMTPSA id 992B75A12D; Thu, 10 Mar 2022 17:35:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=crustytoothpaste.net; s=default; t=1646933721; bh=ltqJ6eVTTkdnHzkh4VXZsXx1qfTvUh7Tdrd7LZGMQKE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Reply-To: Subject:Date:To:CC:Resent-Date:Resent-From:Resent-To:Resent-Cc: In-Reply-To:References:Content-Type:Content-Disposition; b=Rc7KePfYJ+nVgVPqQ59M3cdhcs0QeqG3HT+AAquD0/nBxYEKbQVxBjOYcXxgfLrWT aH2uSTddEorNabp5kBi2mWNFi28r+f+2EMuSSa/mIBYm0fy1RGsD+QYTfFiUNUq1XD zMM8rPhCueprk+ZiGivPzU/7UFESdUb2o7jGLvzhx4w17Tm0svPh8kVgvnPA5FCh/m b0KBtZbcImHhIng1ubBDhSfFmCbLF2A1/udgn8770ThLT652NWs/53c4wePQzbNWak EE86Zi6pZw0GJ6APW2pfBDCR19m67LG2VJ/LJTafXWlOReBhwXnW+Ydjk81nxBTcgf GlnZQ3Soa4wfStAY6N7Ql2qC0fu0Qd0Cevp5gxPNg6Jm/YCeya7vw4dSfpqOzIZyYL a7G5otlxFWLc2BbiARQpPLZnmFdbVs614l1K7wm2erkKmPaLbPKs6gce51KJX+U64g hIBiGu94kTgvVVHBj1o6vMWtUj6Vfe3SlJDy8zKkMDx65LilBxw From: "brian m. carlson" To: Cc: Junio C Hamano , Derrick Stolee , Thomas Gummerer Subject: [PATCH 3/6] object-name: make get_oid quietly return an error Date: Thu, 10 Mar 2022 17:32:33 +0000 Message-Id: <20220310173236.4165310-4-sandals@crustytoothpaste.net> X-Mailer: git-send-email 2.35.1.473.g83b2b277ed In-Reply-To: <20220310173236.4165310-1-sandals@crustytoothpaste.net> References: <20220310173236.4165310-1-sandals@crustytoothpaste.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org A reasonable person looking at the signature and usage of get_oid and friends might conclude that in the event of an error, it always returns -1. However, this is not the case. Instead, get_oid_basic dies if we go too far back into the history of a reflog (or, when quiet, simply exits). This is not especially useful, since in many cases, we might want to handle this error differently. Let's add a flag here to make it just return -1 like elsewhere in these code paths. Note that we cannot make this behavior the default, since we have many other codepaths that rely on the existing behavior, including in tests. Signed-off-by: brian m. carlson --- cache.h | 21 +++++++++++---------- object-name.c | 6 +++++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/cache.h b/cache.h index 825ec17198..416a9d9983 100644 --- a/cache.h +++ b/cache.h @@ -1366,16 +1366,17 @@ struct object_context { char *path; }; -#define GET_OID_QUIETLY 01 -#define GET_OID_COMMIT 02 -#define GET_OID_COMMITTISH 04 -#define GET_OID_TREE 010 -#define GET_OID_TREEISH 020 -#define GET_OID_BLOB 040 -#define GET_OID_FOLLOW_SYMLINKS 0100 -#define GET_OID_RECORD_PATH 0200 -#define GET_OID_ONLY_TO_DIE 04000 -#define GET_OID_REQUIRE_PATH 010000 +#define GET_OID_QUIETLY 01 +#define GET_OID_COMMIT 02 +#define GET_OID_COMMITTISH 04 +#define GET_OID_TREE 010 +#define GET_OID_TREEISH 020 +#define GET_OID_BLOB 040 +#define GET_OID_FOLLOW_SYMLINKS 0100 +#define GET_OID_RECORD_PATH 0200 +#define GET_OID_ONLY_TO_DIE 04000 +#define GET_OID_REQUIRE_PATH 010000 +#define GET_OID_RETURN_FAILURE 020000 #define GET_OID_DISAMBIGUATORS \ (GET_OID_COMMIT | GET_OID_COMMITTISH | \ diff --git a/object-name.c b/object-name.c index 92862eeb1a..daa3ef77ef 100644 --- a/object-name.c +++ b/object-name.c @@ -911,13 +911,17 @@ static int get_oid_basic(struct repository *r, const char *str, int len, len, str, show_date(co_time, co_tz, DATE_MODE(RFC2822))); } - } else { + } else if (!(flags & GET_OID_RETURN_FAILURE)) { if (flags & GET_OID_QUIETLY) { exit(128); } die(_("log for '%.*s' only has %d entries"), len, str, co_cnt); } + if (flags & GET_OID_RETURN_FAILURE) { + free(real_ref); + return -1; + } } } From patchwork Thu Mar 10 17:32:34 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "brian m. carlson" X-Patchwork-Id: 12776822 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 5C23EC433EF for ; Thu, 10 Mar 2022 17:35:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245150AbiCJRga (ORCPT ); Thu, 10 Mar 2022 12:36:30 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36994 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S245058AbiCJRgZ (ORCPT ); Thu, 10 Mar 2022 12:36:25 -0500 Received: from ring.crustytoothpaste.net (ring.crustytoothpaste.net [IPv6:2600:3c04::f03c:92ff:fe9e:c6d8]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 13FAB18DA81 for ; Thu, 10 Mar 2022 09:35:22 -0800 (PST) Received: from camp.crustytoothpaste.net (unknown [IPv6:2001:470:b056:101:a6ae:7d13:8741:9028]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by ring.crustytoothpaste.net (Postfix) with ESMTPSA id A71635A12E; Thu, 10 Mar 2022 17:35:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=crustytoothpaste.net; s=default; t=1646933721; bh=a0CQQxaHZyWnkGFJB3iQONvSs77QhkcfLCSNf9pbhdU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:Content-Type:From: Reply-To:Subject:Date:To:CC:Resent-Date:Resent-From:Resent-To: Resent-Cc:In-Reply-To:References:Content-Type:Content-Disposition; b=e1GFeHndiKJVT4airOYr15qzPryT0e9fk42sCKj0lFnUz33yZtAAJ5mQ69pv83ul1 4d7mP6MFQkFJIhgoTPKgdUqkXHapT8JtUmYr3A++McS84SXCxaJlPDNZCf/ZSEQtzK P8PQHruH1VqBUtJ7qy0BcaGtl+iNKpilvIdEBEMMp/wzyIeQxOF3gJ1sn6F4TjXakY XbwxYFfu/Lcxd5GwnlYvkoeW7WzxvHRNFwxo3TOB4u9is6re7e/eV8P0Vg9F34OG4Y xkicRAyykiVsfVa8ouYau34QzzN3dDN28FHVkIsDikxX/zpiqrIbWfCBo9Blapl3Zk JHrnl6ie8KDnqwr3JHJg5zlAXRsGg8ZHqrrZd7VoB7MFbVGml68FEZn221ccZSsa4u gx0DquPvewU6nJXJLbZxRfpGPoJZfVO86ilF0YXUGqLBIV3DmpetL7znvU0DF0gq7k yA1BQE0lroPT/SUky3x3QrwMnJY/5AkAN9Z/D2zk9tmCi30klyn From: "brian m. carlson" To: Cc: Junio C Hamano , Derrick Stolee , Thomas Gummerer Subject: [PATCH 4/6] builtin/stash: provide a way to export stashes to a ref Date: Thu, 10 Mar 2022 17:32:34 +0000 Message-Id: <20220310173236.4165310-5-sandals@crustytoothpaste.net> X-Mailer: git-send-email 2.35.1.473.g83b2b277ed In-Reply-To: <20220310173236.4165310-1-sandals@crustytoothpaste.net> References: <20220310173236.4165310-1-sandals@crustytoothpaste.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org A common user problem is how to sync in-progress work to another machine. Users currently must use some sort of transfer of the working tree, which poses security risks and also necessarily causes the index to become dirty. The experience is suboptimal and frustrating for users. A reasonable idea is to use the stash for this purpose, but the stash is stored in the reflog, not in a ref, and as such it cannot be pushed or pulled. This also means that it cannot be saved into a bundle or preserved elsewhere, which is a problem when using throwaway development environments. Let's solve this problem by allowing the user to export the stash to a ref (or, to just write it into the repository and print the hash, à la git commit-tree). Introduce git stash export, which writes a chain of commits identical to the ones used for reflog-based stashes, except that the first parent is always a chain to the previous stash, or to a single, empty commit (for the final item). Iterate over each stash from topmost to bottomost, looking up the data for each one, and then create the chain from the single empty commit back up in reverse order. Preserve the author and committer information, as well as the commit message, so we produce an identical stash when importing later. If the user has specified specific stashes they'd like to export instead, use those instead of iterating over all of the stashes. As part of this, specifically request quiet behavior when looking up the OID for a revision because we will eventually hit a revision that doesn't exist and we don't want to die when that occurs. Signed-off-by: brian m. carlson --- builtin/stash.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 1 deletion(-) diff --git a/builtin/stash.c b/builtin/stash.c index 128f0a01ef..582a04dbab 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -33,6 +33,7 @@ static const char * const git_stash_usage[] = { " [--] [...]]"), N_("git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n" " [-u|--include-untracked] [-a|--all] []"), + N_("git stash export (--print | --to-ref ) []"), NULL }; @@ -89,6 +90,12 @@ static const char * const git_stash_save_usage[] = { NULL }; +static const char * const git_stash_export_usage[] = { + N_("git stash export (--print | --to-ref ) []"), + NULL +}; + + static const char ref_stash[] = "refs/stash"; static struct strbuf stash_index_path = STRBUF_INIT; @@ -139,6 +146,7 @@ static int get_stash_info_1(struct stash_info *info, const char *commit, int qui const char *revision; struct object_id dummy; struct strbuf symbolic = STRBUF_INIT; + struct object_context unused; strbuf_init(&info->revision, 0); if (!commit) { @@ -158,7 +166,9 @@ static int get_stash_info_1(struct stash_info *info, const char *commit, int qui revision = info->revision.buf; - if (get_oid(revision, &info->w_commit)) { + if (get_oid_with_context(the_repository, revision, + GET_OID_QUIETLY | GET_OID_RETURN_FAILURE, + &info->w_commit, &unused)) { if (!quiet) error(_("%s is not a valid reference"), revision); free_stash_info(info); @@ -1775,6 +1785,172 @@ static int save_stash(int argc, const char **argv, const char *prefix) return ret; } +static char *write_commit_with_parents(struct object_id *out, const struct stash_info *info, struct commit_list *parents) +{ + size_t author_len, committer_len; + struct commit *this = NULL; + const char *orig_author = NULL, *orig_committer = NULL; + char *author = NULL, *committer = NULL; + const char *buffer = NULL; + unsigned long bufsize; + const char *p; + char *msg = NULL; + + this = lookup_commit_reference(the_repository, &info->w_commit); + buffer = get_commit_buffer(this, &bufsize); + orig_author = find_commit_header(buffer, "author", &author_len); + orig_committer = find_commit_header(buffer, "committer", &committer_len); + p = memmem(buffer, bufsize, "\n\n", 2); + + if (!orig_author || !orig_committer || !p) { + error(_("cannot parse commit %s"), oid_to_hex(&info->w_commit)); + goto out; + } + /* Jump to message. */ + p += 2; + + author = xmemdupz(orig_author, author_len); + committer = xmemdupz(orig_committer, committer_len); + + if (commit_tree_extended(p, bufsize - (p - buffer), + &info->w_tree, parents, + out, author, committer, + NULL, NULL)) { + error(_("could not write commit")); + goto out; + } + msg = xmemdupz(p, bufsize - (p - buffer)); +out: + unuse_commit_buffer(this, buffer); + free(author); + free(committer); + return msg; +} + +static int do_export_stash(const char *ref, size_t argc, const char **argv) +{ + struct object_id base; + struct commit *prev; + struct stash_info *items = NULL; + size_t nitems = 0, nalloc = 0; + int res = 0; + + prepare_fallback_ident("git stash", "git@stash"); + + /* First, we create a single empty commit. */ + if (commit_tree(NULL, 0, the_hash_algo->empty_tree, NULL, &base, NULL, NULL)) + return error(_("unable to write base commit")); + + prev = lookup_commit_reference(the_repository, &base); + + + if (argc) { + /* + * Find each specified stash, and load data into the array. + */ + for (size_t i = 0; i < argc; i++, nitems++) { + int ret; + + if (nalloc <= i) { + size_t new = nalloc * 3 / 2 + 5; + items = xrealloc(items, new * sizeof(*items)); + nalloc = new; + } + memset(&items[i], 0, sizeof(*items)); + /* We want this to be quiet because it might not exist. */ + ret = get_stash_info_1(&items[i], argv[i], 1); + if (ret) + return error(_("unable to find stash entry %s"), argv[i]); + } + } else { + /* + * Walk the reflog, finding each stash entry, and load data into the + * array. + */ + for (size_t i = 0;; i++, nitems++) { + char buf[32]; + int ret; + + if (nalloc <= i) { + size_t new = nalloc * 3 / 2 + 5; + items = xrealloc(items, new * sizeof(*items)); + nalloc = new; + } + snprintf(buf, sizeof(buf), "%zu", i); + memset(&items[i], 0, sizeof(*items)); + /* We want this to be quiet because it might not exist. */ + ret = get_stash_info_1(&items[i], buf, 1); + if (ret) + break; + } + } + + /* + * Now, create a set of commits identical to the regular stash commits, + * but where their first parents form a chain to our original empty + * base commit. + */ + for (ssize_t i = nitems - 1; i >= 0; i--) { + struct commit_list *parents = NULL; + struct commit_list **next = &parents; + struct object_id out; + char *msg; + + next = commit_list_append(prev, next); + next = commit_list_append(lookup_commit_reference(the_repository, &items[i].b_commit), next); + next = commit_list_append(lookup_commit_reference(the_repository, &items[i].i_commit), next); + if (items[i].has_u) + next = commit_list_append(lookup_commit_reference(the_repository, + &items[i].u_commit), + next); + + msg = write_commit_with_parents(&out, &items[i], parents); + if (!msg) { + res = -1; + goto out; + } + free(msg); + prev = lookup_commit_reference(the_repository, &out); + } + if (ref) { + update_ref(NULL, ref, &prev->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); + } else { + puts(oid_to_hex(&prev->object.oid)); + } +out: + for (size_t i = 0; i < nitems; i++) { + free_stash_info(&items[i]); + } + free(items); + + return res; +} + +static int export_stash(int argc, const char **argv, const char *prefix) +{ + int ret = 0; + int print = 0; + const char *ref = NULL; + struct option options[] = { + OPT_BOOL(0, "print", &print, + N_("print the object ID instead of writing it to a ref")), + OPT_STRING(0, "to-ref", &ref, "refname", + N_("save the data to the given ref")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, + git_stash_export_usage, + PARSE_OPT_KEEP_DASHDASH); + + if (!(!!ref ^ print)) + return error("exactly one of --print or --to-ref is required"); + + + ret = do_export_stash(ref, argc, argv); + return ret; +} + int cmd_stash(int argc, const char **argv, const char *prefix) { pid_t pid = getpid(); @@ -1818,6 +1994,8 @@ int cmd_stash(int argc, const char **argv, const char *prefix) return !!push_stash(argc, argv, prefix, 0); else if (!strcmp(argv[0], "save")) return !!save_stash(argc, argv, prefix); + else if (!strcmp(argv[0], "export")) + return !!export_stash(argc, argv, prefix); else if (*argv[0] != '-') usage_msg_optf(_("unknown subcommand: %s"), git_stash_usage, options, argv[0]); From patchwork Thu Mar 10 17:32:35 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "brian m. carlson" X-Patchwork-Id: 12776824 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 987D4C433F5 for ; Thu, 10 Mar 2022 17:35:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245158AbiCJRgd (ORCPT ); Thu, 10 Mar 2022 12:36:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37034 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S245109AbiCJRg0 (ORCPT ); Thu, 10 Mar 2022 12:36:26 -0500 Received: from ring.crustytoothpaste.net (ring.crustytoothpaste.net [172.105.110.227]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BA55C18C795 for ; Thu, 10 Mar 2022 09:35:24 -0800 (PST) Received: from camp.crustytoothpaste.net (unknown [IPv6:2001:470:b056:101:a6ae:7d13:8741:9028]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by ring.crustytoothpaste.net (Postfix) with ESMTPSA id B6DBC5A12F; Thu, 10 Mar 2022 17:35:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=crustytoothpaste.net; s=default; t=1646933721; bh=L1GXRSsBQxSXQ74Kxn+949Y15PrCBVGeG87V2LPxb8k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Reply-To: Subject:Date:To:CC:Resent-Date:Resent-From:Resent-To:Resent-Cc: In-Reply-To:References:Content-Type:Content-Disposition; b=EQzDvkVv5j1Nmj0aQIjTytvtWJfFdzRJ3GWQvO0FCYj/g6Ws4umJGjywwXn0axNms B20L9mX5jDf1IsVPKK6jt39BxLbYl4q8sv3PFWyjH3P/mLarxo4pxk5ntK0+yWMB+G Qyd6/wOpJGvxbkSWSa/l4CmC+XWfQP7ldN/ePHGxW4hS9iIRlMRHR2xahLqX/RE6Mx 1XS21TzaEeI8ZDDL8O85m+1caPJ1cSynueQ2FYiCmiUKQcenhk0xHqgfKTqRyISDTP qPx3a2G8j7MMoyWpfTjUct6tl59Bg4SnQVUlD6h/6OnUdB+3te92kJai12nIEPx2ue yQxaz9xSLu4aHP0hh3LYw9vPD+37tXwuHsH19gWHBcbm2ozBtSsytTUO7VfuIsY9Ti hzt+FAS3U2O2cp2cYm4TQTZG+3Xcz2l2l8RY4+nrFHkfiM683dclbY98eYmYrU0pdx ilWNNfEFZG8mmsCN5Y5HfAkbu3phXdHe75h2qc8FKZ4pr6GfTKh From: "brian m. carlson" To: Cc: Junio C Hamano , Derrick Stolee , Thomas Gummerer Subject: [PATCH 5/6] builtin/stash: provide a way to import stashes from a ref Date: Thu, 10 Mar 2022 17:32:35 +0000 Message-Id: <20220310173236.4165310-6-sandals@crustytoothpaste.net> X-Mailer: git-send-email 2.35.1.473.g83b2b277ed In-Reply-To: <20220310173236.4165310-1-sandals@crustytoothpaste.net> References: <20220310173236.4165310-1-sandals@crustytoothpaste.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Now that we have a way to export stashes to a ref, let's provide a way to import them from such a ref back to the stash. This works much the way the export code does, except that we strip off the first parent chain commit and then store each resulting commit back to the stash. We don't clear the stash first and instead add the specified stashes to the top of the stash. This is because users may want to export just a few stashes, such as to share a small amount of work in progress with a colleague, and it would be undesirable for the receiving user to lose all of their data. For users who do want to replace the stash, it's easy to do to: simply run "git stash clear" first. We specifically rely on the fact that we'll produce identical stash commits on both sides in our tests. This provides a cheap, straightforward check for our tests and also makes it easy for users to see if they already have the same data in both repositories. Note that the exported commits don't because we don't write a predictable base commit, however. Signed-off-by: brian m. carlson --- builtin/stash.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++ t/t3903-stash.sh | 52 ++++++++++++++++++++ 2 files changed, 177 insertions(+) diff --git a/builtin/stash.c b/builtin/stash.c index 582a04dbab..626e7b8531 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -34,6 +34,7 @@ static const char * const git_stash_usage[] = { N_("git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n" " [-u|--include-untracked] [-a|--all] []"), N_("git stash export (--print | --to-ref ) []"), + N_("git stash import "), NULL }; @@ -95,6 +96,10 @@ static const char * const git_stash_export_usage[] = { NULL }; +static const char * const git_stash_import_usage[] = { + N_("git stash import "), + NULL +}; static const char ref_stash[] = "refs/stash"; static struct strbuf stash_index_path = STRBUF_INIT; @@ -104,6 +109,7 @@ static struct strbuf stash_index_path = STRBUF_INIT; * b_commit is set to the base commit * i_commit is set to the commit containing the index tree * u_commit is set to the commit containing the untracked files tree + * c_commit is set to the first parent (chain commit) when importing and is otherwise unset * w_tree is set to the working tree * b_tree is set to the base tree * i_tree is set to the index tree @@ -114,6 +120,7 @@ struct stash_info { struct object_id b_commit; struct object_id i_commit; struct object_id u_commit; + struct object_id c_commit; struct object_id w_tree; struct object_id b_tree; struct object_id i_tree; @@ -138,6 +145,32 @@ static void assert_stash_like(struct stash_info *info, const char *revision) die(_("'%s' is not a stash-like commit"), revision); } +static int get_stash_info_for_import(struct stash_info *info, const struct object_id *oid) +{ + int has_parents; + char hexoid[GIT_MAX_HEXSZ + 1]; + oid_to_hex_r(hexoid, oid); + + oidcpy(&info->w_commit, oid); + if (get_oidf(&info->w_tree, "%s:", hexoid)) + return -1; + + has_parents = !get_oidf(&info->c_commit, "%s^1", hexoid); + + /* If this tree is the empty one and we have no parents, we've reached the end. */ + if (oideq(&info->w_tree, the_hash_algo->empty_tree) && !has_parents) + return 1; + if (get_oidf(&info->b_commit, "%s^2", hexoid) || + get_oidf(&info->i_commit, "%s^3", hexoid) || + get_oidf(&info->b_tree, "%s^2:", hexoid) || + get_oidf(&info->i_tree, "%s^3:", hexoid)) + return -1; + + info->has_u = !get_oidf(&info->u_commit, "%s^4", hexoid) && + !get_oidf(&info->u_tree, "%s^4:", hexoid); + return 0; +} + static int get_stash_info_1(struct stash_info *info, const char *commit, int quiet) { int ret; @@ -1827,6 +1860,96 @@ static char *write_commit_with_parents(struct object_id *out, const struct stash return msg; } +static int do_import_stash(const char *rev) +{ + struct object_id oid; + size_t nitems = 0, nalloc = 0; + struct stash_info *items = NULL; + int res = 0; + + if (get_oid(rev, &oid)) + return error(_("not a valid revision: %s"), rev); + + /* + * Walk the commit history, finding each stash entry, and load data into + * the array. + */ + for (size_t i = 0;; i++, nitems++) { + int ret; + + if (nalloc <= i) { + size_t new = nalloc * 3 / 2 + 5; + items = xrealloc(items, new * sizeof(*items)); + nalloc = new; + } + memset(&items[i], 0, sizeof(*items)); + /* We want this to be quiet because it might not exist. */ + ret = get_stash_info_for_import(&items[i], &oid); + if (ret < 0) + return error(_("%s is not a valid exported stash commit"), oid_to_hex(&oid)); + if (ret) + break; + oidcpy(&oid, &items[i].c_commit); + } + + /* + * Now, walk each entry, adding it to the stash as a normal stash + * commit. + */ + for (ssize_t i = nitems - 1; i >= 0; i--) { + struct commit_list *parents = NULL; + struct commit_list **next = &parents; + struct object_id out; + char *msg; + + next = commit_list_append(lookup_commit_reference(the_repository, &items[i].b_commit), next); + next = commit_list_append(lookup_commit_reference(the_repository, &items[i].i_commit), next); + if (items[i].has_u) + next = commit_list_append(lookup_commit_reference(the_repository, + &items[i].u_commit), + next); + + msg = write_commit_with_parents(&out, &items[i], parents); + if (!msg) { + res = -1; + goto out; + } + if (do_store_stash(&out, msg, 1)) { + free(msg); + res = -1; + error(_("Cannot save the current status")); + goto out; + } + free(msg); + } +out: + for (size_t i = 0; i < nitems; i++) { + free_stash_info(&items[i]); + } + free(items); + + return res; +} + +static int import_stash(int argc, const char **argv, const char *prefix) +{ + int ret = 0; + struct option options[] = { + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, + git_stash_import_usage, + PARSE_OPT_KEEP_DASHDASH); + + if (argc != 1) + return error(_("a revision to import from is required")); + + + ret = do_import_stash(argv[0]); + return ret; +} + static int do_export_stash(const char *ref, size_t argc, const char **argv) { struct object_id base; @@ -1996,6 +2119,8 @@ int cmd_stash(int argc, const char **argv, const char *prefix) return !!save_stash(argc, argv, prefix); else if (!strcmp(argv[0], "export")) return !!export_stash(argc, argv, prefix); + else if (!strcmp(argv[0], "import")) + return !!import_stash(argc, argv, prefix); else if (*argv[0] != '-') usage_msg_optf(_("unknown subcommand: %s"), git_stash_usage, options, argv[0]); diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index b149e2af44..d2ddede9be 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -1295,6 +1295,58 @@ test_expect_success 'stash --keep-index with file deleted in index does not resu test_path_is_missing to-remove ' +test_expect_success 'stash export and import round-trip stashes' ' + git reset && + >untracked && + >tracked1 && + >tracked2 && + git add tracked* && + git stash -- && + >subdir/untracked && + >subdir/tracked1 && + >subdir/tracked2 && + git add subdir/tracked* && + git stash -- subdir/ && + stash0=$(git rev-parse --verify stash@{0}) && + stash1=$(git rev-parse --verify stash@{1}) && + simple=$(git stash export --print) && + git stash clear && + git stash import "$simple" && + imported0=$(git rev-parse --verify stash@{0}) && + imported1=$(git rev-parse --verify stash@{1}) && + test "$imported0" = "$stash0" && + test "$imported1" = "$stash1" && + git stash export --to-ref refs/heads/foo && + git stash clear && + git stash import foo && + imported0=$(git rev-parse --verify stash@{0}) && + imported1=$(git rev-parse --verify stash@{1}) && + test "$imported0" = "$stash0" && + test "$imported1" = "$stash1" +' + +test_expect_success 'stash import appends commits' ' + git log --format=oneline -g refs/stash >actual && + echo $(cat actual | wc -l) >count && + git stash import refs/heads/foo && + git log --format=oneline -g refs/stash >actual && + test_line_count = $(($(cat count) * 2)) actual +' + +test_expect_success 'stash export can accept specified stashes' ' + git stash clear && + git stash import foo && + git stash export --to-ref bar stash@{1} stash@{0} && + git stash clear && + git stash import bar && + imported0=$(git rev-parse --verify stash@{0}) && + imported1=$(git rev-parse --verify stash@{1}) && + test "$imported1" = "$stash0" && + test "$imported0" = "$stash1" && + git log --format=oneline -g refs/stash >actual && + test_line_count = 2 actual +' + test_expect_success 'stash apply should succeed with unmodified file' ' echo base >file && git add file && From patchwork Thu Mar 10 17:32:36 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "brian m. carlson" X-Patchwork-Id: 12776823 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 25051C433FE for ; Thu, 10 Mar 2022 17:35:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245154AbiCJRgc (ORCPT ); Thu, 10 Mar 2022 12:36:32 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37018 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S243214AbiCJRgZ (ORCPT ); Thu, 10 Mar 2022 12:36:25 -0500 Received: from ring.crustytoothpaste.net (ring.crustytoothpaste.net [IPv6:2600:3c04::f03c:92ff:fe9e:c6d8]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BA28F18A783 for ; Thu, 10 Mar 2022 09:35:24 -0800 (PST) Received: from camp.crustytoothpaste.net (unknown [IPv6:2001:470:b056:101:a6ae:7d13:8741:9028]) (using TLSv1.2 with cipher ECDHE-RSA-CHACHA20-POLY1305 (256/256 bits)) (No client certificate requested) by ring.crustytoothpaste.net (Postfix) with ESMTPSA id C6ED75A130; Thu, 10 Mar 2022 17:35:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=crustytoothpaste.net; s=default; t=1646933721; bh=6buXR4uOxTjS4ym5fXokbQ+IJ9qja4KkXgNOi8DH2xc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From:Reply-To: Subject:Date:To:CC:Resent-Date:Resent-From:Resent-To:Resent-Cc: In-Reply-To:References:Content-Type:Content-Disposition; b=esjvAZMglmoCHjK2HCXMX/UZ7++pq9S2UIx1vogpSvraIrs15HItoecOjfwPfwKvn 4k8OfwvB8cyY1dUqZtHlIt5qUw41usvjYxt2m2j/UmQPldmUPsXveyoDjVOXbtSuB5 Tc2dHgZFsZCkL+7CAFZEoqzUxsUbWewrkDkhS1vjpHId0L7P/MvMFnmDfBwhwgtjSC Zp4U99sImlPa+cxfIbkDRsB9sV+Uv2hkuXf46geJKhUFqAsd9uPiMyaDsm/T3wVp0H Dn1fQJb63qOR3VtIfxZS3mVX9x4kIX0QVqv5+c3so/XaR2GFvgtyigGfZsirefps8l tvHmQ1d0OpsijIbWDYU6v8w41isqzNf6UXVG3zlr9neq9V2zBjTUBag0CrGdnQcoyT 6pU1WWkX5eXzA0935qN/FBxRj67Fii5V5Wvw7iTnKB2Lp4uqkCndkDpCpCPUN/LeUr 26a635gjM/V7aBscbJ24/vRIec4e86z0Qf4U6yQWHV/xhlfmc+2 From: "brian m. carlson" To: Cc: Junio C Hamano , Derrick Stolee , Thomas Gummerer Subject: [PATCH 6/6] doc: add stash export and import to docs Date: Thu, 10 Mar 2022 17:32:36 +0000 Message-Id: <20220310173236.4165310-7-sandals@crustytoothpaste.net> X-Mailer: git-send-email 2.35.1.473.g83b2b277ed In-Reply-To: <20220310173236.4165310-1-sandals@crustytoothpaste.net> References: <20220310173236.4165310-1-sandals@crustytoothpaste.net> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Now that we have an easy way for users to import and export their stashes, let's document this in manual page so users will know how to use it. Signed-off-by: brian m. carlson --- Documentation/git-stash.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index 6e15f47525..283677314a 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -20,6 +20,8 @@ SYNOPSIS 'git stash' clear 'git stash' create [] 'git stash' store [-m|--message ] [-q|--quiet] +'git stash' export ( --print | --to-ref ) [] +'git stash' import DESCRIPTION ----------- @@ -151,6 +153,18 @@ store:: reflog. This is intended to be useful for scripts. It is probably not the command you want to use; see "push" above. +export ( --print | --to-ref ) []:: + + Export the specified stashes, or all of them if none are specified, to + a chain of commits which can be transferred using the normal fetch and + push mechanisms, then imported using the `import` subcommand. + +import :: + + Import the specified stashes from the specified commit, which must have been + created by `export`, and add them to the list of stashes. To replace the + existing stashes, use `clear` first. + OPTIONS ------- -a:: @@ -239,6 +253,19 @@ literally (including newlines and quotes). + Quiet, suppress feedback messages. +--print:: + This option is only valid for `export`. ++ +Create the chain of commits representing the exported stashes without +storing it anywhere in the ref namespace and print the object ID to +standard output. This is designed for scripts. + +--to-ref:: + This option is only valid for `export`. ++ +Create the chain of commits representing the exported stashes and store +it to the specified ref. + \--:: This option is only valid for `push` command. +