public inbox for git@vger.kernel.org
 help / color / mirror / Atom feed
From: "Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Harald Nordgren <haraldnordgren@gmail.com>
Subject: [PATCH v5 0/4] checkout: 'autostash' for branch switching
Date: Sun, 15 Mar 2026 11:19:09 +0000	[thread overview]
Message-ID: <pull.2234.v5.git.git.1773573553.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.2234.v4.git.git.1773482375668.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              | 180 ++++++++++++++++++--------------
 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, 447 insertions(+), 149 deletions(-)


base-commit: dc6ecd5354dca88d51b6d6562777fc8fc10d77e1
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2234%2FHaraldNordgren%2Fcheckout_autostash-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2234/HaraldNordgren/checkout_autostash-v5
Pull-Request: https://github.com/git/git/pull/2234

Range-diff vs v4:

 -:  ---------- > 1:  00d8920498 stash: add --ours-label, --theirs-label, --base-label for apply
 -:  ---------- > 2:  5d176f1700 sequencer: allow create_autostash to run silently
 -:  ---------- > 3:  3d6829438a sequencer: teach autostash apply to take optional conflict marker labels
 1:  5d49c0031a ! 4:  7f3735d40e checkout: -m (--merge) uses autostash when switching branches
     @@ Documentation/git-checkout.adoc: working tree, by copying them from elsewhere, e
      +	afterwards.  If the local changes do not overlap with the
      +	differences between branches, the switch proceeds without
      +	stashing.  If reapplying the stash results in conflicts, the
     -+	entry is saved to the stash list so you can use `git stash
     -+	pop` to recover and `git stash drop` when done.
     ++	entry is saved to the stash list.  Resolve the conflicts
     ++	and run `git stash drop` when done, or clear the working
     ++	tree (e.g. with `git reset --hard`) before running `git stash
     ++	pop` later to re-apply your changes.
       +
       When checking out paths from the index, this option lets you recreate
       the conflicted merge in the specified paths.  This option cannot be
     @@ Documentation/git-checkout.adoc: $ git checkout mytopic
       
      -You can give the `-m` flag to the command, which would try a
      -three-way merge:
     -+You can give the `-m` flag to the command, which would save the local
     -+changes in a stash entry and reset the working tree to allow switching:
     ++You can give the `-m` flag to the command, which would carry your local
     ++changes to the new branch:
       
       ------------
       $ git checkout -m mytopic
      -Auto-merging frotz
     -+Applied autostash.
     ++Switched to branch 'mytopic'
       ------------
       
      -After this three-way merge, the local modifications are _not_
     @@ Documentation/git-switch.adoc: variable.
      +	local changes do not overlap with the differences between
      +	branches, the switch proceeds without stashing.  If
      +	reapplying the stash results in conflicts, the entry is
     -+	saved to the stash list so you can use `git stash pop` to
     -+	recover and `git stash drop` when done.
     ++	saved to the stash list.  Resolve the conflicts and run
     ++	`git stash drop` when done, or clear the working tree
     ++	(e.g. with `git reset --hard`) before running `git stash pop`
     ++	later to re-apply your changes.
       
       `--conflict=<style>`::
       	The same as `--merge` option above, but changes the way the
     @@ Documentation/git-switch.adoc: $ git switch mytopic
       
      -You can give the `-m` flag to the command, which would try a three-way
      -merge:
     -+You can give the `-m` flag to the command, which would save the local
     -+changes in a stash entry and reset the working tree to allow switching:
     ++You can give the `-m` flag to the command, which would carry your local
     ++changes to the new branch:
       
       ------------
       $ git switch -m mytopic
      -Auto-merging frotz
     -+Created autostash: 7a9afa3
     -+Applied autostash.
     ++Switched to branch 'mytopic'
       ------------
       
      -After this three-way merge, the local modifications are _not_
     @@ builtin/checkout.c: static int merge_working_tree(const struct checkout_opts *op
       	}
       
       	if (!cache_tree_fully_valid(the_repository->index->cache_tree))
     -@@ builtin/checkout.c: static int merge_working_tree(const struct checkout_opts *opts,
     - 	if (write_locked_index(the_repository->index, &lock_file, COMMIT_LOCK))
     - 		die(_("unable to write new index file"));
     - 
     --	if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
     --		show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
     --
     - 	return 0;
     - }
     - 
      @@ builtin/checkout.c: static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
       	release_revisions(&revs);
       }
     @@ builtin/checkout.c: static int switch_branches(const struct checkout_opts *opts,
       	struct object_id rev;
       	int flag, writeout_error = 0;
       	int do_merge = 1;
     ++	int created_autostash = 0;
      +	struct strbuf old_commit_shortname = STRBUF_INIT;
      +	const char *stash_label_ancestor = NULL;
       
     @@ builtin/checkout.c: static int switch_branches(const struct checkout_opts *opts,
      +		if (repo_read_index(the_repository) < 0)
      +			die(_("index file corrupt"));
      +		if (checkout_would_clobber_changes(&old_branch_info,
     -+						   new_branch_info))
     ++						   new_branch_info)) {
      +			create_autostash_ref_silent(the_repository,
      +						   "CHECKOUT_AUTOSTASH");
     ++			created_autostash = 1;
     ++		}
      +	}
      +
       	if (do_merge) {
     @@ builtin/checkout.c: static int switch_branches(const struct checkout_opts *opts,
      +	if (repo_read_index(the_repository) < 0)
      +		die(_("index file corrupt"));
      +
     -+	if (!opts->discard_changes && !opts->quiet && new_branch_info->commit)
     ++	if (created_autostash && !opts->discard_changes && !opts->quiet &&
     ++	    new_branch_info->commit)
      +		show_local_changes(&new_branch_info->commit->object,
      +				   &opts->diff_options);
      +
     @@ builtin/stash.c: static int apply_stash(int argc, const char **argv, const char
       	return ret;
      
       ## sequencer.c ##
     -@@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list, int offset)
     - 
     - static void create_autostash_internal(struct repository *r,
     - 				      const char *path,
     --				      const char *refname)
     -+				      const char *refname,
     -+				      int silent)
     - {
     - 	struct strbuf buf = STRBUF_INIT;
     - 	struct lock_file lock_file = LOCK_INIT;
     -@@ sequencer.c: static void create_autostash_internal(struct repository *r,
     - 					&oid, null_oid(the_hash_algo), 0, UPDATE_REFS_DIE_ON_ERR);
     - 		}
     - 
     --		printf(_("Created autostash: %s\n"), buf.buf);
     -+		if (!silent)
     -+			fprintf(stderr, _("Created autostash: %s\n"), buf.buf);
     - 		if (reset_head(r, &ropts) < 0)
     - 			die(_("could not reset --hard"));
     - 		discard_index(r->index);
     -@@ sequencer.c: static void create_autostash_internal(struct repository *r,
     - 
     - void create_autostash(struct repository *r, const char *path)
     - {
     --	create_autostash_internal(r, path, NULL);
     -+	create_autostash_internal(r, path, NULL, 0);
     - }
     - 
     - void create_autostash_ref(struct repository *r, const char *refname)
     - {
     --	create_autostash_internal(r, NULL, refname);
     -+	create_autostash_internal(r, NULL, refname, 0);
     - }
     - 
     --static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
     -+void create_autostash_ref_silent(struct repository *r, const char *refname)
     -+{
     -+	create_autostash_internal(r, NULL, refname, 1);
     -+}
     -+
     -+static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply,
     -+				    const char *label1, const char *label2,
     -+				    const char *label_ancestor)
     - {
     - 	struct child_process child = CHILD_PROCESS_INIT;
     - 	int ret = 0;
     -@@ sequencer.c: static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
     - 		child.no_stderr = 1;
     - 		strvec_push(&child.args, "stash");
     - 		strvec_push(&child.args, "apply");
     -+		if (label1)
     -+			strvec_pushf(&child.args, "--ours-label=%s", label1);
     -+		if (label2)
     -+			strvec_pushf(&child.args, "--theirs-label=%s", label2);
     -+		if (label_ancestor)
     -+			strvec_pushf(&child.args, "--base-label=%s", label_ancestor);
     - 		strvec_push(&child.args, stash_oid);
     - 		ret = run_command(&child);
     - 	}
     -@@ sequencer.c: static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
     +@@ sequencer.c: static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply,
       		strvec_push(&store.args, stash_oid);
       		if (run_command(&store))
       			ret = error(_("cannot store %s"), stash_oid);
     @@ sequencer.c: static int apply_save_autostash_oid(const char *stash_oid, int atte
       	}
       
       	return ret;
     -@@ sequencer.c: static int apply_save_autostash(const char *path, int attempt_apply)
     - 	}
     - 	strbuf_trim(&stash_oid);
     - 
     --	ret = apply_save_autostash_oid(stash_oid.buf, attempt_apply);
     -+	ret = apply_save_autostash_oid(stash_oid.buf, attempt_apply,
     -+				      NULL, NULL, NULL);
     - 
     - 	unlink(path);
     - 	strbuf_release(&stash_oid);
     -@@ sequencer.c: int apply_autostash(const char *path)
     - 
     - int apply_autostash_oid(const char *stash_oid)
     - {
     --	return apply_save_autostash_oid(stash_oid, 1);
     -+	return apply_save_autostash_oid(stash_oid, 1, NULL, NULL, NULL);
     - }
     - 
     - static int apply_save_autostash_ref(struct repository *r, const char *refname,
     --				    int attempt_apply)
     -+				    int attempt_apply,
     -+				    const char *label1, const char *label2,
     -+				    const char *label_ancestor)
     - {
     - 	struct object_id stash_oid;
     - 	char stash_oid_hex[GIT_MAX_HEXSZ + 1];
     -@@ sequencer.c: static int apply_save_autostash_ref(struct repository *r, const char *refname,
     - 		return error(_("autostash reference is a symref"));
     - 
     - 	oid_to_hex_r(stash_oid_hex, &stash_oid);
     --	ret = apply_save_autostash_oid(stash_oid_hex, attempt_apply);
     -+	ret = apply_save_autostash_oid(stash_oid_hex, attempt_apply,
     -+				       label1, label2, label_ancestor);
     - 
     - 	refs_delete_ref(get_main_ref_store(r), "", refname,
     - 			&stash_oid, REF_NO_DEREF);
     -@@ sequencer.c: static int apply_save_autostash_ref(struct repository *r, const char *refname,
     - 
     - int save_autostash_ref(struct repository *r, const char *refname)
     - {
     --	return apply_save_autostash_ref(r, refname, 0);
     -+	return apply_save_autostash_ref(r, refname, 0, NULL, NULL, NULL);
     - }
     - 
     - int apply_autostash_ref(struct repository *r, const char *refname)
     - {
     --	return apply_save_autostash_ref(r, refname, 1);
     -+	return apply_save_autostash_ref(r, refname, 1, NULL, NULL, NULL);
     -+}
     -+
     -+int apply_autostash_ref_with_labels(struct repository *r, const char *refname,
     -+				    const char *label1, const char *label2,
     -+				    const char *label_ancestor)
     -+{
     -+	return apply_save_autostash_ref(r, refname, 1,
     -+					label1, label2, label_ancestor);
     - }
     - 
     - static int checkout_onto(struct repository *r, struct replay_opts *opts,
     -
     - ## sequencer.h ##
     -@@ sequencer.h: void commit_post_rewrite(struct repository *r,
     - 
     - void create_autostash(struct repository *r, const char *path);
     - void create_autostash_ref(struct repository *r, const char *refname);
     -+void create_autostash_ref_silent(struct repository *r, const char *refname);
     - int save_autostash(const char *path);
     - int save_autostash_ref(struct repository *r, const char *refname);
     - int apply_autostash(const char *path);
     - int apply_autostash_oid(const char *stash_oid);
     - int apply_autostash_ref(struct repository *r, const char *refname);
     -+int apply_autostash_ref_with_labels(struct repository *r, const char *refname,
     -+				    const char *label1, const char *label2,
     -+				    const char *label_ancestor);
     - 
     - #define SUMMARY_INITIAL_COMMIT   (1 << 0)
     - #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
      
       ## t/t3420-rebase-autostash.sh ##
      @@ t/t3420-rebase-autostash.sh: create_expected_failure_apply () {
     @@ t/t7201-co.sh: test_expect_success 'checkout --merge --conflict=diff3 <branch>'
       	git reset --hard &&
      
       ## t/t7600-merge.sh ##
     -@@ t/t7600-merge.sh: test_expect_success 'merge --squash --autostash conflict does not attempt to app
     - 	>unrelated &&
     - 	git add unrelated &&
     - 	test_must_fail git merge --squash c7 --autostash >out 2>err &&
     --	! grep "Applying autostash resulted in conflicts." err &&
     -+	! grep "resulted in conflicts" err &&
     - 	grep "When finished, apply stashed changes with \`git stash pop\`" out
     - '
     - 
      @@ t/t7600-merge.sh: test_expect_success 'merge with conflicted --autostash changes' '
       	git diff >expect &&
       	test_when_finished "test_might_fail git stash drop" &&
       	git merge --autostash c3 2>err &&
      -	test_grep "Applying autostash resulted in conflicts." err &&
     -+	test_grep "resulted in conflicts" err &&
     ++	test_grep "your local changes resulted in conflicts" err &&
       	git show HEAD:file >merge-result &&
       	test_cmp result.1-9 merge-result &&
       	git stash show -p >actual &&

-- 
gitgitgadget

  parent reply	other threads:[~2026-03-15 11:19 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       ` Harald Nordgren via GitGitGadget [this message]
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         ` [PATCH v6 0/4] checkout: 'autostash' for branch switching Harald Nordgren via GitGitGadget
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.v5.git.git.1773573553.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