From: "Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Harald Nordgren <haraldnordgren@gmail.com>
Subject: [PATCH v6 0/4] checkout: 'autostash' for branch switching
Date: Tue, 17 Mar 2026 09:35:35 +0000 [thread overview]
Message-ID: <pull.2234.v6.git.git.1773740139.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2234.v5.git.git.1773573553.gitgitgadget@gmail.com>
cc: Phillip Wood phillip.wood123@gmail.com
Harald Nordgren (4):
stash: add --ours-label, --theirs-label, --base-label for apply
sequencer: allow create_autostash to run silently
sequencer: teach autostash apply to take optional conflict marker
labels
checkout: -m (--merge) uses autostash when switching branches
Documentation/git-checkout.adoc | 58 ++++++------
Documentation/git-stash.adoc | 11 ++-
Documentation/git-switch.adoc | 27 +++---
builtin/checkout.c | 137 ++++++++++++---------------
builtin/stash.c | 32 +++++--
sequencer.c | 67 +++++++++----
sequencer.h | 4 +
t/t3420-rebase-autostash.sh | 24 +++--
t/t3903-stash.sh | 18 ++++
t/t7201-co.sh | 160 ++++++++++++++++++++++++++++++++
t/t7600-merge.sh | 2 +-
xdiff-interface.c | 12 +++
xdiff-interface.h | 1 +
13 files changed, 403 insertions(+), 150 deletions(-)
base-commit: ca1db8a0f7dc0dbea892e99f5b37c5fe5861be71
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2234%2FHaraldNordgren%2Fcheckout_autostash-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2234/HaraldNordgren/checkout_autostash-v6
Pull-Request: https://github.com/git/git/pull/2234
Range-diff vs v5:
1: 00d8920498 = 1: cd9c64ba60 stash: add --ours-label, --theirs-label, --base-label for apply
2: 5d176f1700 = 2: d572c4bb7d sequencer: allow create_autostash to run silently
3: 3d6829438a = 3: 80a98116fc sequencer: teach autostash apply to take optional conflict marker labels
4: 7f3735d40e ! 4: 7ecb0835b7 checkout: -m (--merge) uses autostash when switching branches
@@ builtin/checkout.c
#include "submodule.h"
#include "symlinks.h"
@@ builtin/checkout.c: static int merge_working_tree(const struct checkout_opts *opts,
+ struct tree *new_tree;
+ repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
+- if (repo_read_index_preload(the_repository, NULL, 0) < 0)
++ if (repo_read_index_preload(the_repository, NULL, 0) < 0) {
++ rollback_lock_file(&lock_file);
+ return error(_("index file corrupt"));
++ }
+
+ resolve_undo_clear_index(the_repository->index);
+ if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
+@@ builtin/checkout.c: static int merge_working_tree(const struct checkout_opts *opts,
+ } else {
+ new_tree = repo_get_commit_tree(the_repository,
+ new_branch_info->commit);
+- if (!new_tree)
++ if (!new_tree) {
++ rollback_lock_file(&lock_file);
+ return error(_("unable to read tree (%s)"),
+ oid_to_hex(&new_branch_info->commit->object.oid));
++ }
+ }
+ if (opts->discard_changes) {
+ ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info);
+- if (ret)
++ if (ret) {
++ rollback_lock_file(&lock_file);
+ return ret;
++ }
+ } else {
+ struct tree_desc trees[2];
+ struct tree *tree;
+@@ builtin/checkout.c: static int merge_working_tree(const struct checkout_opts *opts,
+ refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
+
+ if (unmerged_index(the_repository->index)) {
++ rollback_lock_file(&lock_file);
+ error(_("you need to resolve your current index first"));
+ return 1;
+ }
+@@ builtin/checkout.c: static int merge_working_tree(const struct checkout_opts *opts,
ret = unpack_trees(2, trees, &topts);
clear_unpack_trees_porcelain(&topts);
-- if (ret == -1) {
+ if (ret == -1) {
- /*
- * Unpack couldn't do a trivial merge; either
- * give up or do a real merge, depending on
@@ builtin/checkout.c: static int merge_working_tree(const struct checkout_opts *op
- strbuf_release(&old_commit_shortname);
- if (ret)
- return ret;
-- }
-+ if (ret == -1)
++ rollback_lock_file(&lock_file);
+ return 1;
+ }
}
- if (!cache_tree_fully_valid(the_repository->index->cache_tree))
-@@ builtin/checkout.c: static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
- release_revisions(&revs);
- }
-
-+static int checkout_would_clobber_changes(struct branch_info *old_branch_info,
-+ struct branch_info *new_branch_info)
-+{
-+ struct tree_desc trees[2];
-+ struct tree *old_tree, *new_tree;
-+ struct unpack_trees_options topts;
-+ struct index_state tmp_index = INDEX_STATE_INIT(the_repository);
-+ const struct object_id *old_commit_oid;
-+ int ret;
-+
-+ if (!new_branch_info->commit)
-+ return 0;
-+
-+ old_commit_oid = old_branch_info->commit ?
-+ &old_branch_info->commit->object.oid :
-+ the_hash_algo->empty_tree;
-+ old_tree = repo_parse_tree_indirect(the_repository, old_commit_oid);
-+ if (!old_tree)
-+ return 0;
-+
-+ new_tree = repo_get_commit_tree(the_repository,
-+ new_branch_info->commit);
-+ if (!new_tree)
-+ return 0;
-+ if (repo_parse_tree(the_repository, new_tree) < 0)
-+ return 0;
-+
-+ memset(&topts, 0, sizeof(topts));
-+ topts.head_idx = -1;
-+ topts.src_index = the_repository->index;
-+ topts.dst_index = &tmp_index;
-+ topts.initial_checkout = is_index_unborn(the_repository->index);
-+ topts.merge = 1;
-+ topts.update = 1;
-+ topts.dry_run = 1;
-+ topts.quiet = 1;
-+ topts.fn = twoway_merge;
-+
-+ init_tree_desc(&trees[0], &old_tree->object.oid,
-+ old_tree->buffer, old_tree->size);
-+ init_tree_desc(&trees[1], &new_tree->object.oid,
-+ new_tree->buffer, new_tree->size);
-+
-+ ret = unpack_trees(2, trees, &topts);
-+ discard_index(&tmp_index);
-+
-+ return ret != 0;
-+}
-+
- static int switch_branches(const struct checkout_opts *opts,
- struct branch_info *new_branch_info)
- {
@@ builtin/checkout.c: static int switch_branches(const struct checkout_opts *opts,
struct object_id rev;
int flag, writeout_error = 0;
@@ builtin/checkout.c: static int switch_branches(const struct checkout_opts *opts,
+ stash_label_ancestor = old_commit_shortname.buf;
+ }
+
-+ if (opts->merge) {
-+ if (repo_read_index(the_repository) < 0)
-+ die(_("index file corrupt"));
-+ if (checkout_would_clobber_changes(&old_branch_info,
-+ new_branch_info)) {
+ if (do_merge) {
+ ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
++ if (ret && opts->merge) {
+ create_autostash_ref_silent(the_repository,
+ "CHECKOUT_AUTOSTASH");
+ created_autostash = 1;
++ ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
+ }
-+ }
-+
- if (do_merge) {
- ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
if (ret) {
+ apply_autostash_ref_with_labels(the_repository,
+ "CHECKOUT_AUTOSTASH",
--
gitgitgadget
next prev parent reply other threads:[~2026-03-17 9:35 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-12 13:26 [PATCH] checkout: add --autostash option for branch switching Harald Nordgren via GitGitGadget
2026-03-12 14:40 ` Junio C Hamano
2026-03-12 19:33 ` [PATCH v31 0/2] status: add status.compareBranches config for multiple branch comparisons Harald Nordgren
2026-03-13 14:29 ` [PATCH] checkout: add --autostash option for branch switching Phillip Wood
2026-03-14 17:17 ` Junio C Hamano
2026-03-16 16:36 ` Phillip Wood
2026-03-16 20:04 ` Junio C Hamano
2026-03-17 9:47 ` Harald Nordgren
2026-03-19 8:25 ` Harald Nordgren
2026-03-19 16:48 ` Junio C Hamano
2026-03-12 19:33 ` [PATCH v2] " Harald Nordgren via GitGitGadget
2026-03-12 19:50 ` Junio C Hamano
2026-03-13 9:22 ` [PATCH] " Harald Nordgren
2026-03-13 9:23 ` [PATCH v3] " Harald Nordgren via GitGitGadget
2026-03-13 17:16 ` Junio C Hamano
2026-03-13 19:33 ` [PATCH] " Harald Nordgren
2026-03-13 20:30 ` Junio C Hamano
2026-03-14 9:59 ` [PATCH v4] checkout: -m (--merge) uses autostash when switching branches Harald Nordgren via GitGitGadget
2026-03-15 2:25 ` Junio C Hamano
2026-03-15 11:19 ` [PATCH v5 0/4] checkout: 'autostash' for branch switching Harald Nordgren via GitGitGadget
2026-03-15 11:19 ` [PATCH v5 1/4] stash: add --ours-label, --theirs-label, --base-label for apply Harald Nordgren via GitGitGadget
2026-03-15 11:19 ` [PATCH v5 2/4] sequencer: allow create_autostash to run silently Harald Nordgren via GitGitGadget
2026-03-15 11:19 ` [PATCH v5 3/4] sequencer: teach autostash apply to take optional conflict marker labels Harald Nordgren via GitGitGadget
2026-03-15 11:19 ` [PATCH v5 4/4] checkout: -m (--merge) uses autostash when switching branches Harald Nordgren via GitGitGadget
2026-03-17 9:35 ` Harald Nordgren via GitGitGadget [this message]
2026-03-17 9:35 ` [PATCH v6 1/4] stash: add --ours-label, --theirs-label, --base-label for apply Harald Nordgren via GitGitGadget
2026-03-17 9:35 ` [PATCH v6 2/4] sequencer: allow create_autostash to run silently Harald Nordgren via GitGitGadget
2026-03-17 9:35 ` [PATCH v6 3/4] sequencer: teach autostash apply to take optional conflict marker labels Harald Nordgren via GitGitGadget
2026-03-17 9:35 ` [PATCH v6 4/4] checkout: -m (--merge) uses autostash when switching branches Harald Nordgren via GitGitGadget
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=pull.2234.v6.git.git.1773740139.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=haraldnordgren@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox