Skip to contentSkip to conversation

merge: learn --autostash

RFC PATCH Superseded Denton Liu 5 participants 152 messages Oct 16, 2019 – Apr 10, 2020 Page 1 of 2

Subsystems (2)
lore
Share & tools
lore

Denton Liu

author
#0 lore raw

cover merge: learn --autostash

Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.

The actual logic isn't too bad. That change can be found in the last patch. The remainder is refactoring so that the change can be made easily. One thing I would like some attention on is if apply_autostash() is called in the right place. It's called somewhere above close_object_store() and also in the --abort case, just like in rebase, but I'm not quite sure if that's right.

Since this is an RFC, we're missing tests and documentation but I've tested it and it seems to work reasonably well.

[1]: https://github.com/gitgitgadget/git/issues/394

Denton Liu (7):
Makefile: alphabetically sort += lists
autostash: extract read_one() from rebase
autostash: extract apply_autostash() from rebase
autostash: extract reset_head() from rebase
autostash: extract perform_autostash() from rebase autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS
merge: teach --autostash option

 Makefile         |  75 +++++++--------
 autostash.c      | 239 ++++++++++++++++++++++++++++++++++++++++++++++
 autostash.h      |  24 +++++
 builtin/merge.c  |  13 +++
 builtin/pull.c   |   9 +-
 builtin/rebase.c | 240 +----------------------------------------------
 t/t5520-pull.sh  |   8 --
 7 files changed, 323 insertions(+), 285 deletions(-)
 create mode 100644 autostash.c
 create mode 100644 autostash.h
Signature · 1 line
2.23.0.897.g0a19638b1e
author
#1 lore raw

RFC PATCH 1/7 Makefile: alphabetically sort += lists

There are many += lists in the Makefile and, over time, they have gotten slightly out of order, alphabetically. Alphabetically sort all += lists to bring them back in order.

---
 Makefile | 75 ++++++++++++++++++++++++++++----------------------------
 1 file changed, 38 insertions(+), 37 deletions(-)
Makefile +38 -37
index de60c8e7aa..268a273df5 100644
@@ -604,12 +604,12 @@ unexport CDPATH
 SCRIPT_SH += git-bisect.sh
 SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
+SCRIPT_SH += git-legacy-stash.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-quiltimport.sh
-SCRIPT_SH += git-legacy-stash.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-web--browse.sh
@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
 SCRIPT_LIB += git-rebase--preserve-merges
-SCRIPT_LIB += git-sh-setup
 SCRIPT_LIB += git-sh-i18n
+SCRIPT_LIB += git-sh-setup
 
 SCRIPT_PERL += git-add--interactive.perl
 SCRIPT_PERL += git-archimport.perl
@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o
 PROGRAM_OBJS += fast-import.o
 PROGRAM_OBJS += http-backend.o
 PROGRAM_OBJS += imap-send.o
+PROGRAM_OBJS += remote-testsvn.o
 PROGRAM_OBJS += sh-i18n--envsubst.o
 PROGRAM_OBJS += shell.o
-PROGRAM_OBJS += remote-testsvn.o
 
 # Binary suffix, set to .exe for Windows builds
 X =
@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
 TEST_BUILTINS_OBJS += test-example-decorate.o
 TEST_BUILTINS_OBJS += test-genrandom.o
 TEST_BUILTINS_OBJS += test-genzeros.o
+TEST_BUILTINS_OBJS += test-hash-speed.o
 TEST_BUILTINS_OBJS += test-hash.o
 TEST_BUILTINS_OBJS += test-hashmap.o
-TEST_BUILTINS_OBJS += test-hash-speed.o
 TEST_BUILTINS_OBJS += test-index-version.o
 TEST_BUILTINS_OBJS += test-json-writer.o
 TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@ -735,8 +735,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o
 TEST_BUILTINS_OBJS += test-run-command.o
 TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
 TEST_BUILTINS_OBJS += test-serve-v2.o
-TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha1-array.o
+TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha256.o
 TEST_BUILTINS_OBJS += test-sigchain.o
 TEST_BUILTINS_OBJS += test-strcmp-offset.o
@@ -746,10 +746,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
 TEST_BUILTINS_OBJS += test-subprocess.o
 TEST_BUILTINS_OBJS += test-trace2.o
 TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
-TEST_BUILTINS_OBJS += test-xml-encode.o
 TEST_BUILTINS_OBJS += test-wildmatch.o
 TEST_BUILTINS_OBJS += test-windows-named-pipe.o
 TEST_BUILTINS_OBJS += test-write-cache.o
+TEST_BUILTINS_OBJS += test-xml-encode.o
 
 # Do not add more tests here unless they have extra dependencies. Add
 # them in TEST_BUILTINS_OBJS above.
@@ -786,10 +786,10 @@ OTHER_PROGRAMS = git$X
 
 # what test wrappers are needed and 'install' will install, in bindir
 BINDIR_PROGRAMS_NEED_X += git
-BINDIR_PROGRAMS_NEED_X += git-upload-pack
 BINDIR_PROGRAMS_NEED_X += git-receive-pack
-BINDIR_PROGRAMS_NEED_X += git-upload-archive
 BINDIR_PROGRAMS_NEED_X += git-shell
+BINDIR_PROGRAMS_NEED_X += git-upload-archive
+BINDIR_PROGRAMS_NEED_X += git-upload-pack
 
 BINDIR_PROGRAMS_NO_X += git-cvsserver
 
@@ -827,11 +827,12 @@ LIB_OBJS += advice.o
 LIB_OBJS += alias.o
 LIB_OBJS += alloc.o
 LIB_OBJS += apply.o
-LIB_OBJS += archive.o
 LIB_OBJS += archive-tar.o
 LIB_OBJS += archive-zip.o
+LIB_OBJS += archive.o
 LIB_OBJS += argv-array.o
 LIB_OBJS += attr.o
+LIB_OBJS += autostash.o
 LIB_OBJS += base85.o
 LIB_OBJS += bisect.o
 LIB_OBJS += blame.o
@@ -845,9 +846,9 @@ LIB_OBJS += checkout.o
 LIB_OBJS += color.o
 LIB_OBJS += column.o
 LIB_OBJS += combine-diff.o
-LIB_OBJS += commit.o
 LIB_OBJS += commit-graph.o
 LIB_OBJS += commit-reach.o
+LIB_OBJS += commit.o
 LIB_OBJS += compat/obstack.o
 LIB_OBJS += compat/terminal.o
 LIB_OBJS += config.o
@@ -861,17 +862,17 @@ LIB_OBJS += ctype.o
 LIB_OBJS += date.o
 LIB_OBJS += decorate.o
 LIB_OBJS += delta-islands.o
+LIB_OBJS += diff-delta.o
+LIB_OBJS += diff-lib.o
+LIB_OBJS += diff-no-index.o
+LIB_OBJS += diff.o
 LIB_OBJS += diffcore-break.o
 LIB_OBJS += diffcore-delta.o
 LIB_OBJS += diffcore-order.o
 LIB_OBJS += diffcore-pickaxe.o
 LIB_OBJS += diffcore-rename.o
-LIB_OBJS += diff-delta.o
-LIB_OBJS += diff-lib.o
-LIB_OBJS += diff-no-index.o
-LIB_OBJS += diff.o
-LIB_OBJS += dir.o
 LIB_OBJS += dir-iterator.o
+LIB_OBJS += dir.o
 LIB_OBJS += editor.o
 LIB_OBJS += entry.o
 LIB_OBJS += environment.o
@@ -889,7 +890,6 @@ LIB_OBJS += gpg-interface.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hashmap.o
-LIB_OBJS += linear-assignment.o
 LIB_OBJS += help.o
 LIB_OBJS += hex.o
 LIB_OBJS += ident.o
@@ -899,9 +899,10 @@ LIB_OBJS += kwset.o
 LIB_OBJS += levenshtein.o
 LIB_OBJS += line-log.o
 LIB_OBJS += line-range.o
-LIB_OBJS += list-objects.o
-LIB_OBJS += list-objects-filter.o
+LIB_OBJS += linear-assignment.o
 LIB_OBJS += list-objects-filter-options.o
+LIB_OBJS += list-objects-filter.o
+LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
@@ -910,31 +911,31 @@ LIB_OBJS += mailinfo.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += mem-pool.o
-LIB_OBJS += merge.o
 LIB_OBJS += merge-blobs.o
 LIB_OBJS += merge-recursive.o
+LIB_OBJS += merge.o
 LIB_OBJS += mergesort.o
 LIB_OBJS += midx.o
 LIB_OBJS += name-hash.o
 LIB_OBJS += negotiator/default.o
 LIB_OBJS += negotiator/skipping.o
-LIB_OBJS += notes.o
 LIB_OBJS += notes-cache.o
 LIB_OBJS += notes-merge.o
 LIB_OBJS += notes-utils.o
+LIB_OBJS += notes.o
 LIB_OBJS += object.o
 LIB_OBJS += oidmap.o
 LIB_OBJS += oidset.o
-LIB_OBJS += packfile.o
-LIB_OBJS += pack-bitmap.o
 LIB_OBJS += pack-bitmap-write.o
+LIB_OBJS += pack-bitmap.o
 LIB_OBJS += pack-check.o
 LIB_OBJS += pack-objects.o
 LIB_OBJS += pack-revindex.o
 LIB_OBJS += pack-write.o
+LIB_OBJS += packfile.o
 LIB_OBJS += pager.o
-LIB_OBJS += parse-options.o
 LIB_OBJS += parse-options-cb.o
+LIB_OBJS += parse-options.o
 LIB_OBJS += patch-delta.o
 LIB_OBJS += patch-ids.o
 LIB_OBJS += path.o
@@ -952,6 +953,7 @@ LIB_OBJS += range-diff.o
 LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
 LIB_OBJS += rebase-interactive.o
+LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/files-backend.o
@@ -959,7 +961,6 @@ LIB_OBJS += refs/iterator.o
 LIB_OBJS += refs/packed-backend.o
 LIB_OBJS += refs/ref-cache.o
 LIB_OBJS += refspec.o
-LIB_OBJS += ref-filter.o
 LIB_OBJS += remote.o
 LIB_OBJS += replace-object.o
 LIB_OBJS += repo-settings.o
@@ -974,8 +975,8 @@ LIB_OBJS += serve.o
 LIB_OBJS += server-info.o
 LIB_OBJS += setup.o
 LIB_OBJS += sha1-array.o
-LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1-file.o
+LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1-name.o
 LIB_OBJS += shallow.o
 LIB_OBJS += sideband.o
@@ -985,9 +986,9 @@ LIB_OBJS += stable-qsort.o
 LIB_OBJS += strbuf.o
 LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
-LIB_OBJS += submodule.o
-LIB_OBJS += submodule-config.o
 LIB_OBJS += sub-process.o
+LIB_OBJS += submodule-config.o
+LIB_OBJS += submodule.o
 LIB_OBJS += symlinks.o
 LIB_OBJS += tag.o
 LIB_OBJS += tempfile.o
@@ -1006,11 +1007,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o
 LIB_OBJS += trace2/tr2_tgt_perf.o
 LIB_OBJS += trace2/tr2_tls.o
 LIB_OBJS += trailer.o
-LIB_OBJS += transport.o
 LIB_OBJS += transport-helper.o
+LIB_OBJS += transport.o
 LIB_OBJS += tree-diff.o
-LIB_OBJS += tree.o
 LIB_OBJS += tree-walk.o
+LIB_OBJS += tree.o
 LIB_OBJS += unpack-trees.o
 LIB_OBJS += upload-pack.o
 LIB_OBJS += url.o
@@ -1050,9 +1051,9 @@ BUILTIN_OBJS += builtin/checkout.o
 BUILTIN_OBJS += builtin/clean.o
 BUILTIN_OBJS += builtin/clone.o
 BUILTIN_OBJS += builtin/column.o
+BUILTIN_OBJS += builtin/commit-graph.o
 BUILTIN_OBJS += builtin/commit-tree.o
 BUILTIN_OBJS += builtin/commit.o
-BUILTIN_OBJS += builtin/commit-graph.o
 BUILTIN_OBJS += builtin/config.o
 BUILTIN_OBJS += builtin/count-objects.o
 BUILTIN_OBJS += builtin/credential.o
@@ -1083,13 +1084,13 @@ BUILTIN_OBJS += builtin/ls-remote.o
 BUILTIN_OBJS += builtin/ls-tree.o
 BUILTIN_OBJS += builtin/mailinfo.o
 BUILTIN_OBJS += builtin/mailsplit.o
-BUILTIN_OBJS += builtin/merge.o
 BUILTIN_OBJS += builtin/merge-base.o
 BUILTIN_OBJS += builtin/merge-file.o
 BUILTIN_OBJS += builtin/merge-index.o
 BUILTIN_OBJS += builtin/merge-ours.o
 BUILTIN_OBJS += builtin/merge-recursive.o
 BUILTIN_OBJS += builtin/merge-tree.o
+BUILTIN_OBJS += builtin/merge.o
 BUILTIN_OBJS += builtin/mktag.o
 BUILTIN_OBJS += builtin/mktree.o
 BUILTIN_OBJS += builtin/multi-pack-index.o
@@ -1109,9 +1110,9 @@ BUILTIN_OBJS += builtin/read-tree.o
 BUILTIN_OBJS += builtin/rebase.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
-BUILTIN_OBJS += builtin/remote.o
 BUILTIN_OBJS += builtin/remote-ext.o
 BUILTIN_OBJS += builtin/remote-fd.o
+BUILTIN_OBJS += builtin/remote.o
 BUILTIN_OBJS += builtin/repack.o
 BUILTIN_OBJS += builtin/replace.o
 BUILTIN_OBJS += builtin/rerere.o
@@ -2325,16 +2326,16 @@ reconfigure config.mak.autogen: config.status
 endif
 
 XDIFF_OBJS += xdiff/xdiffi.o
-XDIFF_OBJS += xdiff/xprepare.o
-XDIFF_OBJS += xdiff/xutils.o
 XDIFF_OBJS += xdiff/xemit.o
+XDIFF_OBJS += xdiff/xhistogram.o
 XDIFF_OBJS += xdiff/xmerge.o
 XDIFF_OBJS += xdiff/xpatience.o
-XDIFF_OBJS += xdiff/xhistogram.o
+XDIFF_OBJS += xdiff/xprepare.o
+XDIFF_OBJS += xdiff/xutils.o
 
+VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/line_buffer.o
 VCSSVN_OBJS += vcs-svn/sliding_window.o
-VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/svndiff.o
 VCSSVN_OBJS += vcs-svn/svndump.o
 
@@ -3143,9 +3144,9 @@ endif
 #
 ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS)
 ALL_COMMANDS += git
+ALL_COMMANDS += git-gui git-citool
 ALL_COMMANDS += gitk
 ALL_COMMANDS += gitweb
-ALL_COMMANDS += git-gui git-citool
 
 .PHONY: check-docs
 check-docs::
Signature · 1 line
2.23.0.897.g0a19638b1e
author
#9 lore raw

RFC PATCH 2/7 autostash: extract read_one() from rebase

In reply to Denton Liu

Begin the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.

---
 autostash.c      | 12 ++++++++++++
 autostash.h      |  9 +++++++++
 builtin/rebase.c | 10 +---------
 3 files changed, 22 insertions(+), 9 deletions(-)
 create mode 100644 autostash.c
 create mode 100644 autostash.h
autostash.c new file +12 -0
new file mode 100644
index 0000000000..a6898e0fda
@@ -0,0 +1,12 @@
+#include "git-compat-util.h"
+#include "autostash.h"
+#include "gettext.h"
+#include "strbuf.h"
+
+int read_one(const char *path, struct strbuf *buf)
+{
+	if (strbuf_read_file(buf, path, 0) < 0)
+		return error_errno(_("could not read '%s'"), path);
+	strbuf_trim_trailing_newline(buf);
+	return 0;
+}
autostash.h new file +9 -0
new file mode 100644
index 0000000000..4a8f504f12
@@ -0,0 +1,9 @@
+#ifndef AUTOSTASH_H
+#define AUTOSTASH_H
+
+#include "strbuf.h"
+
+/* Read one file, then strip line endings */
+int read_one(const char *path, struct strbuf *buf);
+
+#endif
builtin/rebase.c +1 -9
index 4a20582e72..9fd7de6b2f 100644
@@ -27,6 +27,7 @@
 #include "branch.h"
 #include "sequencer.h"
 #include "rebase-interactive.h"
+#include "autostash.h"
 
 static char const * const builtin_rebase_usage[] = {
 	N_("git rebase [-i] [options] [--exec <cmd>] "
@@ -561,15 +562,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o
 	return path.buf;
 }
 
-/* Read one file, then strip line endings */
-static int read_one(const char *path, struct strbuf *buf)
-{
-	if (strbuf_read_file(buf, path, 0) < 0)
-		return error_errno(_("could not read '%s'"), path);
-	strbuf_trim_trailing_newline(buf);
-	return 0;
-}
-
 /* Initialize the rebase options from the state directory. */
 static int read_basic_state(struct rebase_options *opts)
 {
Signature · 1 line
2.23.0.897.g0a19638b1e
author
#12 lore raw

RFC PATCH 3/7 autostash: extract apply_autostash() from rebase

In reply to Denton Liu

Continue the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.

---
 autostash.c      | 46 +++++++++++++++++++++++++++++++++++++++++++++
 autostash.h      |  2 ++
 builtin/rebase.c | 49 ++----------------------------------------------
 3 files changed, 50 insertions(+), 47 deletions(-)
autostash.c +46 -0
index a6898e0fda..62ec7a7c80 100644
@@ -1,6 +1,8 @@
 #include "git-compat-util.h"
 #include "autostash.h"
+#include "dir.h"
 #include "gettext.h"
+#include "run-command.h"
 #include "strbuf.h"
 
 int read_one(const char *path, struct strbuf *buf)
@@ -10,3 +12,47 @@ int read_one(const char *path, struct strbuf *buf)
 	strbuf_trim_trailing_newline(buf);
 	return 0;
 }
+
+int apply_autostash(const char *path)
+{
+	struct strbuf autostash = STRBUF_INIT;
+	struct child_process stash_apply = CHILD_PROCESS_INIT;
+
+	if (!file_exists(path))
+		return 0;
+
+	if (read_one(path, &autostash))
+		return error(_("Could not read '%s'"), path);
+	/* Ensure that the hash is not mistaken for a number */
+	strbuf_addstr(&autostash, "^0");
+	argv_array_pushl(&stash_apply.args,
+			 "stash", "apply", autostash.buf, NULL);
+	stash_apply.git_cmd = 1;
+	stash_apply.no_stderr = stash_apply.no_stdout =
+		stash_apply.no_stdin = 1;
+	if (!run_command(&stash_apply))
+		printf(_("Applied autostash.\n"));
+	else {
+		struct argv_array args = ARGV_ARRAY_INIT;
+		int res = 0;
+
+		argv_array_pushl(&args,
+				 "stash", "store", "-m", "autostash", "-q",
+				 autostash.buf, NULL);
+		if (run_command_v_opt(args.argv, RUN_GIT_CMD))
+			res = error(_("Cannot store %s"), autostash.buf);
+		argv_array_clear(&args);
+		strbuf_release(&autostash);
+		if (res)
+			return res;
+
+		fprintf(stderr,
+			_("Applying autostash resulted in conflicts.\n"
+			  "Your changes are safe in the stash.\n"
+			  "You can run \"git stash pop\" or \"git stash drop\" "
+			  "at any time.\n"));
+	}
+
+	strbuf_release(&autostash);
+	return 0;
+}
autostash.h +2 -0
index 4a8f504f12..5f4e4bd22c 100644
@@ -6,4 +6,6 @@
 /* Read one file, then strip line endings */
 int read_one(const char *path, struct strbuf *buf);
 
+int apply_autostash(const char *path);
+
 #endif
builtin/rebase.c +2 -47

67 lines not on this page · raw message

Signature · 1 line
2.23.0.897.g0a19638b1e
author
#13 lore raw

RFC PATCH 4/7 autostash: extract reset_head() from rebase

In reply to Denton Liu

Begin the process of lib-ifying the autostash code. In a future commit,

This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.
this will be used to implement --autostash in other builtins.

---
 autostash.c      | 137 +++++++++++++++++++++++++++++++++++++++++++++++
 autostash.h      |  12 +++++
 builtin/rebase.c | 137 -----------------------------------------------
 3 files changed, 149 insertions(+), 137 deletions(-)
autostash.c +137 -0
index 62ec7a7c80..eb58e0c8a4 100644
@@ -1,9 +1,17 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+
 #include "git-compat-util.h"
 #include "autostash.h"
+#include "cache-tree.h"
 #include "dir.h"
 #include "gettext.h"
+#include "lockfile.h"
+#include "refs.h"
 #include "run-command.h"
 #include "strbuf.h"
+#include "tree-walk.h"
+#include "tree.h"
+#include "unpack-trees.h"
 
 int read_one(const char *path, struct strbuf *buf)
 {
@@ -13,6 +21,135 @@ int read_one(const char *path, struct strbuf *buf)
 	return 0;
 }
 
+int reset_head(struct object_id *oid, const char *action,
+	       const char *switch_to_branch, unsigned flags,
+	       const char *reflog_orig_head, const char *reflog_head)
+{
+	unsigned detach_head = flags & RESET_HEAD_DETACH;
+	unsigned reset_hard = flags & RESET_HEAD_HARD;
+	unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
+	unsigned refs_only = flags & RESET_HEAD_REFS_ONLY;
+	unsigned update_orig_head = flags & RESET_ORIG_HEAD;
+	struct object_id head_oid;
+	struct tree_desc desc[2] = { { NULL }, { NULL } };
+	struct lock_file lock = LOCK_INIT;
+	struct unpack_trees_options unpack_tree_opts;
+	struct tree *tree;
+	const char *reflog_action;
+	struct strbuf msg = STRBUF_INIT;
+	size_t prefix_len;
+	struct object_id *orig = NULL, oid_orig,
+		*old_orig = NULL, oid_old_orig;
+	int ret = 0, nr = 0;
+
+	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
+		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
+
+	if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
+		ret = -1;
+		goto leave_reset_head;
+	}
+
+	if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) {
+		ret = error(_("could not determine HEAD revision"));
+		goto leave_reset_head;
+	}
+
+	if (!oid)
+		oid = &head_oid;
+
+	if (refs_only)
+		goto reset_head_refs;
+
+	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
+	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
+	unpack_tree_opts.head_idx = 1;
+	unpack_tree_opts.src_index = the_repository->index;
+	unpack_tree_opts.dst_index = the_repository->index;
+	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
+	unpack_tree_opts.update = 1;
+	unpack_tree_opts.merge = 1;
+	if (!detach_head)
+		unpack_tree_opts.reset = 1;
+
+	if (repo_read_index_unmerged(the_repository) < 0) {
+		ret = error(_("could not read index"));
+		goto leave_reset_head;
+	}
+
+	if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {
+		ret = error(_("failed to find tree of %s"),
+			    oid_to_hex(&head_oid));
+		goto leave_reset_head;
+	}
+
+	if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {
+		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
+		goto leave_reset_head;
+	}
+
+	if (unpack_trees(nr, desc, &unpack_tree_opts)) {
+		ret = -1;
+		goto leave_reset_head;
+	}
+
+	tree = parse_tree_indirect(oid);
+	prime_cache_tree(the_repository, the_repository->index, tree);
+
+	if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {
+		ret = error(_("could not write index"));
+		goto leave_reset_head;
+	}
+
+reset_head_refs:
+	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
+	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
+	prefix_len = msg.len;
+
+	if (update_orig_head) {
+		if (!get_oid("ORIG_HEAD", &oid_old_orig))
+			old_orig = &oid_old_orig;
+		if (!get_oid("HEAD", &oid_orig)) {
+			orig = &oid_orig;
+			if (!reflog_orig_head) {
+				strbuf_addstr(&msg, "updating ORIG_HEAD");
+				reflog_orig_head = msg.buf;
+			}
+			update_ref(reflog_orig_head, "ORIG_HEAD", orig,
+				   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
+		} else if (old_orig)
+			delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+	}
+
+	if (!reflog_head) {
+		strbuf_setlen(&msg, prefix_len);
+		strbuf_addstr(&msg, "updating HEAD");
+		reflog_head = msg.buf;
+	}
+	if (!switch_to_branch)
+		ret = update_ref(reflog_head, "HEAD", oid, orig,
+				 detach_head ? REF_NO_DEREF : 0,
+				 UPDATE_REFS_MSG_ON_ERR);
+	else {
+		ret = update_ref(reflog_head, switch_to_branch, oid,
+				 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+		if (!ret)
+			ret = create_symref("HEAD", switch_to_branch,
+					    reflog_head);
+	}
+	if (run_hook)
+		run_hook_le(NULL, "post-checkout",
+			    oid_to_hex(orig ? orig : &null_oid),
+			    oid_to_hex(oid), "1", NULL);
+
+leave_reset_head:
+	strbuf_release(&msg);
+	rollback_lock_file(&lock);
+	while (nr)
+		free((void *)desc[--nr].buffer);
+	return ret;
+}
+
 int apply_autostash(const char *path)
 {
 	struct strbuf autostash = STRBUF_INIT;
autostash.h +12 -0
index 5f4e4bd22c..1406638166 100644
@@ -6,6 +6,18 @@
 /* Read one file, then strip line endings */
 int read_one(const char *path, struct strbuf *buf);
 
+#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
+
+#define RESET_HEAD_DETACH (1<<0)
+#define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
+#define RESET_HEAD_REFS_ONLY (1<<3)
+#define RESET_ORIG_HEAD (1<<4)
+
+int reset_head(struct object_id *oid, const char *action,
+	       const char *switch_to_branch, unsigned flags,
+	       const char *reflog_orig_head, const char *reflog_head);
+
 int apply_autostash(const char *path);
 
 #endif
builtin/rebase.c +0 -137

143 lines not on this page · raw message

Signature · 1 line
2.23.0.897.g0a19638b1e
author
#16 lore raw

RFC PATCH 5/7 autostash: extract perform_autostash() from rebase

In reply to Denton Liu

Continue the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.

---
 autostash.c      | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 autostash.h      |  1 +
 builtin/rebase.c | 44 +-------------------------------------------
 3 files changed, 48 insertions(+), 43 deletions(-)
autostash.c +46 -0
index eb58e0c8a4..722cf78b12 100644
@@ -12,6 +12,7 @@
 #include "tree-walk.h"
 #include "tree.h"
 #include "unpack-trees.h"
+#include "wt-status.h"
 
 int read_one(const char *path, struct strbuf *buf)
 {
@@ -150,6 +151,51 @@ int reset_head(struct object_id *oid, const char *action,
 	return ret;
 }
 
+void perform_autostash(const char *path)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct lock_file lock_file = LOCK_INIT;
+	int fd;
+
+	fd = hold_locked_index(&lock_file, 0);
+	refresh_cache(REFRESH_QUIET);
+	if (0 <= fd)
+		repo_update_index_if_able(the_repository, &lock_file);
+	rollback_lock_file(&lock_file);
+
+	if (has_unstaged_changes(the_repository, 1) ||
+	    has_uncommitted_changes(the_repository, 1)) {
+		struct child_process stash = CHILD_PROCESS_INIT;
+		struct object_id oid;
+
+		argv_array_pushl(&stash.args,
+				 "stash", "create", "autostash", NULL);
+		stash.git_cmd = 1;
+		stash.no_stdin = 1;
+		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
+			die(_("Cannot autostash"));
+		strbuf_trim_trailing_newline(&buf);
+		if (get_oid(buf.buf, &oid))
+			die(_("Unexpected stash response: '%s'"),
+			    buf.buf);
+		strbuf_reset(&buf);
+		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
+
+		if (safe_create_leading_directories_const(path))
+			die(_("Could not create directory for '%s'"),
+			    path);
+		write_file(path, "%s", oid_to_hex(&oid));
+		printf(_("Created autostash: %s\n"), buf.buf);
+		if (reset_head(NULL, "reset --hard",
+			       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
+			die(_("could not reset --hard"));
+
+		if (discard_index(the_repository->index) < 0 ||
+			repo_read_index(the_repository) < 0)
+			die(_("could not read index"));
+	}
+}
+
 int apply_autostash(const char *path)
 {
 	struct strbuf autostash = STRBUF_INIT;
autostash.h +1 -0
index 1406638166..e08ccb9881 100644
@@ -18,6 +18,7 @@ int reset_head(struct object_id *oid, const char *action,
 	       const char *switch_to_branch, unsigned flags,
 	       const char *reflog_orig_head, const char *reflog_head);
 
+void perform_autostash(const char *path);
 int apply_autostash(const char *path);
 
 #endif
builtin/rebase.c +1 -43

50 lines not on this page · raw message

Signature · 1 line
2.23.0.897.g0a19638b1e
author
#18 lore raw

RFC PATCH 6/7 autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS

In reply to Denton Liu

Since autostash.c was recently introduced, we should avoid USE_THE_INDEX_COMPATIBILITY_MACROS since we are trying to move away from this in the rest of the codebase. Rewrite the autostash code to not need it and remove its definition.

---
 autostash.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)
autostash.c +3 -5
index 722cf78b12..0a1f00d2e5 100644
@@ -1,5 +1,3 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-
 #include "git-compat-util.h"
 #include "autostash.h"
 #include "cache-tree.h"
@@ -46,7 +44,7 @@ int reset_head(struct object_id *oid, const char *action,
 	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
 		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
 
-	if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
+	if (!refs_only && repo_hold_locked_index(the_repository, &lock, LOCK_REPORT_ON_ERROR) < 0) {
 		ret = -1;
 		goto leave_reset_head;
 	}
@@ -157,8 +155,8 @@ void perform_autostash(const char *path)
 	struct lock_file lock_file = LOCK_INIT;
 	int fd;
 
-	fd = hold_locked_index(&lock_file, 0);
-	refresh_cache(REFRESH_QUIET);
+	fd = repo_hold_locked_index(the_repository, &lock_file, 0);
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
 	if (0 <= fd)
 		repo_update_index_if_able(the_repository, &lock_file);
 	rollback_lock_file(&lock_file);
Signature · 1 line
2.23.0.897.g0a19638b1e
author
#20 lore raw

RFC PATCH 7/7 merge: teach --autostash option

In reply to Denton Liu

In rebase, one can pass the --autostash option to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.

Implement the --autostash option and corresponding merge.autoStash option in merge which stashes before merging and then pops after.

---
 builtin/merge.c | 13 +++++++++++++
 builtin/pull.c  |  9 +++++----
 t/t5520-pull.sh |  8 --------
 3 files changed, 18 insertions(+), 12 deletions(-)
builtin/merge.c +13 -0
index 062e911441..d1a5eaad0d 100644
@@ -40,6 +40,7 @@
 #include "branch.h"
 #include "commit-reach.h"
 #include "wt-status.h"
+#include "autostash.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {
 	NULL
 };
 
+static GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
+
 static int show_diffstat = 1, shortlog_len = -1, squash;
 static int option_commit = -1;
 static int option_edit = -1;
@@ -81,6 +84,7 @@ static int show_progress = -1;
 static int default_to_upstream = 1;
 static int signoff;
 static const char *sign_commit;
+static int autostash;
 static int no_verify;
 
 static struct strategy all_strategy[] = {
@@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {
 	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
 	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
 	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+	OPT_BOOL(0, "autostash", &autostash,
+	      N_("automatically stash/stash pop before and after")),
 	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
 	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
 	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,
 		strbuf_addf(&reflog_message, "%s: %s",
 			getenv("GIT_REFLOG_ACTION"), msg);
 	}
+	apply_autostash(merge_autostash());
 	if (squash) {
 		squash_message(head_commit, remoteheads);
 	} else {
@@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 	} else if (!strcmp(k, "commit.gpgsign")) {
 		sign_commit = git_config_bool(k, v) ? "" : NULL;
 		return 0;
+	} else if (!strcmp(k, "merge.autostash")) {
+		autostash = git_config_bool(k, v);
+		return 0;
 	}
 
 	status = fmt_merge_msg_config(k, v, cb);
@@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
 		for (j = common; j; j = j->next)
 			commit_list_insert(j->item, &reversed);
 
+		if (autostash)
+			perform_autostash(merge_autostash());
 		hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
 		clean = merge_recursive(&o, head,
 				remoteheads->item, reversed, &result);
@@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
 		/* Invoke 'git reset --merge' */
 		ret = cmd_reset(nargc, nargv, prefix);
+		apply_autostash(merge_autostash());
 		goto done;
 	}
 
builtin/pull.c +5 -4
index d25ff13a60..ee186781ae 100644
@@ -183,7 +183,7 @@ static struct option pull_options[] = {
 		N_("verify that the named commit has a valid GPG signature"),
 		PARSE_OPT_NOARG),
 	OPT_BOOL(0, "autostash", &opt_autostash,
-		N_("automatically stash/stash pop before and after rebase")),
+		N_("automatically stash/stash pop before and after")),
 	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
 		N_("merge strategy to use"),
 		0),
@@ -671,6 +671,10 @@ static int run_merge(void)
 	argv_array_pushv(&args, opt_strategy_opts.argv);
 	if (opt_gpg_sign)
 		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");
 	if (opt_allow_unrelated_histories > 0)
 		argv_array_push(&args, "--allow-unrelated-histories");
 
@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 	if (get_oid("HEAD", &orig_head))
 		oidclr(&orig_head);
 
-	if (!opt_rebase && opt_autostash != -1)
-		die(_("--[no-]autostash option is only valid with --rebase."));
-
 	autostash = config_autostash;
 	if (opt_rebase) {
 		if (opt_autostash != -1)
t/t5520-pull.sh +0 -8
index cf4cc32fd0..75f162495a 100755
@@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
 	test_pull_autostash_fail --rebase --no-autostash
 '
 
-for i in --autostash --no-autostash
-do
-	test_expect_success "pull $i (without --rebase) is illegal" '
-		test_must_fail git pull $i . copy 2>err &&
-		test_i18ngrep "only valid with --rebase" err
-	'
-done
-
 test_expect_success 'pull.rebase' '
 	git reset --hard before-rebase &&
 	test_config pull.rebase true &&
Signature · 1 line
2.23.0.897.g0a19638b1e
#2 lore raw

Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists

In reply to Denton Liu
Quoted 4 lines · Denton Liu

There are many += lists in the Makefile and, over time, they have gotten slightly out of order, alphabetically. Alphabetically sort all += lists to bring them back in order.
...

Hmm. I like the general thrust, but ...

Makefile LIB_OBJS += checkout.o · line 851

Quoted 5 lines

LIB_OBJS += combine-diff.o

Makefile +1 -1
-LIB_OBJS += commit.o
 LIB_OBJS += commit-graph.o
 LIB_OBJS += commit-reach.o
+LIB_OBJS += commit.o

... I do not particularly see this change (there may be similar ones) desirable. I'd find it it be much more natural to sort "commit-anything" after "commit", and that is true with or without the common extension ".o" added to these entries.

In short, flipping these entries because '.' sorts later than '-' is making the result look "less sorted", at least to me.

Thanks.

#10 lore raw

Re: [RFC PATCH 2/7] autostash: extract read_one() from rebase

In reply to Denton Liu

Hi Denton

Quoted 23 lines · Denton Liu

Begin the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.

---
  autostash.c      | 12 ++++++++++++
  autostash.h      |  9 +++++++++
  builtin/rebase.c | 10 +---------
  3 files changed, 22 insertions(+), 9 deletions(-)

create mode 100644 autostash.c
create mode 100644 autostash.h

autostash.c new file +2 -0
new file mode 100644
index 0000000000..a6898e0fda
@@ -0,0 +1,12 @@
+#include "git-compat-util.h"
+#include "autostash.h"

autostash.c line 12

Quoted 10 lines
autostash.c new file +10 -0
+#include "gettext.h"
+#include "strbuf.h"
+
+int read_one(const char *path, struct strbuf *buf)
+{
+	if (strbuf_read_file(buf, path, 0) < 0)
+		return error_errno(_("could not read '%s'"), path);
+	strbuf_trim_trailing_newline(buf);
+	return 0;
+}

This looks like it's doing a similar job to read_oneliner() in sequencer.c, is it possible to make that public and use it instead? (There may be a difference if the file is missing but that function already takes a flag so it could probably be modified easily enough.)

Best Wishes

Phillip

Quoted 44 lines
autostash.h new file +9 -0
new file mode 100644
index 0000000000..4a8f504f12
@@ -0,0 +1,9 @@
+#ifndef AUTOSTASH_H
+#define AUTOSTASH_H
+
+#include "strbuf.h"
+
+/* Read one file, then strip line endings */
+int read_one(const char *path, struct strbuf *buf);
+
+#endif
builtin/rebase.c +1 -9
index 4a20582e72..9fd7de6b2f 100644
@@ -27,6 +27,7 @@
  #include "branch.h"
  #include "sequencer.h"
  #include "rebase-interactive.h"
+#include "autostash.h"
 
  static char const * const builtin_rebase_usage[] = {
  	N_("git rebase [-i] [options] [--exec <cmd>] "
@@ -561,15 +562,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o
  	return path.buf;
  }
 
-/* Read one file, then strip line endings */
-static int read_one(const char *path, struct strbuf *buf)
-{
-	if (strbuf_read_file(buf, path, 0) < 0)
-		return error_errno(_("could not read '%s'"), path);
-	strbuf_trim_trailing_newline(buf);
-	return 0;
-}
-
  /* Initialize the rebase options from the state directory. */
  static int read_basic_state(struct rebase_options *opts)
  {
#14 lore raw

Re: [RFC PATCH 4/7] autostash: extract reset_head() from rebase

In reply to Denton Liu

Hi Denton

It's great to see this being libified, I've had it in mind to do this so we can avoid forking 'git checkout' in sequencer.c

Quoted 18 lines · Denton Liu

Begin the process of lib-ifying the autostash code. In a future commit,

This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.
this will be used to implement --autostash in other builtins.

---
  autostash.c      | 137 +++++++++++++++++++++++++++++++++++++++++++++++
  autostash.h      |  12 +++++
  builtin/rebase.c | 137 -----------------------------------------------
  3 files changed, 149 insertions(+), 137 deletions(-)
autostash.c +0 -0
index 62ec7a7c80..eb58e0c8a4 100644

autostash.c line 1

Quoted 1 line
autostash.c +1 -0
@@ -1,9 +1,17 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS

It might be nicer to have a preparatory step that fixes this by adding a 'struct repository *r' argument to the function in builtin/rebase.c before moving the function. You could also do the same for next patch and then move both functions together.

Best Wishes

Phillip

Quoted 324 lines
autostash.c +136 -0
+
  #include "git-compat-util.h"
  #include "autostash.h"
+#include "cache-tree.h"
  #include "dir.h"
  #include "gettext.h"
+#include "lockfile.h"
+#include "refs.h"
  #include "run-command.h"
  #include "strbuf.h"
+#include "tree-walk.h"
+#include "tree.h"
+#include "unpack-trees.h"
 
  int read_one(const char *path, struct strbuf *buf)
  {
@@ -13,6 +21,135 @@ int read_one(const char *path, struct strbuf *buf)
  	return 0;
  }
 
+int reset_head(struct object_id *oid, const char *action,
+	       const char *switch_to_branch, unsigned flags,
+	       const char *reflog_orig_head, const char *reflog_head)
+{
+	unsigned detach_head = flags & RESET_HEAD_DETACH;
+	unsigned reset_hard = flags & RESET_HEAD_HARD;
+	unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
+	unsigned refs_only = flags & RESET_HEAD_REFS_ONLY;
+	unsigned update_orig_head = flags & RESET_ORIG_HEAD;
+	struct object_id head_oid;
+	struct tree_desc desc[2] = { { NULL }, { NULL } };
+	struct lock_file lock = LOCK_INIT;
+	struct unpack_trees_options unpack_tree_opts;
+	struct tree *tree;
+	const char *reflog_action;
+	struct strbuf msg = STRBUF_INIT;
+	size_t prefix_len;
+	struct object_id *orig = NULL, oid_orig,
+		*old_orig = NULL, oid_old_orig;
+	int ret = 0, nr = 0;
+
+	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
+		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
+
+	if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
+		ret = -1;
+		goto leave_reset_head;
+	}
+
+	if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) {
+		ret = error(_("could not determine HEAD revision"));
+		goto leave_reset_head;
+	}
+
+	if (!oid)
+		oid = &head_oid;
+
+	if (refs_only)
+		goto reset_head_refs;
+
+	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
+	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
+	unpack_tree_opts.head_idx = 1;
+	unpack_tree_opts.src_index = the_repository->index;
+	unpack_tree_opts.dst_index = the_repository->index;
+	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
+	unpack_tree_opts.update = 1;
+	unpack_tree_opts.merge = 1;
+	if (!detach_head)
+		unpack_tree_opts.reset = 1;
+
+	if (repo_read_index_unmerged(the_repository) < 0) {
+		ret = error(_("could not read index"));
+		goto leave_reset_head;
+	}
+
+	if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {
+		ret = error(_("failed to find tree of %s"),
+			    oid_to_hex(&head_oid));
+		goto leave_reset_head;
+	}
+
+	if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {
+		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
+		goto leave_reset_head;
+	}
+
+	if (unpack_trees(nr, desc, &unpack_tree_opts)) {
+		ret = -1;
+		goto leave_reset_head;
+	}
+
+	tree = parse_tree_indirect(oid);
+	prime_cache_tree(the_repository, the_repository->index, tree);
+
+	if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {
+		ret = error(_("could not write index"));
+		goto leave_reset_head;
+	}
+
+reset_head_refs:
+	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
+	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
+	prefix_len = msg.len;
+
+	if (update_orig_head) {
+		if (!get_oid("ORIG_HEAD", &oid_old_orig))
+			old_orig = &oid_old_orig;
+		if (!get_oid("HEAD", &oid_orig)) {
+			orig = &oid_orig;
+			if (!reflog_orig_head) {
+				strbuf_addstr(&msg, "updating ORIG_HEAD");
+				reflog_orig_head = msg.buf;
+			}
+			update_ref(reflog_orig_head, "ORIG_HEAD", orig,
+				   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
+		} else if (old_orig)
+			delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+	}
+
+	if (!reflog_head) {
+		strbuf_setlen(&msg, prefix_len);
+		strbuf_addstr(&msg, "updating HEAD");
+		reflog_head = msg.buf;
+	}
+	if (!switch_to_branch)
+		ret = update_ref(reflog_head, "HEAD", oid, orig,
+				 detach_head ? REF_NO_DEREF : 0,
+				 UPDATE_REFS_MSG_ON_ERR);
+	else {
+		ret = update_ref(reflog_head, switch_to_branch, oid,
+				 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+		if (!ret)
+			ret = create_symref("HEAD", switch_to_branch,
+					    reflog_head);
+	}
+	if (run_hook)
+		run_hook_le(NULL, "post-checkout",
+			    oid_to_hex(orig ? orig : &null_oid),
+			    oid_to_hex(oid), "1", NULL);
+
+leave_reset_head:
+	strbuf_release(&msg);
+	rollback_lock_file(&lock);
+	while (nr)
+		free((void *)desc[--nr].buffer);
+	return ret;
+}
+
  int apply_autostash(const char *path)
  {
  	struct strbuf autostash = STRBUF_INIT;
autostash.h +12 -0
index 5f4e4bd22c..1406638166 100644
@@ -6,6 +6,18 @@
  /* Read one file, then strip line endings */
  int read_one(const char *path, struct strbuf *buf);
 
+#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
+
+#define RESET_HEAD_DETACH (1<<0)
+#define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
+#define RESET_HEAD_REFS_ONLY (1<<3)
+#define RESET_ORIG_HEAD (1<<4)
+
+int reset_head(struct object_id *oid, const char *action,
+	       const char *switch_to_branch, unsigned flags,
+	       const char *reflog_orig_head, const char *reflog_head);
+
  int apply_autostash(const char *path);
 
  #endif
builtin/rebase.c +0 -137
index 661928d427..c3165896cc 100644
@@ -734,143 +734,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)
  	}
  }
 
-#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
-
-#define RESET_HEAD_DETACH (1<<0)
-#define RESET_HEAD_HARD (1<<1)
-#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
-#define RESET_HEAD_REFS_ONLY (1<<3)
-#define RESET_ORIG_HEAD (1<<4)
-
-static int reset_head(struct object_id *oid, const char *action,
-		      const char *switch_to_branch, unsigned flags,
-		      const char *reflog_orig_head, const char *reflog_head)
-{
-	unsigned detach_head = flags & RESET_HEAD_DETACH;
-	unsigned reset_hard = flags & RESET_HEAD_HARD;
-	unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
-	unsigned refs_only = flags & RESET_HEAD_REFS_ONLY;
-	unsigned update_orig_head = flags & RESET_ORIG_HEAD;
-	struct object_id head_oid;
-	struct tree_desc desc[2] = { { NULL }, { NULL } };
-	struct lock_file lock = LOCK_INIT;
-	struct unpack_trees_options unpack_tree_opts;
-	struct tree *tree;
-	const char *reflog_action;
-	struct strbuf msg = STRBUF_INIT;
-	size_t prefix_len;
-	struct object_id *orig = NULL, oid_orig,
-		*old_orig = NULL, oid_old_orig;
-	int ret = 0, nr = 0;
-
-	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
-		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
-
-	if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
-		ret = -1;
-		goto leave_reset_head;
-	}
-
-	if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) {
-		ret = error(_("could not determine HEAD revision"));
-		goto leave_reset_head;
-	}
-
-	if (!oid)
-		oid = &head_oid;
-
-	if (refs_only)
-		goto reset_head_refs;
-
-	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
-	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
-	unpack_tree_opts.head_idx = 1;
-	unpack_tree_opts.src_index = the_repository->index;
-	unpack_tree_opts.dst_index = the_repository->index;
-	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
-	unpack_tree_opts.update = 1;
-	unpack_tree_opts.merge = 1;
-	if (!detach_head)
-		unpack_tree_opts.reset = 1;
-
-	if (repo_read_index_unmerged(the_repository) < 0) {
-		ret = error(_("could not read index"));
-		goto leave_reset_head;
-	}
-
-	if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {
-		ret = error(_("failed to find tree of %s"),
-			    oid_to_hex(&head_oid));
-		goto leave_reset_head;
-	}
-
-	if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {
-		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
-		goto leave_reset_head;
-	}
-
-	if (unpack_trees(nr, desc, &unpack_tree_opts)) {
-		ret = -1;
-		goto leave_reset_head;
-	}
-
-	tree = parse_tree_indirect(oid);
-	prime_cache_tree(the_repository, the_repository->index, tree);
-
-	if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {
-		ret = error(_("could not write index"));
-		goto leave_reset_head;
-	}
-
-reset_head_refs:
-	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
-	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
-	prefix_len = msg.len;
-
-	if (update_orig_head) {
-		if (!get_oid("ORIG_HEAD", &oid_old_orig))
-			old_orig = &oid_old_orig;
-		if (!get_oid("HEAD", &oid_orig)) {
-			orig = &oid_orig;
-			if (!reflog_orig_head) {
-				strbuf_addstr(&msg, "updating ORIG_HEAD");
-				reflog_orig_head = msg.buf;
-			}
-			update_ref(reflog_orig_head, "ORIG_HEAD", orig,
-				   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
-		} else if (old_orig)
-			delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
-	}
-
-	if (!reflog_head) {
-		strbuf_setlen(&msg, prefix_len);
-		strbuf_addstr(&msg, "updating HEAD");
-		reflog_head = msg.buf;
-	}
-	if (!switch_to_branch)
-		ret = update_ref(reflog_head, "HEAD", oid, orig,
-				 detach_head ? REF_NO_DEREF : 0,
-				 UPDATE_REFS_MSG_ON_ERR);
-	else {
-		ret = update_ref(reflog_head, switch_to_branch, oid,
-				 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
-		if (!ret)
-			ret = create_symref("HEAD", switch_to_branch,
-					    reflog_head);
-	}
-	if (run_hook)
-		run_hook_le(NULL, "post-checkout",
-			    oid_to_hex(orig ? orig : &null_oid),
-			    oid_to_hex(oid), "1", NULL);
-
-leave_reset_head:
-	strbuf_release(&msg);
-	rollback_lock_file(&lock);
-	while (nr)
-		free((void *)desc[--nr].buffer);
-	return ret;
-}
-
  static int move_to_original_branch(struct rebase_options *opts)
  {
  	struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
#19 lore raw

Re: [RFC PATCH 6/7] autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS

In reply to Denton Liu

Hi Denton

Quoted 21 lines · Denton Liu

Since autostash.c was recently introduced, we should avoid USE_THE_INDEX_COMPATIBILITY_MACROS since we are trying to move away from this in the rest of the codebase. Rewrite the autostash code to not need it and remove its definition.

---
  autostash.c | 8 +++-----
  1 file changed, 3 insertions(+), 5 deletions(-)
autostash.c +0 -2
index 722cf78b12..0a1f00d2e5 100644
@@ -1,5 +1,3 @@
-#define USE_THE_INDEX_COMPATIBILITY_MACROS
-
  #include "git-compat-util.h"
  #include "autostash.h"
  #include "cache-tree.h"

autostash.c int reset_head(struct object_id *oid, const char *action, · line 47

Quoted 5 lines
autostash.c +1 -1
@@ -46,7 +44,7 @@ int reset_head(struct object_id *oid, const char *action,
  	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
  		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
 
-	if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
+	if (!refs_only && repo_hold_locked_index(the_repository, &lock, LOCK_REPORT_ON_ERROR) < 0) {

As I understand it the reason for moving away from hold_locked_index() is that it relies on a global variable. While replacing it with an explicit reference the the_repository removes the implicit dependency on global state, it does not move us away from depending on a global variable. I think it would be nicer to change these functions to take a struct repository* before moving them.

Best Wishes

Phillip

Quoted 15 lines
autostash.c +2 -2
  		ret = -1;
  		goto leave_reset_head;
  	}
@@ -157,8 +155,8 @@ void perform_autostash(const char *path)
  	struct lock_file lock_file = LOCK_INIT;
  	int fd;
 
-	fd = hold_locked_index(&lock_file, 0);
-	refresh_cache(REFRESH_QUIET);
+	fd = repo_hold_locked_index(the_repository, &lock_file, 0);
+	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
  	if (0 <= fd)
  		repo_update_index_if_able(the_repository, &lock_file);
  	rollback_lock_file(&lock_file);
#21 lore raw

Re: [RFC PATCH 7/7] merge: teach --autostash option

In reply to Denton Liu

Hi Denton

Quoted 3 lines · Denton Liu

In rebase, one can pass the --autostash option to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.

It might be helpful to say why this option is useful. I can see one might want to stash before doing git pull in the same way as one might before a rebase but what's the motivation for doing it before a normal merge?

Quoted 90 lines

Implement the --autostash option and corresponding merge.autoStash option in merge which stashes before merging and then pops after.

---
  builtin/merge.c | 13 +++++++++++++
  builtin/pull.c  |  9 +++++----
  t/t5520-pull.sh |  8 --------
  3 files changed, 18 insertions(+), 12 deletions(-)
builtin/merge.c +13 -0
index 062e911441..d1a5eaad0d 100644
@@ -40,6 +40,7 @@
  #include "branch.h"
  #include "commit-reach.h"
  #include "wt-status.h"
+#include "autostash.h"
 
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {
  	NULL
  };
 
+static GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
+
  static int show_diffstat = 1, shortlog_len = -1, squash;
  static int option_commit = -1;
  static int option_edit = -1;
@@ -81,6 +84,7 @@ static int show_progress = -1;
  static int default_to_upstream = 1;
  static int signoff;
  static const char *sign_commit;
+static int autostash;
  static int no_verify;
 
  static struct strategy all_strategy[] = {
@@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {
  	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
  	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
  	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+	OPT_BOOL(0, "autostash", &autostash,
+	      N_("automatically stash/stash pop before and after")),
  	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
  	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
  	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,
  		strbuf_addf(&reflog_message, "%s: %s",
  			getenv("GIT_REFLOG_ACTION"), msg);
  	}
+	apply_autostash(merge_autostash());
  	if (squash) {
  		squash_message(head_commit, remoteheads);
  	} else {
@@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
  	} else if (!strcmp(k, "commit.gpgsign")) {
  		sign_commit = git_config_bool(k, v) ? "" : NULL;
  		return 0;
+	} else if (!strcmp(k, "merge.autostash")) {
+		autostash = git_config_bool(k, v);
+		return 0;
  	}
 
  	status = fmt_merge_msg_config(k, v, cb);
@@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
  		for (j = common; j; j = j->next)
  			commit_list_insert(j->item, &reversed);
 
+		if (autostash)
+			perform_autostash(merge_autostash());
  		hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
  		clean = merge_recursive(&o, head,
  				remoteheads->item, reversed, &result);
@@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
  		/* Invoke 'git reset --merge' */
  		ret = cmd_reset(nargc, nargv, prefix);
+		apply_autostash(merge_autostash());
  		goto done;
  	}
 
builtin/pull.c +0 -0
index d25ff13a60..ee186781ae 100644

builtin/pull.c static struct option pull_options[] = { · line 186

Quoted 5 lines
builtin/pull.c +1 -1
@@ -183,7 +183,7 @@ static struct option pull_options[] = {
  		N_("verify that the named commit has a valid GPG signature"),
  		PARSE_OPT_NOARG),
  	OPT_BOOL(0, "autostash", &opt_autostash,
-		N_("automatically stash/stash pop before and after rebase")),
+		N_("automatically stash/stash pop before and after")),

I've not looked closely at the code in this patch but noticed this. I think it would read better if it said

automatically stash/stash pop before and after pulling

Best Wishes

Phillip

Quoted 44 lines
builtin/pull.c +4 -3
  	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
  		N_("merge strategy to use"),
  		0),
@@ -671,6 +671,10 @@ static int run_merge(void)
  	argv_array_pushv(&args, opt_strategy_opts.argv);
  	if (opt_gpg_sign)
  		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");
  	if (opt_allow_unrelated_histories > 0)
  		argv_array_push(&args, "--allow-unrelated-histories");
 
@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
  	if (get_oid("HEAD", &orig_head))
  		oidclr(&orig_head);
 
-	if (!opt_rebase && opt_autostash != -1)
-		die(_("--[no-]autostash option is only valid with --rebase."));
-
  	autostash = config_autostash;
  	if (opt_rebase) {
  		if (opt_autostash != -1)
t/t5520-pull.sh +0 -8
index cf4cc32fd0..75f162495a 100755
@@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
  	test_pull_autostash_fail --rebase --no-autostash
  '
 
-for i in --autostash --no-autostash
-do
-	test_expect_success "pull $i (without --rebase) is illegal" '
-		test_must_fail git pull $i . copy 2>err &&
-		test_i18ngrep "only valid with --rebase" err
-	'
-done
-
  test_expect_success 'pull.rebase' '
  	git reset --hard before-rebase &&
  	test_config pull.rebase true &&
#3 lore raw

Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists

In reply to Junio C Hamano

Hi Junio,

Quoted 20 lines · Junio C Hamano
Quoted 4 lines · Denton Liu

There are many += lists in the Makefile and, over time, they have gotten slightly out of order, alphabetically. Alphabetically sort all += lists to bring them back in order.
...

Hmm. I like the general thrust, but ...

Makefile LIB_OBJS += checkout.o · line 851

Quoted 5 lines

LIB_OBJS += combine-diff.o

Makefile +1 -1
-LIB_OBJS += commit.o
 LIB_OBJS += commit-graph.o
 LIB_OBJS += commit-reach.o
+LIB_OBJS += commit.o

... I do not particularly see this change (there may be similar ones) desirable. I'd find it it be much more natural to sort "commit-anything" after "commit", and that is true with or without the common extension ".o" added to these entries.

Quoted 2 lines

In short, flipping these entries because '.' sorts later than '-' is making the result look "less sorted", at least to me.

The problem with this argument is that it disagrees with ASCII, as - has code 0x2d while . has code 0x2e, i.e. it is lexicographically _larger_.

So Denton's patch does the correct thing.

Ciao,
Dscho

#11 lore raw

Re: [RFC PATCH 2/7] autostash: extract read_one() from rebase

In reply to Phillip Wood

Hi,

Quoted 33 lines · Phillip Wood

Hi Denton

Quoted 30 lines · Denton Liu

Begin the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.

---
  autostash.c      | 12 ++++++++++++
  autostash.h      |  9 +++++++++
  builtin/rebase.c | 10 +---------
  3 files changed, 22 insertions(+), 9 deletions(-)

create mode 100644 autostash.c
create mode 100644 autostash.h

autostash.c new file +9 -0
new file mode 100644
index 0000000000..a6898e0fda
@@ -0,0 +1,12 @@
+#include "git-compat-util.h"
+#include "autostash.h"
+#include "gettext.h"
+#include "strbuf.h"
+
+int read_one(const char *path, struct strbuf *buf)
+{
+	if (strbuf_read_file(buf, path, 0) < 0)
+		return error_errno(_("could not read '%s'"), path);
Quoted 8 lines

autostash.c line 12

Quoted 3 lines
autostash.c new file +3 -0
+	strbuf_trim_trailing_newline(buf);
+	return 0;
+}

This looks like it's doing a similar job to read_oneliner() in sequencer.c, is it possible to make that public and use it instead? (There may be a difference if the file is missing but that function already takes a flag so it could probably be modified easily enough.)

Oh, I would _love_ to see those two functions reconciled.

Thanks,
Dscho

Quoted 52 lines

Best Wishes

Phillip

Quoted 45 lines
autostash.h new file +9 -0
new file mode 100644
index 0000000000..4a8f504f12
@@ -0,0 +1,9 @@
+#ifndef AUTOSTASH_H
+#define AUTOSTASH_H
+
+#include "strbuf.h"
+
+/* Read one file, then strip line endings */
+int read_one(const char *path, struct strbuf *buf);
+
+#endif
builtin/rebase.c +1 -0
index 4a20582e72..9fd7de6b2f 100644
@@ -27,6 +27,7 @@
  #include "branch.h"
  #include "sequencer.h"
  #include "rebase-interactive.h"
+#include "autostash.h"
 
  static char const * const builtin_rebase_usage[] = {
  	N_("git rebase [-i] [options] [--exec <cmd>] "
@@ -561,15 +562,6 @@ static const char *state_dir_path(const char *filename,

struct rebase_options *o
return path.buf;
}

builtin/rebase.c +0 -9
-/* Read one file, then strip line endings */
-static int read_one(const char *path, struct strbuf *buf)
-{
-	if (strbuf_read_file(buf, path, 0) < 0)
-		return error_errno(_("could not read '%s'"), path);
-	strbuf_trim_trailing_newline(buf);
-	return 0;
-}
-
  /* Initialize the rebase options from the state directory. */
  static int read_basic_state(struct rebase_options *opts)
  {
author
#4 lore raw

Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists

In reply to Johannes Schindelin

Hi Johannes,

Quoted 24 lines · Johannes Schindelin

Hi Junio,

Quoted 20 lines · Junio C Hamano
Quoted 4 lines · Denton Liu

There are many += lists in the Makefile and, over time, they have gotten slightly out of order, alphabetically. Alphabetically sort all += lists to bring them back in order.
...

Hmm. I like the general thrust, but ...

Makefile LIB_OBJS += checkout.o · line 851

Quoted 5 lines

LIB_OBJS += combine-diff.o

Makefile +1 -1
-LIB_OBJS += commit.o
 LIB_OBJS += commit-graph.o
 LIB_OBJS += commit-reach.o
+LIB_OBJS += commit.o

... I do not particularly see this change (there may be similar ones) desirable. I'd find it it be much more natural to sort "commit-anything" after "commit", and that is true with or without the common extension ".o" added to these entries.

Quoted 8 lines
Quoted 2 lines

In short, flipping these entries because '.' sorts later than '-' is making the result look "less sorted", at least to me.

The problem with this argument is that it disagrees with ASCII, as - has code 0x2d while . has code 0x2e, i.e. it is lexicographically _larger_.

So Denton's patch does the correct thing.

I actually agree with Junio on this one. Without the prefixes, "commit" would go before "commit-graph" so I think it would make more sense to order with the prefixes removed instead of taking the naive ordering by just sorting each block.

Thanks,

Denton

Quoted 3 lines

Ciao,
Dscho

#15 lore raw

Re: [RFC PATCH 4/7] autostash: extract reset_head() from rebase

In reply to Phillip Wood

Hi,

[sorry, Phillip, my reply-all fu deserts me today, apparently.]

Quoted 19 lines · Phillip Wood

Hi Denton

It's great to see this being libified, I've had it in mind to do this so we can avoid forking 'git checkout' in sequencer.c

Quoted 13 lines · Denton Liu

Begin the process of lib-ifying the autostash code. In a future commit,

This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.
this will be used to implement --autostash in other builtins.

---
  autostash.c      | 137 +++++++++++++++++++++++++++++++++++++++++++++++
  autostash.h      |  12 +++++
  builtin/rebase.c | 137 -----------------------------------------------
  3 files changed, 149 insertions(+), 137 deletions(-)
Quoted 11 lines

autostash.c line 1

Quoted 6 lines
autostash.c +1 -0
index 62ec7a7c80..eb58e0c8a4 100644
@@ -1,9 +1,17 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS

It might be nicer to have a preparatory step that fixes this by adding a 'struct repository *r' argument to the function in builtin/rebase.c before moving the function. You could also do the same for next patch and then move both functions together.

In addition to that, I think that reset_head()

- should live in its own file, not be hidden in autostash.c,

- its default reflog action should _not_ be "rebase".

- ideally be made the working horse of builtin/reset.c,

- in addition to that struct repository *r, it should probably accept a struct index_state *index and a const char *worktree_directory, but that can easily come in the future, as needed.

Thanks,
Dscho

Quoted 337 lines

Best Wishes

Phillip

Quoted 331 lines
autostash.c +136 -0
+
  #include "git-compat-util.h"
  #include "autostash.h"
+#include "cache-tree.h"
  #include "dir.h"
  #include "gettext.h"
+#include "lockfile.h"
+#include "refs.h"
  #include "run-command.h"
  #include "strbuf.h"
+#include "tree-walk.h"
+#include "tree.h"
+#include "unpack-trees.h"
 
  int read_one(const char *path, struct strbuf *buf)
  {
@@ -13,6 +21,135 @@ int read_one(const char *path, struct strbuf *buf)
  	return 0;
  }
 
+int reset_head(struct object_id *oid, const char *action,
+	       const char *switch_to_branch, unsigned flags,
+	       const char *reflog_orig_head, const char *reflog_head)
+{
+	unsigned detach_head = flags & RESET_HEAD_DETACH;
+	unsigned reset_hard = flags & RESET_HEAD_HARD;
+	unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
+	unsigned refs_only = flags & RESET_HEAD_REFS_ONLY;
+	unsigned update_orig_head = flags & RESET_ORIG_HEAD;
+	struct object_id head_oid;
+	struct tree_desc desc[2] = { { NULL }, { NULL } };
+	struct lock_file lock = LOCK_INIT;
+	struct unpack_trees_options unpack_tree_opts;
+	struct tree *tree;
+	const char *reflog_action;
+	struct strbuf msg = STRBUF_INIT;
+	size_t prefix_len;
+	struct object_id *orig = NULL, oid_orig,
+		*old_orig = NULL, oid_old_orig;
+	int ret = 0, nr = 0;
+
+	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
+		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
+
+	if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)
 {
+		ret = -1;
+		goto leave_reset_head;
+	}
+
+	if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) {
+		ret = error(_("could not determine HEAD revision"));
+		goto leave_reset_head;
+	}
+
+	if (!oid)
+		oid = &head_oid;
+
+	if (refs_only)
+		goto reset_head_refs;
+
+	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
+	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
+	unpack_tree_opts.head_idx = 1;
+	unpack_tree_opts.src_index = the_repository->index;
+	unpack_tree_opts.dst_index = the_repository->index;
+	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
+	unpack_tree_opts.update = 1;
+	unpack_tree_opts.merge = 1;
+	if (!detach_head)
+		unpack_tree_opts.reset = 1;
+
+	if (repo_read_index_unmerged(the_repository) < 0) {
+		ret = error(_("could not read index"));
+		goto leave_reset_head;
+	}
+
+	if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++],
 &head_oid)) {
+		ret = error(_("failed to find tree of %s"),
+			    oid_to_hex(&head_oid));
+		goto leave_reset_head;
+	}
+
+	if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {
+		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
+		goto leave_reset_head;
+	}
+
+	if (unpack_trees(nr, desc, &unpack_tree_opts)) {
+		ret = -1;
+		goto leave_reset_head;
+	}
+
+	tree = parse_tree_indirect(oid);
+	prime_cache_tree(the_repository, the_repository->index, tree);
+
+	if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0)
 {
+		ret = error(_("could not write index"));
+		goto leave_reset_head;
+	}
+
+reset_head_refs:
+	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
+	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
+	prefix_len = msg.len;
+
+	if (update_orig_head) {
+		if (!get_oid("ORIG_HEAD", &oid_old_orig))
+			old_orig = &oid_old_orig;
+		if (!get_oid("HEAD", &oid_orig)) {
+			orig = &oid_orig;
+			if (!reflog_orig_head) {
+				strbuf_addstr(&msg, "updating ORIG_HEAD");
+				reflog_orig_head = msg.buf;
+			}
+			update_ref(reflog_orig_head, "ORIG_HEAD", orig,
+				   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
+		} else if (old_orig)
+			delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+	}
+
+	if (!reflog_head) {
+		strbuf_setlen(&msg, prefix_len);
+		strbuf_addstr(&msg, "updating HEAD");
+		reflog_head = msg.buf;
+	}
+	if (!switch_to_branch)
+		ret = update_ref(reflog_head, "HEAD", oid, orig,
+				 detach_head ? REF_NO_DEREF : 0,
+				 UPDATE_REFS_MSG_ON_ERR);
+	else {
+		ret = update_ref(reflog_head, switch_to_branch, oid,
+				 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+		if (!ret)
+			ret = create_symref("HEAD", switch_to_branch,
+					    reflog_head);
+	}
+	if (run_hook)
+		run_hook_le(NULL, "post-checkout",
+			    oid_to_hex(orig ? orig : &null_oid),
+			    oid_to_hex(oid), "1", NULL);
+
+leave_reset_head:
+	strbuf_release(&msg);
+	rollback_lock_file(&lock);
+	while (nr)
+		free((void *)desc[--nr].buffer);
+	return ret;
+}
+
  int apply_autostash(const char *path)
  {
  	struct strbuf autostash = STRBUF_INIT;
autostash.h +12 -0
index 5f4e4bd22c..1406638166 100644
@@ -6,6 +6,18 @@
  /* Read one file, then strip line endings */
  int read_one(const char *path, struct strbuf *buf);
 
+#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
+
+#define RESET_HEAD_DETACH (1<<0)
+#define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
+#define RESET_HEAD_REFS_ONLY (1<<3)
+#define RESET_ORIG_HEAD (1<<4)
+
+int reset_head(struct object_id *oid, const char *action,
+	       const char *switch_to_branch, unsigned flags,
+	       const char *reflog_orig_head, const char *reflog_head);
+
  int apply_autostash(const char *path);
 
  #endif
builtin/rebase.c +0 -84
index 661928d427..c3165896cc 100644
@@ -734,143 +734,6 @@ static void add_var(struct strbuf *buf, const char
 *name, const char *value)
  	}
  }
 
-#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
-
-#define RESET_HEAD_DETACH (1<<0)
-#define RESET_HEAD_HARD (1<<1)
-#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
-#define RESET_HEAD_REFS_ONLY (1<<3)
-#define RESET_ORIG_HEAD (1<<4)
-
-static int reset_head(struct object_id *oid, const char *action,
-		      const char *switch_to_branch, unsigned flags,
-		      const char *reflog_orig_head, const char *reflog_head)
-{
-	unsigned detach_head = flags & RESET_HEAD_DETACH;
-	unsigned reset_hard = flags & RESET_HEAD_HARD;
-	unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
-	unsigned refs_only = flags & RESET_HEAD_REFS_ONLY;
-	unsigned update_orig_head = flags & RESET_ORIG_HEAD;
-	struct object_id head_oid;
-	struct tree_desc desc[2] = { { NULL }, { NULL } };
-	struct lock_file lock = LOCK_INIT;
-	struct unpack_trees_options unpack_tree_opts;
-	struct tree *tree;
-	const char *reflog_action;
-	struct strbuf msg = STRBUF_INIT;
-	size_t prefix_len;
-	struct object_id *orig = NULL, oid_orig,
-		*old_orig = NULL, oid_old_orig;
-	int ret = 0, nr = 0;
-
-	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
-		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
-
-	if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)
 {
-		ret = -1;
-		goto leave_reset_head;
-	}
-
-	if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) {
-		ret = error(_("could not determine HEAD revision"));
-		goto leave_reset_head;
-	}
-
-	if (!oid)
-		oid = &head_oid;
-
-	if (refs_only)
-		goto reset_head_refs;
-
-	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
-	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
-	unpack_tree_opts.head_idx = 1;
-	unpack_tree_opts.src_index = the_repository->index;
-	unpack_tree_opts.dst_index = the_repository->index;
-	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
-	unpack_tree_opts.update = 1;
-	unpack_tree_opts.merge = 1;
-	if (!detach_head)
-		unpack_tree_opts.reset = 1;
-
-	if (repo_read_index_unmerged(the_repository) < 0) {
-		ret = error(_("could not read index"));
-		goto leave_reset_head;
-	}
-
-	if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++],
 &head_oid)) {
-		ret = error(_("failed to find tree of %s"),
-			    oid_to_hex(&head_oid));
-		goto leave_reset_head;
-	}
-
-	if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {
-		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
-		goto leave_reset_head;
-	}
-
-	if (unpack_trees(nr, desc, &unpack_tree_opts)) {
-		ret = -1;
-		goto leave_reset_head;
-	}
-
-	tree = parse_tree_indirect(oid);
-	prime_cache_tree(the_repository, the_repository->index, tree);
-
-	if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0)

{

builtin/rebase.c +0 -53
-		ret = error(_("could not write index"));
-		goto leave_reset_head;
-	}
-
-reset_head_refs:
-	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
-	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
-	prefix_len = msg.len;
-
-	if (update_orig_head) {
-		if (!get_oid("ORIG_HEAD", &oid_old_orig))
-			old_orig = &oid_old_orig;
-		if (!get_oid("HEAD", &oid_orig)) {
-			orig = &oid_orig;
-			if (!reflog_orig_head) {
-				strbuf_addstr(&msg, "updating ORIG_HEAD");
-				reflog_orig_head = msg.buf;
-			}
-			update_ref(reflog_orig_head, "ORIG_HEAD", orig,
-				   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
-		} else if (old_orig)
-			delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
-	}
-
-	if (!reflog_head) {
-		strbuf_setlen(&msg, prefix_len);
-		strbuf_addstr(&msg, "updating HEAD");
-		reflog_head = msg.buf;
-	}
-	if (!switch_to_branch)
-		ret = update_ref(reflog_head, "HEAD", oid, orig,
-				 detach_head ? REF_NO_DEREF : 0,
-				 UPDATE_REFS_MSG_ON_ERR);
-	else {
-		ret = update_ref(reflog_head, switch_to_branch, oid,
-				 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
-		if (!ret)
-			ret = create_symref("HEAD", switch_to_branch,
-					    reflog_head);
-	}
-	if (run_hook)
-		run_hook_le(NULL, "post-checkout",
-			    oid_to_hex(orig ? orig : &null_oid),
-			    oid_to_hex(oid), "1", NULL);
-
-leave_reset_head:
-	strbuf_release(&msg);
-	rollback_lock_file(&lock);
-	while (nr)
-		free((void *)desc[--nr].buffer);
-	return ret;
-}
-
  static int move_to_original_branch(struct rebase_options *opts)
  {
   struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
#17 lore raw

Re: [RFC PATCH 5/7] autostash: extract perform_autostash() from rebase

In reply to Denton Liu

Hi Denton,

Quoted 26 lines · Denton Liu

Continue the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.

---
 autostash.c      | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 autostash.h      |  1 +
 builtin/rebase.c | 44 +-------------------------------------------
 3 files changed, 48 insertions(+), 43 deletions(-)
autostash.c +1 -0
index eb58e0c8a4..722cf78b12 100644
@@ -12,6 +12,7 @@
 #include "tree-walk.h"
 #include "tree.h"
 #include "unpack-trees.h"
+#include "wt-status.h"
 
 int read_one(const char *path, struct strbuf *buf)
 {

autostash.c int reset_head(struct object_id *oid, const char *action, · line 154

Quoted 4 lines
autostash.c +1 -0
@@ -150,6 +151,51 @@ int reset_head(struct object_id *oid, const char *action,
 	return ret;
 }
 
+void perform_autostash(const char *path)

Maybe we can find a better name than "perform_autostash"? It is not clear whether this is the "saving" or "applying" part of the autostash, I think.

Maybe save_autostash()? And maybe instead of path, the parameter could be save_hash_to_path or something similar?

Now that I think of it, I forgot to mention in a reply to an earlier patch in this series that reset_head() might be too generic a name to be a global function...

Ciao,
Dscho

Quoted 118 lines
autostash.c +44 -0
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct lock_file lock_file = LOCK_INIT;
+	int fd;
+
+	fd = hold_locked_index(&lock_file, 0);
+	refresh_cache(REFRESH_QUIET);
+	if (0 <= fd)
+		repo_update_index_if_able(the_repository, &lock_file);
+	rollback_lock_file(&lock_file);
+
+	if (has_unstaged_changes(the_repository, 1) ||
+	    has_uncommitted_changes(the_repository, 1)) {
+		struct child_process stash = CHILD_PROCESS_INIT;
+		struct object_id oid;
+
+		argv_array_pushl(&stash.args,
+				 "stash", "create", "autostash", NULL);
+		stash.git_cmd = 1;
+		stash.no_stdin = 1;
+		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
+			die(_("Cannot autostash"));
+		strbuf_trim_trailing_newline(&buf);
+		if (get_oid(buf.buf, &oid))
+			die(_("Unexpected stash response: '%s'"),
+			    buf.buf);
+		strbuf_reset(&buf);
+		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
+
+		if (safe_create_leading_directories_const(path))
+			die(_("Could not create directory for '%s'"),
+			    path);
+		write_file(path, "%s", oid_to_hex(&oid));
+		printf(_("Created autostash: %s\n"), buf.buf);
+		if (reset_head(NULL, "reset --hard",
+			       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
+			die(_("could not reset --hard"));
+
+		if (discard_index(the_repository->index) < 0 ||
+			repo_read_index(the_repository) < 0)
+			die(_("could not read index"));
+	}
+}
+
 int apply_autostash(const char *path)
 {
 	struct strbuf autostash = STRBUF_INIT;
autostash.h +1 -0
index 1406638166..e08ccb9881 100644
@@ -18,6 +18,7 @@ int reset_head(struct object_id *oid, const char *action,
 	       const char *switch_to_branch, unsigned flags,
 	       const char *reflog_orig_head, const char *reflog_head);
 
+void perform_autostash(const char *path);
 int apply_autostash(const char *path);
 
 #endif
builtin/rebase.c +1 -44
index c3165896cc..c4decdfb5b 100644
@@ -1797,49 +1797,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		die(_("could not read index"));
 
 	if (options.autostash) {
-		struct lock_file lock_file = LOCK_INIT;
-		int fd;
-
-		fd = hold_locked_index(&lock_file, 0);
-		refresh_cache(REFRESH_QUIET);
-		if (0 <= fd)
-			repo_update_index_if_able(the_repository, &lock_file);
-		rollback_lock_file(&lock_file);
-
-		if (has_unstaged_changes(the_repository, 1) ||
-		    has_uncommitted_changes(the_repository, 1)) {
-			const char *autostash =
-				state_dir_path("autostash", &options);
-			struct child_process stash = CHILD_PROCESS_INIT;
-			struct object_id oid;
-
-			argv_array_pushl(&stash.args,
-					 "stash", "create", "autostash", NULL);
-			stash.git_cmd = 1;
-			stash.no_stdin = 1;
-			strbuf_reset(&buf);
-			if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
-				die(_("Cannot autostash"));
-			strbuf_trim_trailing_newline(&buf);
-			if (get_oid(buf.buf, &oid))
-				die(_("Unexpected stash response: '%s'"),
-				    buf.buf);
-			strbuf_reset(&buf);
-			strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
-
-			if (safe_create_leading_directories_const(autostash))
-				die(_("Could not create directory for '%s'"),
-				    options.state_dir);
-			write_file(autostash, "%s", oid_to_hex(&oid));
-			printf(_("Created autostash: %s\n"), buf.buf);
-			if (reset_head(NULL, "reset --hard",
-				       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
-				die(_("could not reset --hard"));
-
-			if (discard_index(the_repository->index) < 0 ||
-				repo_read_index(the_repository) < 0)
-				die(_("could not read index"));
-		}
+		perform_autostash(state_dir_path("autostash", &options));
 	}
 
 	if (require_clean_work_tree(the_repository, "rebase",
--

2.23.0.897.g0a19638b1e

#23 lore raw

Re: [RFC PATCH 7/7] merge: teach --autostash option

In reply to Denton Liu

Hi Denton,

Quoted 8 lines · Denton Liu

In rebase, one can pass the --autostash option to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.

Implement the --autostash option and corresponding merge.autoStash option in merge which stashes before merging and then pops after.

Maybe "Suggested-by" would be more accurate, it is not like this feature request was a bug report...

Quoted 46 lines
---
 builtin/merge.c | 13 +++++++++++++
 builtin/pull.c  |  9 +++++----
 t/t5520-pull.sh |  8 --------
 3 files changed, 18 insertions(+), 12 deletions(-)
builtin/merge.c +6 -0
index 062e911441..d1a5eaad0d 100644
@@ -40,6 +40,7 @@
 #include "branch.h"
 #include "commit-reach.h"
 #include "wt-status.h"
+#include "autostash.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {
 	NULL
 };
 
+static GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
+
 static int show_diffstat = 1, shortlog_len = -1, squash;
 static int option_commit = -1;
 static int option_edit = -1;
@@ -81,6 +84,7 @@ static int show_progress = -1;
 static int default_to_upstream = 1;
 static int signoff;
 static const char *sign_commit;
+static int autostash;
 static int no_verify;
 
 static struct strategy all_strategy[] = {
@@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {
 	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
 	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
 	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+	OPT_BOOL(0, "autostash", &autostash,
+	      N_("automatically stash/stash pop before and after")),
 	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
 	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
 	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),

builtin/merge.c static void finish(struct commit *head_commit, · line 449

Quoted 4 lines
builtin/merge.c +1 -0
@@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,
 		strbuf_addf(&reflog_message, "%s: %s",
 			getenv("GIT_REFLOG_ACTION"), msg);
 	}
+	apply_autostash(merge_autostash());

Should this not be guarded by if (autostash)?

Quoted 23 lines
builtin/merge.c +5 -0
 	if (squash) {
 		squash_message(head_commit, remoteheads);
 	} else {
@@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 	} else if (!strcmp(k, "commit.gpgsign")) {
 		sign_commit = git_config_bool(k, v) ? "" : NULL;
 		return 0;
+	} else if (!strcmp(k, "merge.autostash")) {
+		autostash = git_config_bool(k, v);
+		return 0;
 	}
 
 	status = fmt_merge_msg_config(k, v, cb);
@@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
 		for (j = common; j; j = j->next)
 			commit_list_insert(j->item, &reversed);
 
+		if (autostash)
+			perform_autostash(merge_autostash());
 		hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
 		clean = merge_recursive(&o, head,
 				remoteheads->item, reversed, &result);

builtin/merge.c int cmd_merge(int argc, const char **argv, const char *prefix) · line 1303

Quoted 4 lines
builtin/merge.c +1 -0
@@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
 		/* Invoke 'git reset --merge' */
 		ret = cmd_reset(nargc, nargv, prefix);
+		apply_autostash(merge_autostash());

Again, this should be guarded by if (autostash), methinks.

builtin/pull.c static struct option pull_options[] = { · line 186

Quoted 13 lines
builtin/merge.c +0 -0
 		goto done;
 	}
 
builtin/pull.c +1 -1
index d25ff13a60..ee186781ae 100644
@@ -183,7 +183,7 @@ static struct option pull_options[] = {
 		N_("verify that the named commit has a valid GPG signature"),
 		PARSE_OPT_NOARG),
 	OPT_BOOL(0, "autostash", &opt_autostash,
-		N_("automatically stash/stash pop before and after rebase")),
+		N_("automatically stash/stash pop before and after")),

Makes sense; this is now shared between the rebase and the merge modes.

builtin/pull.c static int run_merge(void) · line 677

Quoted 11 lines
builtin/pull.c +4 -0
 	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
 		N_("merge strategy to use"),
 		0),
@@ -671,6 +671,10 @@ static int run_merge(void)
 	argv_array_pushv(&args, opt_strategy_opts.argv);
 	if (opt_gpg_sign)
 		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");

Or shorter:

	argv_array_pushf(&args, "%s-autostash", opt_autostash ? "-" : "--no");

Ah, but that would mishandle -1. I bet I will be puzzled by this again. Maybe it would make sense to mention in a code comment that it can be -1 in which case we leave it to rebase to use the config settings to determine whether or not to autostash.

Quoted 19 lines
builtin/pull.c +0 -3
 	if (opt_allow_unrelated_histories > 0)
 		argv_array_push(&args, "--allow-unrelated-histories");
 
@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 	if (get_oid("HEAD", &orig_head))
 		oidclr(&orig_head);
 
-	if (!opt_rebase && opt_autostash != -1)
-		die(_("--[no-]autostash option is only valid with --rebase."));
-
 	autostash = config_autostash;
 	if (opt_rebase) {
 		if (opt_autostash != -1)
t/t5520-pull.sh +0 -0
index cf4cc32fd0..75f162495a 100755
@@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
 	test_pull_autostash_fail --rebase --no-autostash

t/t5520-pull.sh test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' ' · line 375

Quoted 10 lines
t/t5520-pull.sh +0 -8
 '
 
-for i in --autostash --no-autostash
-do
-	test_expect_success "pull $i (without --rebase) is illegal" '
-		test_must_fail git pull $i . copy 2>err &&
-		test_i18ngrep "only valid with --rebase" err
-	'
-done
-

Nice!
Dscho

Quoted 7 lines
t/t5520-pull.sh +0 -1
 test_expect_success 'pull.rebase' '
 	git reset --hard before-rebase &&
 	test_config pull.rebase true &&
--

2.23.0.897.g0a19638b1e

#6 lore raw

Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists

In reply to Johannes Schindelin
Quoted 11 lines · Johannes Schindelin
Quoted 7 lines

... I do not particularly see this change (there may be similar ones) desirable. I'd find it it be much more natural to sort "commit-anything" after "commit", and that is true with or without the common extension ".o" added to these entries.

In short, flipping these entries because '.' sorts later than '-' is making the result look "less sorted", at least to me.

The problem with this argument is that it disagrees with ASCII, as - has code 0x2d while . has code 0x2e, i.e. it is lexicographically _larger_.

I am saying that sorting these in ASCII order did not produce result that is easy to the eyes.

You are saying that Denton's patch sorted these lines in ASCII order.

I agree with you that it did correctly sort them in ASCII order.

That does not make the patch right ;-)

#7 lore raw

Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists

Quoted 14 lines · Junio C Hamano
Quoted 11 lines · Johannes Schindelin
Quoted 7 lines

... I do not particularly see this change (there may be similar ones) desirable. I'd find it it be much more natural to sort "commit-anything" after "commit", and that is true with or without the common extension ".o" added to these entries.

In short, flipping these entries because '.' sorts later than '-' is making the result look "less sorted", at least to me.

The problem with this argument is that it disagrees with ASCII, as - has code 0x2d while . has code 0x2e, i.e. it is lexicographically _larger_.

Quoted 8 lines

I am saying that sorting these in ASCII order did not produce result that is easy to the eyes.

You are saying that Denton's patch sorted these lines in ASCII order.

I agree with you that it did correctly sort them in ASCII order.

That does not make the patch right ;-)

What's the purpose of sorting them, though? I thought it was less for aesthetics and more to to keep lines deterministic (to avoid two branches adding the same line in different places, thus causing weirdness when the two are merged). In that case, I think we care less about the exact order and more that anybody can easily reproduce the same sort (by running "10:!sort" or whatever you weird emacs-types would type).

-Peff

#8 lore raw

Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists

Quoted 10 lines · Jeff King
Quoted 2 lines

...
I agree with you that it did correctly sort them in ASCII order.

What's the purpose of sorting them, though? I thought it was less for aesthetics and more to to keep lines deterministic (to avoid two branches adding the same line in different places, thus causing weirdness when the two are merged). In that case, I think we care less about the exact order and more that anybody can easily reproduce the same sort (by running "10:!sort" or whatever you weird emacs-types would type).

In the ideal world, "sort" would have a handy option we can tell it to reshuffle the ASCII table in such a way that all punctuations come before alphanumeric, making sure "/" and "." are the first two letters in the alphabet, and everybody can use it to sort the lines reproducibly and also readably. But I do not know of such a widely used implementation of "sort", so...

If we had known better, we would have used such a custom sort order to sort the index entries, making sure that slash sorts before any other byte ;-)

#5 lore raw

Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists

In reply to Denton Liu

Hi,

Quoted 34 lines · Denton Liu

Hi Johannes,

Quoted 31 lines · Johannes Schindelin

Hi Junio,

Quoted 20 lines · Junio C Hamano
Quoted 4 lines · Denton Liu

There are many += lists in the Makefile and, over time, they have gotten slightly out of order, alphabetically. Alphabetically sort all += lists to bring them back in order.
...

Hmm. I like the general thrust, but ...

Makefile LIB_OBJS += checkout.o · line 851

Quoted 5 lines

LIB_OBJS += combine-diff.o

Makefile +1 -1
-LIB_OBJS += commit.o
 LIB_OBJS += commit-graph.o
 LIB_OBJS += commit-reach.o
+LIB_OBJS += commit.o

... I do not particularly see this change (there may be similar ones) desirable. I'd find it it be much more natural to sort "commit-anything" after "commit", and that is true with or without the common extension ".o" added to these entries.

Quoted 2 lines

In short, flipping these entries because '.' sorts later than '-' is making the result look "less sorted", at least to me.

The problem with this argument is that it disagrees with ASCII, as - has code 0x2d while . has code 0x2e, i.e. it is lexicographically _larger_.

Quoted 6 lines
Quoted 1 line

So Denton's patch does the correct thing.

I actually agree with Junio on this one. Without the prefixes, "commit" would go before "commit-graph" so I think it would make more sense to order with the prefixes removed instead of taking the naive ordering by just sorting each block.

That will make it harder on other contributors like me, who prefer to mark the lines in vim and then call :sort on them, and then not care about it any further.

Any decision that makes automating tedious tasks harder puts more burden on human beings. I don't like that.

Ciao,
Dscho

Quoted 9 lines

Thanks,

Denton

Quoted 3 lines

Ciao,
Dscho

author
#22 lore raw

Re: [RFC PATCH 7/7] merge: teach --autostash option

In reply to Phillip Wood

Hi Phillip,

Quoted 10 lines · Phillip Wood

Hi Denton

Quoted 3 lines · Denton Liu

In rebase, one can pass the --autostash option to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.

It might be helpful to say why this option is useful. I can see one might want to stash before doing git pull in the same way as one might before a rebase but what's the motivation for doing it before a normal merge?

Will do.

Quoted 93 lines
Quoted 84 lines

Implement the --autostash option and corresponding merge.autoStash option in merge which stashes before merging and then pops after.

---
  builtin/merge.c | 13 +++++++++++++
  builtin/pull.c  |  9 +++++----
  t/t5520-pull.sh |  8 --------
  3 files changed, 18 insertions(+), 12 deletions(-)
builtin/merge.c +13 -0
index 062e911441..d1a5eaad0d 100644
@@ -40,6 +40,7 @@
  #include "branch.h"
  #include "commit-reach.h"
  #include "wt-status.h"
+#include "autostash.h"
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {
  	NULL
  };
+static GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
+
  static int show_diffstat = 1, shortlog_len = -1, squash;
  static int option_commit = -1;
  static int option_edit = -1;
@@ -81,6 +84,7 @@ static int show_progress = -1;
  static int default_to_upstream = 1;
  static int signoff;
  static const char *sign_commit;
+static int autostash;
  static int no_verify;
  static struct strategy all_strategy[] = {
@@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {
  	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
  	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
  	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+	OPT_BOOL(0, "autostash", &autostash,
+	      N_("automatically stash/stash pop before and after")),
  	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
  	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
  	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,
  		strbuf_addf(&reflog_message, "%s: %s",
  			getenv("GIT_REFLOG_ACTION"), msg);
  	}
+	apply_autostash(merge_autostash());
  	if (squash) {
  		squash_message(head_commit, remoteheads);
  	} else {
@@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
  	} else if (!strcmp(k, "commit.gpgsign")) {
  		sign_commit = git_config_bool(k, v) ? "" : NULL;
  		return 0;
+	} else if (!strcmp(k, "merge.autostash")) {
+		autostash = git_config_bool(k, v);
+		return 0;
  	}
  	status = fmt_merge_msg_config(k, v, cb);
@@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
  		for (j = common; j; j = j->next)
  			commit_list_insert(j->item, &reversed);
+		if (autostash)
+			perform_autostash(merge_autostash());
  		hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
  		clean = merge_recursive(&o, head,
  				remoteheads->item, reversed, &result);
@@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
  		/* Invoke 'git reset --merge' */
  		ret = cmd_reset(nargc, nargv, prefix);
+		apply_autostash(merge_autostash());
  		goto done;
  	}
builtin/pull.c +0 -0
index d25ff13a60..ee186781ae 100644

builtin/pull.c static struct option pull_options[] = { · line 186

Quoted 5 lines
builtin/pull.c +1 -1
@@ -183,7 +183,7 @@ static struct option pull_options[] = {
  		N_("verify that the named commit has a valid GPG signature"),
  		PARSE_OPT_NOARG),
  	OPT_BOOL(0, "autostash", &opt_autostash,
-		N_("automatically stash/stash pop before and after rebase")),
+		N_("automatically stash/stash pop before and after")),

I've not looked closely at the code in this patch but noticed this. I think it would read better if it said

Quoted 1 line

automatically stash/stash pop before and after pulling

Since this is an argh argument, it says that we should "keep it homogenous across the repository". Since rebase (which I copied it from), merge and pull will be sharing the same argh string, I think that we should leave it as is.

Thanks,

Denton

Quoted 46 lines

Best Wishes

Phillip

Quoted 41 lines
builtin/pull.c +4 -3
  	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
  		N_("merge strategy to use"),
  		0),
@@ -671,6 +671,10 @@ static int run_merge(void)
  	argv_array_pushv(&args, opt_strategy_opts.argv);
  	if (opt_gpg_sign)
  		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");
  	if (opt_allow_unrelated_histories > 0)
  		argv_array_push(&args, "--allow-unrelated-histories");
@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
  	if (get_oid("HEAD", &orig_head))
  		oidclr(&orig_head);
-	if (!opt_rebase && opt_autostash != -1)
-		die(_("--[no-]autostash option is only valid with --rebase."));
-
  	autostash = config_autostash;
  	if (opt_rebase) {
  		if (opt_autostash != -1)
t/t5520-pull.sh +0 -8
index cf4cc32fd0..75f162495a 100755
@@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
  	test_pull_autostash_fail --rebase --no-autostash
  '
-for i in --autostash --no-autostash
-do
-	test_expect_success "pull $i (without --rebase) is illegal" '
-		test_must_fail git pull $i . copy 2>err &&
-		test_i18ngrep "only valid with --rebase" err
-	'
-done
-
  test_expect_success 'pull.rebase' '
  	git reset --hard before-rebase &&
  	test_config pull.rebase true &&
 
author
#24 lore raw

Re: [RFC PATCH 7/7] merge: teach --autostash option

In reply to Johannes Schindelin

Hi Dscho,

Quoted 8 lines · Johannes Schindelin

Hi Denton,

Quoted 4 lines · Denton Liu

In rebase, one can pass the --autostash option to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.

Quoted 7 lines
Quoted 4 lines

Implement the --autostash option and corresponding merge.autoStash option in merge which stashes before merging and then pops after.

Maybe "Suggested-by" would be more accurate, it is not like this feature request was a bug report...

Makes sense.

Quoted 45 lines
Quoted 45 lines
---
 builtin/merge.c | 13 +++++++++++++
 builtin/pull.c  |  9 +++++----
 t/t5520-pull.sh |  8 --------
 3 files changed, 18 insertions(+), 12 deletions(-)
builtin/merge.c +6 -0
index 062e911441..d1a5eaad0d 100644
@@ -40,6 +40,7 @@
 #include "branch.h"
 #include "commit-reach.h"
 #include "wt-status.h"
+#include "autostash.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {
 	NULL
 };
 
+static GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
+
 static int show_diffstat = 1, shortlog_len = -1, squash;
 static int option_commit = -1;
 static int option_edit = -1;
@@ -81,6 +84,7 @@ static int show_progress = -1;
 static int default_to_upstream = 1;
 static int signoff;
 static const char *sign_commit;
+static int autostash;
 static int no_verify;
 
 static struct strategy all_strategy[] = {
@@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {
 	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
 	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
 	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+	OPT_BOOL(0, "autostash", &autostash,
+	      N_("automatically stash/stash pop before and after")),
 	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
 	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
Quoted 8 lines

builtin/merge.c static void finish(struct commit *head_commit, · line 449

Quoted 6 lines
builtin/merge.c +1 -0
 	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,
 		strbuf_addf(&reflog_message, "%s: %s",
 			getenv("GIT_REFLOG_ACTION"), msg);
 	}
+	apply_autostash(merge_autostash());

Should this not be guarded by if (autostash)?

The apply_autostash() checks if an autostash file exists and then applies it if it does. As a result, we should be unconditionally calling apply_autostash().

I guess that an odd edge-case that might come up is that a user may run into a merge conflict, resolve the conflict, create a .git/MERGE_AUTOSTASH file and then, after they commit, the autostash file will be applied. Currently, rebase behaves the same way so I think it's safe to leave the behaviour as is. I don't think this is a common use-case that will come up anyway ;)

Quoted 61 lines
Quoted 24 lines
builtin/merge.c +5 -0
 	if (squash) {
 		squash_message(head_commit, remoteheads);
 	} else {
@@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 	} else if (!strcmp(k, "commit.gpgsign")) {
 		sign_commit = git_config_bool(k, v) ? "" : NULL;
 		return 0;
+	} else if (!strcmp(k, "merge.autostash")) {
+		autostash = git_config_bool(k, v);
+		return 0;
 	}
 
 	status = fmt_merge_msg_config(k, v, cb);
@@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
 		for (j = common; j; j = j->next)
 			commit_list_insert(j->item, &reversed);
 
+		if (autostash)
+			perform_autostash(merge_autostash());
 		hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
 		clean = merge_recursive(&o, head,
 				remoteheads->item, reversed, &result);

builtin/merge.c int cmd_merge(int argc, const char **argv, const char *prefix) · line 1303

Quoted 4 lines
builtin/merge.c +1 -0
@@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
 		/* Invoke 'git reset --merge' */
 		ret = cmd_reset(nargc, nargv, prefix);
+		apply_autostash(merge_autostash());

Again, this should be guarded by if (autostash), methinks.

builtin/pull.c static struct option pull_options[] = { · line 186

Quoted 13 lines
builtin/merge.c +0 -0
 		goto done;
 	}
 
builtin/pull.c +1 -1
index d25ff13a60..ee186781ae 100644
@@ -183,7 +183,7 @@ static struct option pull_options[] = {
 		N_("verify that the named commit has a valid GPG signature"),
 		PARSE_OPT_NOARG),
 	OPT_BOOL(0, "autostash", &opt_autostash,
-		N_("automatically stash/stash pop before and after rebase")),
+		N_("automatically stash/stash pop before and after")),

Makes sense; this is now shared between the rebase and the merge modes.

builtin/pull.c static int run_merge(void) · line 677

Quoted 11 lines
builtin/pull.c +4 -0
 	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
 		N_("merge strategy to use"),
 		0),
@@ -671,6 +671,10 @@ static int run_merge(void)
 	argv_array_pushv(&args, opt_strategy_opts.argv);
 	if (opt_gpg_sign)
 		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");

Or shorter:

Quoted 6 lines
	argv_array_pushf(&args, "%s-autostash", opt_autostash ? "-" : "--no");

Ah, but that would mishandle -1. I bet I will be puzzled by this again. Maybe it would make sense to mention in a code comment that it can be -1 in which case we leave it to rebase to use the config settings to determine whether or not to autostash.

I copied this over from the rebase case. I'll add a comment there as well.

Quoted 22 lines
Quoted 22 lines
builtin/pull.c +0 -3
 	if (opt_allow_unrelated_histories > 0)
 		argv_array_push(&args, "--allow-unrelated-histories");
 
@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 	if (get_oid("HEAD", &orig_head))
 		oidclr(&orig_head);
 
-	if (!opt_rebase && opt_autostash != -1)
-		die(_("--[no-]autostash option is only valid with --rebase."));
-
 	autostash = config_autostash;
 	if (opt_rebase) {
 		if (opt_autostash != -1)
t/t5520-pull.sh +0 -0
index cf4cc32fd0..75f162495a 100755
@@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
 	test_pull_autostash_fail --rebase --no-autostash
 '
 
Quoted 11 lines

t/t5520-pull.sh test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' ' · line 375

Quoted 8 lines
t/t5520-pull.sh +0 -8
-for i in --autostash --no-autostash
-do
-	test_expect_success "pull $i (without --rebase) is illegal" '
-		test_must_fail git pull $i . copy 2>err &&
-		test_i18ngrep "only valid with --rebase" err
-	'
-done
-

Nice!
Dscho

Thanks,

Denton

Quoted 8 lines
Quoted 8 lines
t/t5520-pull.sh +0 -1
 test_expect_success 'pull.rebase' '
 	git reset --hard before-rebase &&
 	test_config pull.rebase true &&
--

2.23.0.897.g0a19638b1e

author
#25 lore raw

Re: [RFC PATCH 7/7] merge: teach --autostash option

Quoted 16 lines · Denton Liu
Quoted 16 lines

builtin/pull.c static int run_merge(void) · line 677

Quoted 11 lines
builtin/pull.c +4 -0
 	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
 		N_("merge strategy to use"),
 		0),
@@ -671,6 +671,10 @@ static int run_merge(void)
 	argv_array_pushv(&args, opt_strategy_opts.argv);
 	if (opt_gpg_sign)
 		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");

Or shorter:

	argv_array_pushf(&args, "%s-autostash", opt_autostash ? "-" : "--no");
Quoted 7 lines
Quoted 4 lines

Ah, but that would mishandle -1. I bet I will be puzzled by this again. Maybe it would make sense to mention in a code comment that it can be -1 in which case we leave it to rebase to use the config settings to determine whether or not to autostash.

I copied this over from the rebase case. I'll add a comment there as well.

Actually, on another thought, this happens in a couple of places in builtin/pull.c. I'm not sure that it's worth the noise of commenting it every place where this happens. I dunno. I'll send a re-roll out and if someone else brings it up again, I'll add the comments in.

author
#26 lore raw

v2 cover merge: learn --autostash

In reply to Denton Liu

Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.

The majority of this patchset is spent refactoring. This is so that the actual implementation in merge is done as simply as possible.

Changes since v1:

* Completely changed how the refactoring was done

* More tests and documentation

[1]: https://github.com/gitgitgadget/git/issues/394

Denton Liu (17):
Makefile: alphabetically sort += lists
t7600: use test_write_lines()
sequencer: use file strbuf for read_oneliner()
sequencer: configurably warn on non-existent files
sequencer: make read_oneliner() extern
rebase: use read_oneliner()
sequencer: make apply_rebase() accept a path
rebase: use apply_autostash() from sequencer.c
rebase: generify reset_head()
reset: extract reset_head() from rebase
rebase: extract create_autostash()
rebase: generify create_autostash()
sequencer: extract perform_autostash() from rebase
sequencer: unlink autostash in apply_autostash()
merge: teach --autostash option
t5520: make test_pull_autostash() accept expect_parent_num
pull: pass --autostash to merge

 Documentation/config/merge.txt  |  10 ++
 Documentation/git-pull.txt      |   9 -
 Documentation/merge-options.txt |   8 +
 Makefile                        |  76 ++++----
 branch.c                        |   1 +
 builtin/commit.c                |   2 +
 builtin/merge.c                 |  17 ++
 builtin/pull.c                  |   9 +-
 builtin/rebase.c                | 295 ++++----------------------------
 path.c                          |   1 +
 path.h                          |   4 +-
 reset.c                         | 140 +++++++++++++++
 reset.h                         |  20 +++
 sequencer.c                     | 128 +++++++++-----
 sequencer.h                     |  15 ++
 t/t3033-merge-toplevel.sh       |  22 +++
 t/t5520-pull.sh                 |  57 ++++--
 t/t7600-merge.sh                |  87 ++++++++--
 18 files changed, 522 insertions(+), 379 deletions(-)
 create mode 100644 reset.c
 create mode 100644 reset.h
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#27 lore raw

PATCH v2 1/17 Makefile: alphabetically sort += lists

There are many += lists in the Makefile and, over time, they have gotten slightly out of order, alphabetically. Alphabetically sort all += lists to bring them back in order.

Note that if we omit file prefixes, the lists aren't sorted in strictly alphabetical order (e.g. archive.o comes after archive-zip.o instead of before archive-tar.o). This is intentional because the purpose of maintaining the sorted list is to ensure line insertions are deterministic. As a result, we want to ensure that anybody can easily reproduce the sort order and that is very simple when we don't have to treat the prefix specially.

This patch is best viewed with --color-moved.

---
 Makefile | 75 ++++++++++++++++++++++++++++----------------------------
 1 file changed, 38 insertions(+), 37 deletions(-)
Makefile +38 -37
index 42a061d3fb..de149947f6 100644
@@ -604,12 +604,12 @@ unexport CDPATH
 SCRIPT_SH += git-bisect.sh
 SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
+SCRIPT_SH += git-legacy-stash.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-quiltimport.sh
-SCRIPT_SH += git-legacy-stash.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-web--browse.sh
@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
 SCRIPT_LIB += git-rebase--preserve-merges
-SCRIPT_LIB += git-sh-setup
 SCRIPT_LIB += git-sh-i18n
+SCRIPT_LIB += git-sh-setup
 
 SCRIPT_PERL += git-add--interactive.perl
 SCRIPT_PERL += git-archimport.perl
@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o
 PROGRAM_OBJS += fast-import.o
 PROGRAM_OBJS += http-backend.o
 PROGRAM_OBJS += imap-send.o
+PROGRAM_OBJS += remote-testsvn.o
 PROGRAM_OBJS += sh-i18n--envsubst.o
 PROGRAM_OBJS += shell.o
-PROGRAM_OBJS += remote-testsvn.o
 
 # Binary suffix, set to .exe for Windows builds
 X =
@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
 TEST_BUILTINS_OBJS += test-example-decorate.o
 TEST_BUILTINS_OBJS += test-genrandom.o
 TEST_BUILTINS_OBJS += test-genzeros.o
+TEST_BUILTINS_OBJS += test-hash-speed.o
 TEST_BUILTINS_OBJS += test-hash.o
 TEST_BUILTINS_OBJS += test-hashmap.o
-TEST_BUILTINS_OBJS += test-hash-speed.o
 TEST_BUILTINS_OBJS += test-index-version.o
 TEST_BUILTINS_OBJS += test-json-writer.o
 TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@ -736,8 +736,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o
 TEST_BUILTINS_OBJS += test-run-command.o
 TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
 TEST_BUILTINS_OBJS += test-serve-v2.o
-TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha1-array.o
+TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha256.o
 TEST_BUILTINS_OBJS += test-sigchain.o
 TEST_BUILTINS_OBJS += test-strcmp-offset.o
@@ -747,10 +747,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
 TEST_BUILTINS_OBJS += test-subprocess.o
 TEST_BUILTINS_OBJS += test-trace2.o
 TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
-TEST_BUILTINS_OBJS += test-xml-encode.o
 TEST_BUILTINS_OBJS += test-wildmatch.o
 TEST_BUILTINS_OBJS += test-windows-named-pipe.o
 TEST_BUILTINS_OBJS += test-write-cache.o
+TEST_BUILTINS_OBJS += test-xml-encode.o
 
 # Do not add more tests here unless they have extra dependencies. Add
 # them in TEST_BUILTINS_OBJS above.
@@ -787,10 +787,10 @@ OTHER_PROGRAMS = git$X
 
 # what test wrappers are needed and 'install' will install, in bindir
 BINDIR_PROGRAMS_NEED_X += git
-BINDIR_PROGRAMS_NEED_X += git-upload-pack
 BINDIR_PROGRAMS_NEED_X += git-receive-pack
-BINDIR_PROGRAMS_NEED_X += git-upload-archive
 BINDIR_PROGRAMS_NEED_X += git-shell
+BINDIR_PROGRAMS_NEED_X += git-upload-archive
+BINDIR_PROGRAMS_NEED_X += git-upload-pack
 
 BINDIR_PROGRAMS_NO_X += git-cvsserver
 
@@ -829,9 +829,9 @@ LIB_OBJS += advice.o
 LIB_OBJS += alias.o
 LIB_OBJS += alloc.o
 LIB_OBJS += apply.o
-LIB_OBJS += archive.o
 LIB_OBJS += archive-tar.o
 LIB_OBJS += archive-zip.o
+LIB_OBJS += archive.o
 LIB_OBJS += argv-array.o
 LIB_OBJS += attr.o
 LIB_OBJS += base85.o
@@ -847,9 +847,9 @@ LIB_OBJS += checkout.o
 LIB_OBJS += color.o
 LIB_OBJS += column.o
 LIB_OBJS += combine-diff.o
-LIB_OBJS += commit.o
 LIB_OBJS += commit-graph.o
 LIB_OBJS += commit-reach.o
+LIB_OBJS += commit.o
 LIB_OBJS += compat/obstack.o
 LIB_OBJS += compat/terminal.o
 LIB_OBJS += config.o
@@ -863,17 +863,17 @@ LIB_OBJS += ctype.o
 LIB_OBJS += date.o
 LIB_OBJS += decorate.o
 LIB_OBJS += delta-islands.o
+LIB_OBJS += diff-delta.o
+LIB_OBJS += diff-lib.o
+LIB_OBJS += diff-no-index.o
+LIB_OBJS += diff.o
 LIB_OBJS += diffcore-break.o
 LIB_OBJS += diffcore-delta.o
 LIB_OBJS += diffcore-order.o
 LIB_OBJS += diffcore-pickaxe.o
 LIB_OBJS += diffcore-rename.o
-LIB_OBJS += diff-delta.o
-LIB_OBJS += diff-lib.o
-LIB_OBJS += diff-no-index.o
-LIB_OBJS += diff.o
-LIB_OBJS += dir.o
 LIB_OBJS += dir-iterator.o
+LIB_OBJS += dir.o
 LIB_OBJS += editor.o
 LIB_OBJS += entry.o
 LIB_OBJS += environment.o
@@ -891,7 +891,6 @@ LIB_OBJS += gpg-interface.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hashmap.o
-LIB_OBJS += linear-assignment.o
 LIB_OBJS += help.o
 LIB_OBJS += hex.o
 LIB_OBJS += ident.o
@@ -901,9 +900,10 @@ LIB_OBJS += kwset.o
 LIB_OBJS += levenshtein.o
 LIB_OBJS += line-log.o
 LIB_OBJS += line-range.o
-LIB_OBJS += list-objects.o
-LIB_OBJS += list-objects-filter.o
+LIB_OBJS += linear-assignment.o
 LIB_OBJS += list-objects-filter-options.o
+LIB_OBJS += list-objects-filter.o
+LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
@@ -912,31 +912,31 @@ LIB_OBJS += mailinfo.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += mem-pool.o
-LIB_OBJS += merge.o
 LIB_OBJS += merge-blobs.o
 LIB_OBJS += merge-recursive.o
+LIB_OBJS += merge.o
 LIB_OBJS += mergesort.o
 LIB_OBJS += midx.o
 LIB_OBJS += name-hash.o
 LIB_OBJS += negotiator/default.o
 LIB_OBJS += negotiator/skipping.o
-LIB_OBJS += notes.o
 LIB_OBJS += notes-cache.o
 LIB_OBJS += notes-merge.o
 LIB_OBJS += notes-utils.o
+LIB_OBJS += notes.o
 LIB_OBJS += object.o
 LIB_OBJS += oidmap.o
 LIB_OBJS += oidset.o
-LIB_OBJS += packfile.o
-LIB_OBJS += pack-bitmap.o
 LIB_OBJS += pack-bitmap-write.o
+LIB_OBJS += pack-bitmap.o
 LIB_OBJS += pack-check.o
 LIB_OBJS += pack-objects.o
 LIB_OBJS += pack-revindex.o
 LIB_OBJS += pack-write.o
+LIB_OBJS += packfile.o
 LIB_OBJS += pager.o
-LIB_OBJS += parse-options.o
 LIB_OBJS += parse-options-cb.o
+LIB_OBJS += parse-options.o
 LIB_OBJS += patch-delta.o
 LIB_OBJS += patch-ids.o
 LIB_OBJS += path.o
@@ -954,6 +954,7 @@ LIB_OBJS += range-diff.o
 LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
 LIB_OBJS += rebase-interactive.o
+LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/files-backend.o
@@ -961,7 +962,6 @@ LIB_OBJS += refs/iterator.o
 LIB_OBJS += refs/packed-backend.o
 LIB_OBJS += refs/ref-cache.o
 LIB_OBJS += refspec.o
-LIB_OBJS += ref-filter.o
 LIB_OBJS += remote.o
 LIB_OBJS += replace-object.o
 LIB_OBJS += repo-settings.o
@@ -976,8 +976,8 @@ LIB_OBJS += serve.o
 LIB_OBJS += server-info.o
 LIB_OBJS += setup.o
 LIB_OBJS += sha1-array.o
-LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1-file.o
+LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1-name.o
 LIB_OBJS += shallow.o
 LIB_OBJS += sideband.o
@@ -987,9 +987,9 @@ LIB_OBJS += stable-qsort.o
 LIB_OBJS += strbuf.o
 LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
-LIB_OBJS += submodule.o
-LIB_OBJS += submodule-config.o
 LIB_OBJS += sub-process.o
+LIB_OBJS += submodule-config.o
+LIB_OBJS += submodule.o
 LIB_OBJS += symlinks.o
 LIB_OBJS += tag.o
 LIB_OBJS += tempfile.o
@@ -1008,11 +1008,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o
 LIB_OBJS += trace2/tr2_tgt_perf.o
 LIB_OBJS += trace2/tr2_tls.o
 LIB_OBJS += trailer.o
-LIB_OBJS += transport.o
 LIB_OBJS += transport-helper.o
+LIB_OBJS += transport.o
 LIB_OBJS += tree-diff.o
-LIB_OBJS += tree.o
 LIB_OBJS += tree-walk.o
+LIB_OBJS += tree.o
 LIB_OBJS += unpack-trees.o
 LIB_OBJS += upload-pack.o
 LIB_OBJS += url.o
@@ -1052,9 +1052,9 @@ BUILTIN_OBJS += builtin/checkout.o
 BUILTIN_OBJS += builtin/clean.o
 BUILTIN_OBJS += builtin/clone.o
 BUILTIN_OBJS += builtin/column.o
+BUILTIN_OBJS += builtin/commit-graph.o
 BUILTIN_OBJS += builtin/commit-tree.o
 BUILTIN_OBJS += builtin/commit.o
-BUILTIN_OBJS += builtin/commit-graph.o
 BUILTIN_OBJS += builtin/config.o
 BUILTIN_OBJS += builtin/count-objects.o
 BUILTIN_OBJS += builtin/credential.o
@@ -1085,13 +1085,13 @@ BUILTIN_OBJS += builtin/ls-remote.o
 BUILTIN_OBJS += builtin/ls-tree.o
 BUILTIN_OBJS += builtin/mailinfo.o
 BUILTIN_OBJS += builtin/mailsplit.o
-BUILTIN_OBJS += builtin/merge.o
 BUILTIN_OBJS += builtin/merge-base.o
 BUILTIN_OBJS += builtin/merge-file.o
 BUILTIN_OBJS += builtin/merge-index.o
 BUILTIN_OBJS += builtin/merge-ours.o
 BUILTIN_OBJS += builtin/merge-recursive.o
 BUILTIN_OBJS += builtin/merge-tree.o
+BUILTIN_OBJS += builtin/merge.o
 BUILTIN_OBJS += builtin/mktag.o
 BUILTIN_OBJS += builtin/mktree.o
 BUILTIN_OBJS += builtin/multi-pack-index.o
@@ -1111,9 +1111,9 @@ BUILTIN_OBJS += builtin/read-tree.o
 BUILTIN_OBJS += builtin/rebase.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
-BUILTIN_OBJS += builtin/remote.o
 BUILTIN_OBJS += builtin/remote-ext.o
 BUILTIN_OBJS += builtin/remote-fd.o
+BUILTIN_OBJS += builtin/remote.o
 BUILTIN_OBJS += builtin/repack.o
 BUILTIN_OBJS += builtin/replace.o
 BUILTIN_OBJS += builtin/rerere.o
@@ -2328,16 +2328,16 @@ reconfigure config.mak.autogen: config.status
 endif
 
 XDIFF_OBJS += xdiff/xdiffi.o
-XDIFF_OBJS += xdiff/xprepare.o
-XDIFF_OBJS += xdiff/xutils.o
 XDIFF_OBJS += xdiff/xemit.o
+XDIFF_OBJS += xdiff/xhistogram.o
 XDIFF_OBJS += xdiff/xmerge.o
 XDIFF_OBJS += xdiff/xpatience.o
-XDIFF_OBJS += xdiff/xhistogram.o
+XDIFF_OBJS += xdiff/xprepare.o
+XDIFF_OBJS += xdiff/xutils.o
 
+VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/line_buffer.o
 VCSSVN_OBJS += vcs-svn/sliding_window.o
-VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/svndiff.o
 VCSSVN_OBJS += vcs-svn/svndump.o
 
@@ -3146,9 +3146,10 @@ endif
 #
 ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS)
 ALL_COMMANDS += git
+ALL_COMMANDS += git-citool
+ALL_COMMANDS += git-gui
 ALL_COMMANDS += gitk
 ALL_COMMANDS += gitweb
-ALL_COMMANDS += git-gui git-citool
 
 .PHONY: check-docs
 check-docs::
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#29 lore raw

PATCH v2 2/17 t7600: use test_write_lines()

In reply to Denton Liu

In t7600, we were rewriting printf '%s\n' ... to create files from parameters, one per line. However, we already have a function that wraps this for us: test_write_lines(). Rewrite these instances to use that function instead of open coding it.

---
 t/t7600-merge.sh | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
t/t7600-merge.sh +11 -11
index 132608879a..4fa0ef8e3b 100755
@@ -29,15 +29,15 @@ Testing basic merge operations/option parsing.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
-printf '%s\n' 1 2 3 4 5 6 7 8 9 >file
-printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >file.1
-printf '%s\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5
-printf '%s\n' 1 2 3 4 5 6 7 8 '9 X' >file.9
-printf '%s\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y
-printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >result.1
-printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
-printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
-printf '%s\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z
+test_write_lines 1 2 3 4 5 6 7 8 9 >file
+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1
+test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5
+test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9
+test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y
+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1
+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
+test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z
 
 create_merge_msgs () {
 	echo "Merge tag 'c2'" >msg.1-5 &&
@@ -81,7 +81,7 @@ verify_head () {
 }
 
 verify_parents () {
-	printf '%s\n' "$@" >parents.expected &&
+	test_write_lines "$@" >parents.expected &&
 	>parents.actual &&
 	i=1 &&
 	while test $i -le $#
@@ -95,7 +95,7 @@ verify_parents () {
 }
 
 verify_mergeheads () {
-	printf '%s\n' "$@" >mergehead.expected &&
+	test_write_lines "$@" >mergehead.expected &&
 	while read sha1 rest
 	do
 		git rev-parse $sha1
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#30 lore raw

PATCH v2 3/17 sequencer: use file strbuf for read_oneliner()

In reply to Denton Liu

In the original read_oneliner logic, we duplicated the logic for strbuf_trim_trailing_newline() with one exception: instead of preventing buffer accesses below index 0, it would prevent buffer accesses below index orig_len. Although this is correct, it isn't worth having the duplicated logic around.

Add a second strbuf to which files are read and run strbuf_trim_trailing_newline() directly on this strbuf then concatenate this strbuf with the argument strbuf at the end of the function. The function's external behaviour is unchanged.

---
 sequencer.c | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)
sequencer.c +13 -11
index 763ccbbc45..5ba8b4383f 100644
@@ -429,26 +429,28 @@ static int write_message(const void *buf, size_t len, const char *filename,
 static int read_oneliner(struct strbuf *buf,
 	const char *path, int skip_if_empty)
 {
-	int orig_len = buf->len;
+	int ret = 0;
+	struct strbuf file_buf = STRBUF_INIT;
 
 	if (!file_exists(path))
 		return 0;
 
-	if (strbuf_read_file(buf, path, 0) < 0) {
+	if (strbuf_read_file(&file_buf, path, 0) < 0) {
 		warning_errno(_("could not read '%s'"), path);
-		return 0;
+		goto done;
 	}
 
-	if (buf->len > orig_len && buf->buf[buf->len - 1] == '\n') {
-		if (--buf->len > orig_len && buf->buf[buf->len - 1] == '\r')
-			--buf->len;
-		buf->buf[buf->len] = '\0';
-	}
+	strbuf_trim_trailing_newline(&file_buf);
 
-	if (skip_if_empty && buf->len == orig_len)
-		return 0;
+	if (skip_if_empty && !file_buf.len)
+		goto done;
 
-	return 1;
+	strbuf_addbuf(buf, &file_buf);
+	ret = 1;
+
+done:
+	strbuf_release(&file_buf);
+	return ret;
 }
 
 static struct tree *empty_tree(struct repository *r)
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#31 lore raw

PATCH v2 4/17 sequencer: configurably warn on non-existent files

In reply to Denton Liu

In the future, we plan on externing read_oneliner(). Future users of read_oneliner() will want the ability to output warnings in the event that the path doesn't exist. Introduce the warn_nonexistence parameter which, if true, would issue a warning when a file doesn't exist by skipping the !file_exists() check and letting strbuf_read_file() handle that case.

Mechanically replace uses of read_oneliner() in sequencer.c with the following spatch so that current behaviour is maintained:

	@@
	expression a, b, c;
	@@
	  read_oneliner(a, b, c
	+ , 0
	  )
---
 sequencer.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)
sequencer.c +16 -16
index 5ba8b4383f..103cea1460 100644
@@ -426,13 +426,13 @@ static int write_message(const void *buf, size_t len, const char *filename,
  *
  * Returns 1 if the file was read, 0 if it could not be read or does not exist.
  */
-static int read_oneliner(struct strbuf *buf,
-	const char *path, int skip_if_empty)
+static int read_oneliner(struct strbuf *buf, const char *path,
+			 int skip_if_empty, int warn_nonexistence)
 {
 	int ret = 0;
 	struct strbuf file_buf = STRBUF_INIT;
 
-	if (!file_exists(path))
+	if (!warn_nonexistence && !file_exists(path))
 		return 0;
 
 	if (strbuf_read_file(&file_buf, path, 0) < 0) {
@@ -2558,10 +2558,10 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
 static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
 {
 	strbuf_reset(buf);
-	if (!read_oneliner(buf, rebase_path_strategy(), 0))
+	if (!read_oneliner(buf, rebase_path_strategy(), 0, 0))
 		return;
 	opts->strategy = strbuf_detach(buf, NULL);
-	if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
+	if (!read_oneliner(buf, rebase_path_strategy_opts(), 0, 0))
 		return;
 
 	parse_strategy_opts(opts, buf->buf);
@@ -2572,7 +2572,7 @@ static int read_populate_opts(struct replay_opts *opts)
 	if (is_rebase_i(opts)) {
 		struct strbuf buf = STRBUF_INIT;
 
-		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
+		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1, 0)) {
 			if (!starts_with(buf.buf, "-S"))
 				strbuf_reset(&buf);
 			else {
@@ -2582,7 +2582,7 @@ static int read_populate_opts(struct replay_opts *opts)
 			strbuf_reset(&buf);
 		}
 
-		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
+		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1, 0)) {
 			if (!strcmp(buf.buf, "--rerere-autoupdate"))
 				opts->allow_rerere_auto = RERERE_AUTOUPDATE;
 			else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
@@ -2618,7 +2618,7 @@ static int read_populate_opts(struct replay_opts *opts)
 		strbuf_release(&buf);
 
 		if (read_oneliner(&opts->current_fixups,
-				  rebase_path_current_fixups(), 1)) {
+				  rebase_path_current_fixups(), 1, 0)) {
 			const char *p = opts->current_fixups.buf;
 			opts->current_fixup_count = 1;
 			while ((p = strchr(p, '\n'))) {
@@ -2627,7 +2627,7 @@ static int read_populate_opts(struct replay_opts *opts)
 			}
 		}
 
-		if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {
+		if (read_oneliner(&buf, rebase_path_squash_onto(), 0, 0)) {
 			if (get_oid_hex(buf.buf, &opts->squash_onto) < 0)
 				return error(_("unusable squash-onto"));
 			opts->have_squash_onto = 1;
@@ -3759,7 +3759,7 @@ static int apply_autostash(struct replay_opts *opts)
 	struct child_process child = CHILD_PROCESS_INIT;
 	int ret = 0;
 
-	if (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {
+	if (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1, 0)) {
 		strbuf_release(&stash_sha1);
 		return 0;
 	}
@@ -4093,7 +4093,7 @@ static int pick_commits(struct repository *r,
 		if (todo_list->current < todo_list->nr)
 			return 0;
 
-		if (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&
+		if (read_oneliner(&head_ref, rebase_path_head_name(), 0, 0) &&
 				starts_with(head_ref.buf, "refs/")) {
 			const char *msg;
 			struct object_id head, orig;
@@ -4106,13 +4106,13 @@ static int pick_commits(struct repository *r,
 				strbuf_release(&buf);
 				return res;
 			}
-			if (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||
+			if (!read_oneliner(&buf, rebase_path_orig_head(), 0, 0) ||
 					get_oid_hex(buf.buf, &orig)) {
 				res = error(_("could not read orig-head"));
 				goto cleanup_head_ref;
 			}
 			strbuf_reset(&buf);
-			if (!read_oneliner(&buf, rebase_path_onto(), 0)) {
+			if (!read_oneliner(&buf, rebase_path_onto(), 0, 0)) {
 				res = error(_("could not read 'onto'"));
 				goto cleanup_head_ref;
 			}
@@ -4145,7 +4145,7 @@ static int pick_commits(struct repository *r,
 				DIFF_FORMAT_DIFFSTAT;
 			log_tree_opt.disable_stdin = 1;
 
-			if (read_oneliner(&buf, rebase_path_orig_head(), 0) &&
+			if (read_oneliner(&buf, rebase_path_orig_head(), 0, 0) &&
 			    !get_oid(buf.buf, &orig) &&
 			    !get_oid("HEAD", &head)) {
 				diff_tree_oid(&orig, &head, "",
@@ -4230,7 +4230,7 @@ static int commit_staged_changes(struct repository *r,
 
 		if (get_oid("HEAD", &head))
 			return error(_("cannot amend non-existing commit"));
-		if (!read_oneliner(&rev, rebase_path_amend(), 0))
+		if (!read_oneliner(&rev, rebase_path_amend(), 0, 0))
 			return error(_("invalid file: '%s'"), rebase_path_amend());
 		if (get_oid_hex(rev.buf, &to_amend))
 			return error(_("invalid contents: '%s'"),
@@ -4391,7 +4391,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
 		struct strbuf buf = STRBUF_INIT;
 		struct object_id oid;
 
-		if (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&
+		if (read_oneliner(&buf, rebase_path_stopped_sha(), 1, 0) &&
 		    !get_oid_committish(buf.buf, &oid))
 			record_in_rewritten(&oid, peek_command(&todo_list, 0));
 		strbuf_release(&buf);
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#34 lore raw

PATCH v2 6/17 rebase: use read_oneliner()

In reply to Denton Liu

Since in sequencer.c, read_one() basically duplicates the functionality of read_oneliner(), reduce code duplication by replacing read_one() with read_oneliner().

This was done with the following Coccinelle script:

	@@
	expression a, b;
	@@
	- read_one(a, b)
	+ !read_oneliner(b, a, 0, 1)
---
 builtin/rebase.c | 29 +++++++++--------------------
 1 file changed, 9 insertions(+), 20 deletions(-)
builtin/rebase.c +9 -20
index ddf33bc9d4..68bedccb95 100644
@@ -582,15 +582,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o
 	return path.buf;
 }
 
-/* Read one file, then strip line endings */
-static int read_one(const char *path, struct strbuf *buf)
-{
-	if (strbuf_read_file(buf, path, 0) < 0)
-		return error_errno(_("could not read '%s'"), path);
-	strbuf_trim_trailing_newline(buf);
-	return 0;
-}
-
 /* Initialize the rebase options from the state directory. */
 static int read_basic_state(struct rebase_options *opts)
 {
@@ -598,8 +589,8 @@ static int read_basic_state(struct rebase_options *opts)
 	struct strbuf buf = STRBUF_INIT;
 	struct object_id oid;
 
-	if (read_one(state_dir_path("head-name", opts), &head_name) ||
-	    read_one(state_dir_path("onto", opts), &buf))
+	if (!read_oneliner(&head_name, state_dir_path("head-name", opts), 0, 1) ||
+	    !read_oneliner(&buf, state_dir_path("onto", opts), 0, 1))
 		return -1;
 	opts->head_name = starts_with(head_name.buf, "refs/") ?
 		xstrdup(head_name.buf) : NULL;
@@ -615,9 +606,9 @@ static int read_basic_state(struct rebase_options *opts)
 	 */
 	strbuf_reset(&buf);
 	if (file_exists(state_dir_path("orig-head", opts))) {
-		if (read_one(state_dir_path("orig-head", opts), &buf))
+		if (!read_oneliner(&buf, state_dir_path("orig-head", opts), 0, 1))
 			return -1;
-	} else if (read_one(state_dir_path("head", opts), &buf))
+	} else if (!read_oneliner(&buf, state_dir_path("head", opts), 0, 1))
 		return -1;
 	if (get_oid(buf.buf, &opts->orig_head))
 		return error(_("invalid orig-head: '%s'"), buf.buf);
@@ -637,8 +628,7 @@ static int read_basic_state(struct rebase_options *opts)
 
 	if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
 		strbuf_reset(&buf);
-		if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
-			    &buf))
+		if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts), 0, 1))
 			return -1;
 		if (!strcmp(buf.buf, "--rerere-autoupdate"))
 			opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
@@ -651,8 +641,7 @@ static int read_basic_state(struct rebase_options *opts)
 
 	if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
 		strbuf_reset(&buf);
-		if (read_one(state_dir_path("gpg_sign_opt", opts),
-			    &buf))
+		if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts), 0, 1))
 			return -1;
 		free(opts->gpg_sign_opt);
 		opts->gpg_sign_opt = xstrdup(buf.buf);
@@ -660,7 +649,7 @@ static int read_basic_state(struct rebase_options *opts)
 
 	if (file_exists(state_dir_path("strategy", opts))) {
 		strbuf_reset(&buf);
-		if (read_one(state_dir_path("strategy", opts), &buf))
+		if (!read_oneliner(&buf, state_dir_path("strategy", opts), 0, 1))
 			return -1;
 		free(opts->strategy);
 		opts->strategy = xstrdup(buf.buf);
@@ -668,7 +657,7 @@ static int read_basic_state(struct rebase_options *opts)
 
 	if (file_exists(state_dir_path("strategy_opts", opts))) {
 		strbuf_reset(&buf);
-		if (read_one(state_dir_path("strategy_opts", opts), &buf))
+		if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts), 0, 1))
 			return -1;
 		free(opts->strategy_opts);
 		opts->strategy_opts = xstrdup(buf.buf);
@@ -720,7 +709,7 @@ static int apply_autostash(struct rebase_options *opts)
 	if (!file_exists(path))
 		return 0;
 
-	if (read_one(path, &autostash))
+	if (!read_oneliner(&autostash, path, 0, 1))
 		return error(_("Could not read '%s'"), path);
 	/* Ensure that the hash is not mistaken for a number */
 	strbuf_addstr(&autostash, "^0");
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#35 lore raw

PATCH v2 7/17 sequencer: make apply_rebase() accept a path

In reply to Denton Liu

In order to make apply_rebase() more generic for future extraction, make it accept a path argument so that the location from where to read the reference to the autostash commit can be customized. Remove the opts argument since it was unused before anyway.

---
 sequencer.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
sequencer.c +7 -7
index 9ef9424f88..e1b7cb461c 100644
@@ -3744,13 +3744,13 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
 	return -1;
 }
 
-static int apply_autostash(struct replay_opts *opts)
+static int apply_autostash(const char *path)
 {
 	struct strbuf stash_sha1 = STRBUF_INIT;
 	struct child_process child = CHILD_PROCESS_INIT;
 	int ret = 0;
 
-	if (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1, 0)) {
+	if (!read_oneliner(&stash_sha1, path, 1, 0)) {
 		strbuf_release(&stash_sha1);
 		return 0;
 	}
@@ -3856,7 +3856,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,
 		return error(_("%s: not a valid OID"), orig_head);
 
 	if (run_git_checkout(r, opts, oid_to_hex(onto), action)) {
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 		sequencer_remove_state(opts);
 		return error(_("could not detach HEAD"));
 	}
@@ -4172,7 +4172,7 @@ static int pick_commits(struct repository *r,
 				run_command(&hook);
 			}
 		}
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 
 		if (!opts->quiet) {
 			if (!opts->verbose)
@@ -5213,7 +5213,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
 		todo_list_add_exec_commands(todo_list, commands);
 
 	if (count_commands(todo_list) == 0) {
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 		sequencer_remove_state(opts);
 
 		return error(_("nothing to do"));
@@ -5224,12 +5224,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
 	if (res == -1)
 		return -1;
 	else if (res == -2) {
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 		sequencer_remove_state(opts);
 
 		return -1;
 	} else if (res == -3) {
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 		sequencer_remove_state(opts);
 		todo_list_release(&new_todo);
 
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#36 lore raw

PATCH v2 8/17 rebase: use apply_autostash() from sequencer.c

In reply to Denton Liu

The apply_autostash() function in builtin/rebase.c is similar enough to the apply_autostash() function in sequencer.c that they are almost interchangeable. Make the sequencer.c version extern and use it in rebase.

The rebase version was introduced in 6defce2b02 (builtin rebase: support --autostash option, 2018-09-04) as part of the shell to C conversion. It opted to duplicate the function because, at the time, there was another in-progress project converting interactive rebase from shell to C as well and they did not want to clash with them by refactoring sequencer.c version of apply_autostash(). Since both efforts are long done, we can freely combine them together now.

---
 builtin/rebase.c | 49 ++----------------------------------------------
 sequencer.c      |  2 +-
 sequencer.h      |  2 ++
 3 files changed, 5 insertions(+), 48 deletions(-)
builtin/rebase.c +2 -47
index 68bedccb95..07674a16d8 100644
@@ -700,51 +700,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)
 	return 0;
 }
 
-static int apply_autostash(struct rebase_options *opts)
-{
-	const char *path = state_dir_path("autostash", opts);
-	struct strbuf autostash = STRBUF_INIT;
-	struct child_process stash_apply = CHILD_PROCESS_INIT;
-
-	if (!file_exists(path))
-		return 0;
-
-	if (!read_oneliner(&autostash, path, 0, 1))
-		return error(_("Could not read '%s'"), path);
-	/* Ensure that the hash is not mistaken for a number */
-	strbuf_addstr(&autostash, "^0");
-	argv_array_pushl(&stash_apply.args,
-			 "stash", "apply", autostash.buf, NULL);
-	stash_apply.git_cmd = 1;
-	stash_apply.no_stderr = stash_apply.no_stdout =
-		stash_apply.no_stdin = 1;
-	if (!run_command(&stash_apply))
-		printf(_("Applied autostash.\n"));
-	else {
-		struct argv_array args = ARGV_ARRAY_INIT;
-		int res = 0;
-
-		argv_array_pushl(&args,
-				 "stash", "store", "-m", "autostash", "-q",
-				 autostash.buf, NULL);
-		if (run_command_v_opt(args.argv, RUN_GIT_CMD))
-			res = error(_("Cannot store %s"), autostash.buf);
-		argv_array_clear(&args);
-		strbuf_release(&autostash);
-		if (res)
-			return res;
-
-		fprintf(stderr,
-			_("Applying autostash resulted in conflicts.\n"
-			  "Your changes are safe in the stash.\n"
-			  "You can run \"git stash pop\" or \"git stash drop\" "
-			  "at any time.\n"));
-	}
-
-	strbuf_release(&autostash);
-	return 0;
-}
-
 static int finish_rebase(struct rebase_options *opts)
 {
 	struct strbuf dir = STRBUF_INIT;
@@ -752,7 +707,7 @@ static int finish_rebase(struct rebase_options *opts)
 	int ret = 0;
 
 	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
-	apply_autostash(opts);
+	apply_autostash(state_dir_path("autostash", opts));
 	close_object_store(the_repository->objects);
 	/*
 	 * We ignore errors in 'gc --auto', since the
@@ -1206,7 +1161,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
 	} else if (status == 2) {
 		struct strbuf dir = STRBUF_INIT;
 
-		apply_autostash(opts);
+		apply_autostash(state_dir_path("autostash", opts));
 		strbuf_addstr(&dir, opts->state_dir);
 		remove_dir_recursively(&dir, 0);
 		strbuf_release(&dir);
sequencer.c +1 -1
index e1b7cb461c..98e08dbbe0 100644
@@ -3744,7 +3744,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
 	return -1;
 }
 
-static int apply_autostash(const char *path)
+int apply_autostash(const char *path)
 {
 	struct strbuf stash_sha1 = STRBUF_INIT;
 	struct child_process child = CHILD_PROCESS_INIT;
sequencer.h +2 -0
index 56cea80dd8..ecef2c144c 100644
@@ -195,6 +195,8 @@ void commit_post_rewrite(struct repository *r,
 int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
 				 const char *commit);
 
+int apply_autostash(const char *path);
+
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
 void print_commit_summary(struct repository *repo,
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#33 lore raw

PATCH v2 5/17 sequencer: make read_oneliner() extern

In reply to Denton Liu

The function read_oneliner() is a generally useful util function. Instead of hiding it as a static function within sequencer.c, extern it so that it can be reused by others.

---
 sequencer.c | 13 ++-----------
 sequencer.h | 11 +++++++++++
 2 files changed, 13 insertions(+), 11 deletions(-)
sequencer.c +2 -11
index 103cea1460..9ef9424f88 100644
@@ -417,17 +417,8 @@ static int write_message(const void *buf, size_t len, const char *filename,
 	return 0;
 }
 
-/*
- * Reads a file that was presumably written by a shell script, i.e. with an
- * end-of-line marker that needs to be stripped.
- *
- * Note that only the last end-of-line marker is stripped, consistent with the
- * behavior of "$(cat path)" in a shell script.
- *
- * Returns 1 if the file was read, 0 if it could not be read or does not exist.
- */
-static int read_oneliner(struct strbuf *buf, const char *path,
-			 int skip_if_empty, int warn_nonexistence)
+int read_oneliner(struct strbuf *buf, const char *path,
+		  int skip_if_empty, int warn_nonexistence)
 {
 	int ret = 0;
 	struct strbuf file_buf = STRBUF_INIT;
sequencer.h +11 -0
index e9a0e03ea2..56cea80dd8 100644
@@ -202,6 +202,17 @@ void print_commit_summary(struct repository *repo,
 			  const struct object_id *oid,
 			  unsigned int flags);
 
+/*
+ * Reads a file that was presumably written by a shell script, i.e. with an
+ * end-of-line marker that needs to be stripped.
+ *
+ * Note that only the last end-of-line marker is stripped, consistent with the
+ * behavior of "$(cat path)" in a shell script.
+ *
+ * Returns 1 if the file was read, 0 if it could not be read.
+ */
+int read_oneliner(struct strbuf *buf, const char *path,
+		  int skip_if_empty, int warn_nonexistence);
 int read_author_script(const char *path, char **name, char **email, char **date,
 		       int allow_missing);
 void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#38 lore raw

PATCH v2 10/17 reset: extract reset_head() from rebase

In reply to Denton Liu

Continue the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved.

---
 Makefile         |   1 +
 builtin/rebase.c | 139 +---------------------------------------------
 reset.c          | 140 +++++++++++++++++++++++++++++++++++++++++++++++
 reset.h          |  20 +++++++
 4 files changed, 162 insertions(+), 138 deletions(-)
 create mode 100644 reset.c
 create mode 100644 reset.h
Makefile +1 -0
index de149947f6..8d3aa4153e 100644
@@ -967,6 +967,7 @@ LIB_OBJS += replace-object.o
 LIB_OBJS += repo-settings.o
 LIB_OBJS += repository.o
 LIB_OBJS += rerere.o
+LIB_OBJS += reset.o
 LIB_OBJS += resolve-undo.o
 LIB_OBJS += revision.o
 LIB_OBJS += run-command.o
builtin/rebase.c +1 -138

151 lines not on this page · raw message

reset.c new file +140 -0

140 lines not on this page · raw message

reset.h new file +20 -0
new file mode 100644
index 0000000000..12f83c78e2
@@ -0,0 +1,20 @@
+#ifndef RESET_H
+#define RESET_H
+
+#include "hash.h"
+#include "repository.h"
+
+#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
+
+#define RESET_HEAD_DETACH (1<<0)
+#define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
+#define RESET_HEAD_REFS_ONLY (1<<3)
+#define RESET_ORIG_HEAD (1<<4)
+
+int reset_head(struct repository *r, struct object_id *oid, const char *action,
+	       const char *switch_to_branch, unsigned flags,
+	       const char *reflog_orig_head, const char *reflog_head,
+	       const char *default_reflog_action);
+
+#endif
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#40 lore raw

PATCH v2 12/17 rebase: generify create_autostash()

In reply to Denton Liu

In the future, we plan on lib-ifying create_autostash() so we need it to be more generic. Make it more generic by making it accept a struct repository argument instead of implicitly using the non-repo functions and the_repository. Also, make it accept a path argument so that we no longer rely have to rely on struct rebase_options. Finally, make it accept a default_reflog_action argument so we no longer have to rely on DEFAULT_REFLOG_ACTION.

---
 builtin/rebase.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)
builtin/rebase.c +16 -16
index b60be5c875..fea4ae754d 100644
@@ -1232,22 +1232,21 @@ static int check_exec_cmd(const char *cmd)
 	return 0;
 }
 
-static void create_autostash(struct rebase_options *options)
+static void create_autostash(struct repository *r, const char *path,
+			     const char *default_reflog_action)
 {
 	struct strbuf buf = STRBUF_INIT;
 	struct lock_file lock_file = LOCK_INIT;
 	int fd;
 
-	fd = hold_locked_index(&lock_file, 0);
-	refresh_cache(REFRESH_QUIET);
+	fd = repo_hold_locked_index(r, &lock_file, 0);
+	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
 	if (0 <= fd)
-		repo_update_index_if_able(the_repository, &lock_file);
+		repo_update_index_if_able(r, &lock_file);
 	rollback_lock_file(&lock_file);
 
-	if (has_unstaged_changes(the_repository, 1) ||
-	    has_uncommitted_changes(the_repository, 1)) {
-		const char *autostash =
-			state_dir_path("autostash", options);
+	if (has_unstaged_changes(r, 1) ||
+	    has_uncommitted_changes(r, 1)) {
 		struct child_process stash = CHILD_PROCESS_INIT;
 		struct object_id oid;
 
@@ -1265,18 +1264,18 @@ static void create_autostash(struct rebase_options *options)
 		strbuf_reset(&buf);
 		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
 
-		if (safe_create_leading_directories_const(autostash))
+		if (safe_create_leading_directories_const(path))
 			die(_("Could not create directory for '%s'"),
-			    options->state_dir);
-		write_file(autostash, "%s", oid_to_hex(&oid));
+			    path);
+		write_file(path, "%s", oid_to_hex(&oid));
 		printf(_("Created autostash: %s\n"), buf.buf);
-		if (reset_head(the_repository, NULL, "reset --hard",
+		if (reset_head(r, NULL, "reset --hard",
 			       NULL, RESET_HEAD_HARD, NULL, NULL,
-			       DEFAULT_REFLOG_ACTION) < 0)
+			       default_reflog_action) < 0)
 			die(_("could not reset --hard"));
 
-		if (discard_index(the_repository->index) < 0 ||
-			repo_read_index(the_repository) < 0)
+		if (discard_index(r->index) < 0 ||
+			repo_read_index(r) < 0)
 			die(_("could not read index"));
 	}
 	strbuf_release(&buf);
@@ -1881,7 +1880,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		die(_("could not read index"));
 
 	if (options.autostash) {
-		create_autostash(&options);
+		create_autostash(the_repository, state_dir_path("autostash", &options),
+				 DEFAULT_REFLOG_ACTION);
 	}
 
 	if (require_clean_work_tree(the_repository, "rebase",
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#41 lore raw

PATCH v2 13/17 sequencer: extract perform_autostash() from rebase

In reply to Denton Liu

Lib-ify the autostash code by extracting perform_autostash() from rebase into sequencer. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved.

---
 builtin/rebase.c | 49 -----------------------------------------------
 sequencer.c      | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |  2 ++
 3 files changed, 52 insertions(+), 49 deletions(-)
builtin/rebase.c +0 -49
index fea4ae754d..666d3f0181 100644
@@ -1232,55 +1232,6 @@ static int check_exec_cmd(const char *cmd)
 	return 0;
 }
 
-static void create_autostash(struct repository *r, const char *path,
-			     const char *default_reflog_action)
-{
-	struct strbuf buf = STRBUF_INIT;
-	struct lock_file lock_file = LOCK_INIT;
-	int fd;
-
-	fd = repo_hold_locked_index(r, &lock_file, 0);
-	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
-	if (0 <= fd)
-		repo_update_index_if_able(r, &lock_file);
-	rollback_lock_file(&lock_file);
-
-	if (has_unstaged_changes(r, 1) ||
-	    has_uncommitted_changes(r, 1)) {
-		struct child_process stash = CHILD_PROCESS_INIT;
-		struct object_id oid;
-
-		argv_array_pushl(&stash.args,
-				 "stash", "create", "autostash", NULL);
-		stash.git_cmd = 1;
-		stash.no_stdin = 1;
-		strbuf_reset(&buf);
-		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
-			die(_("Cannot autostash"));
-		strbuf_trim_trailing_newline(&buf);
-		if (get_oid(buf.buf, &oid))
-			die(_("Unexpected stash response: '%s'"),
-			    buf.buf);
-		strbuf_reset(&buf);
-		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
-
-		if (safe_create_leading_directories_const(path))
-			die(_("Could not create directory for '%s'"),
-			    path);
-		write_file(path, "%s", oid_to_hex(&oid));
-		printf(_("Created autostash: %s\n"), buf.buf);
-		if (reset_head(r, NULL, "reset --hard",
-			       NULL, RESET_HEAD_HARD, NULL, NULL,
-			       default_reflog_action) < 0)
-			die(_("could not reset --hard"));
-
-		if (discard_index(r->index) < 0 ||
-			repo_read_index(r) < 0)
-			die(_("could not read index"));
-	}
-	strbuf_release(&buf);
-}
-
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
 	struct rebase_options options = REBASE_OPTIONS_INIT;
sequencer.c +50 -0

62 lines not on this page · raw message

sequencer.h +2 -0
index ecef2c144c..8beb1472f7 100644
@@ -195,6 +195,8 @@ void commit_post_rewrite(struct repository *r,
 int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
 				 const char *commit);
 
+void create_autostash(struct repository *r, const char *path,
+		      const char *default_reflog_action);
 int apply_autostash(const char *path);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#37 lore raw

PATCH v2 9/17 rebase: generify reset_head()

In reply to Denton Liu

In the future, we plan on lib-ifying reset_head() so we need it to be more generic. Make it more generic by making it accept a struct repository argument instead of implicitly using the non-repo functions. Also, make it accept a const char *default_reflog_action argument so that the default action of "rebase" isn't hardcoded in.

---
 builtin/rebase.c | 63 +++++++++++++++++++++++++++---------------------
 1 file changed, 36 insertions(+), 27 deletions(-)
builtin/rebase.c +36 -27
index 07674a16d8..4e3cb0852d 100644
@@ -28,6 +28,8 @@
 #include "sequencer.h"
 #include "rebase-interactive.h"
 
+#define DEFAULT_REFLOG_ACTION "rebase"
+
 static char const * const builtin_rebase_usage[] = {
 	N_("git rebase [-i] [options] [--exec <cmd>] "
 		"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
@@ -760,9 +762,10 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)
 #define RESET_HEAD_REFS_ONLY (1<<3)
 #define RESET_ORIG_HEAD (1<<4)
 
-static int reset_head(struct object_id *oid, const char *action,
+static int reset_head(struct repository *r, struct object_id *oid, const char *action,
 		      const char *switch_to_branch, unsigned flags,
-		      const char *reflog_orig_head, const char *reflog_head)
+		      const char *reflog_orig_head, const char *reflog_head,
+		      const char *default_reflog_action)
 {
 	unsigned detach_head = flags & RESET_HEAD_DETACH;
 	unsigned reset_hard = flags & RESET_HEAD_HARD;
@@ -784,7 +787,7 @@ static int reset_head(struct object_id *oid, const char *action,
 	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
 		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
 
-	if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
+	if (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {
 		ret = -1;
 		goto leave_reset_head;
 	}
@@ -803,26 +806,26 @@ static int reset_head(struct object_id *oid, const char *action,
 	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
 	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
 	unpack_tree_opts.head_idx = 1;
-	unpack_tree_opts.src_index = the_repository->index;
-	unpack_tree_opts.dst_index = the_repository->index;
+	unpack_tree_opts.src_index = r->index;
+	unpack_tree_opts.dst_index = r->index;
 	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
 	unpack_tree_opts.update = 1;
 	unpack_tree_opts.merge = 1;
 	if (!detach_head)
 		unpack_tree_opts.reset = 1;
 
-	if (repo_read_index_unmerged(the_repository) < 0) {
+	if (repo_read_index_unmerged(r) < 0) {
 		ret = error(_("could not read index"));
 		goto leave_reset_head;
 	}
 
-	if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {
+	if (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {
 		ret = error(_("failed to find tree of %s"),
 			    oid_to_hex(&head_oid));
 		goto leave_reset_head;
 	}
 
-	if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {
+	if (!fill_tree_descriptor(r, &desc[nr++], oid)) {
 		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
 		goto leave_reset_head;
 	}
@@ -833,16 +836,16 @@ static int reset_head(struct object_id *oid, const char *action,
 	}
 
 	tree = parse_tree_indirect(oid);
-	prime_cache_tree(the_repository, the_repository->index, tree);
+	prime_cache_tree(r, r->index, tree);
 
-	if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {
+	if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {
 		ret = error(_("could not write index"));
 		goto leave_reset_head;
 	}
 
 reset_head_refs:
 	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
-	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
+	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : default_reflog_action);
 	prefix_len = msg.len;
 
 	if (update_orig_head) {
@@ -904,8 +907,10 @@ static int move_to_original_branch(struct rebase_options *opts)
 		    opts->head_name, oid_to_hex(&opts->onto->object.oid));
 	strbuf_addf(&head_reflog, "rebase finished: returning to %s",
 		    opts->head_name);
-	ret = reset_head(NULL, "", opts->head_name, RESET_HEAD_REFS_ONLY,
-			 orig_head_reflog.buf, head_reflog.buf);
+	ret = reset_head(the_repository, NULL, "", opts->head_name,
+			 RESET_HEAD_REFS_ONLY,
+			 orig_head_reflog.buf, head_reflog.buf,
+			 DEFAULT_REFLOG_ACTION);
 
 	strbuf_release(&orig_head_reflog);
 	strbuf_release(&head_reflog);
@@ -999,8 +1004,9 @@ static int run_am(struct rebase_options *opts)
 		free(rebased_patches);
 		argv_array_clear(&am.args);
 
-		reset_head(&opts->orig_head, "checkout", opts->head_name, 0,
-			   "HEAD", NULL);
+		reset_head(the_repository, &opts->orig_head, "checkout",
+			   opts->head_name, 0,
+			   "HEAD", NULL, DEFAULT_REFLOG_ACTION);
 		error(_("\ngit encountered an error while preparing the "
 			"patches to replay\n"
 			"these revisions:\n"
@@ -1609,8 +1615,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		rerere_clear(the_repository, &merge_rr);
 		string_list_clear(&merge_rr, 1);
 
-		if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
-			       NULL, NULL) < 0)
+		if (reset_head(the_repository, NULL, "reset", NULL, RESET_HEAD_HARD,
+			       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)
 			die(_("could not discard worktree changes"));
 		remove_branch_state(the_repository, 0);
 		if (read_basic_state(&options))
@@ -1627,9 +1633,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 		if (read_basic_state(&options))
 			exit(1);
-		if (reset_head(&options.orig_head, "reset",
+		if (reset_head(the_repository, &options.orig_head, "reset",
 			       options.head_name, RESET_HEAD_HARD,
-			       NULL, NULL) < 0)
+			       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)
 			die(_("could not move back to %s"),
 			    oid_to_hex(&options.orig_head));
 		remove_branch_state(the_repository, 0);
@@ -1998,8 +2004,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				    options.state_dir);
 			write_file(autostash, "%s", oid_to_hex(&oid));
 			printf(_("Created autostash: %s\n"), buf.buf);
-			if (reset_head(NULL, "reset --hard",
-				       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
+			if (reset_head(the_repository, NULL, "reset --hard",
+				       NULL, RESET_HEAD_HARD, NULL, NULL,
+				       DEFAULT_REFLOG_ACTION) < 0)
 				die(_("could not reset --hard"));
 
 			if (discard_index(the_repository->index) < 0 ||
@@ -2045,10 +2052,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				strbuf_addf(&buf, "%s: checkout %s",
 					    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
 					    options.switch_to);
-				if (reset_head(&oid, "checkout",
+				if (reset_head(the_repository, &oid, "checkout",
 					       options.head_name,
 					       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
-					       NULL, buf.buf) < 0) {
+					       NULL, buf.buf,
+					       DEFAULT_REFLOG_ACTION) < 0) {
 					ret = !!error(_("could not switch to "
 							"%s"),
 						      options.switch_to);
@@ -2120,10 +2128,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	strbuf_addf(&msg, "%s: checkout %s",
 		    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
-	if (reset_head(&options.onto->object.oid, "checkout", NULL,
+	if (reset_head(the_repository, &options.onto->object.oid, "checkout", NULL,
 		       RESET_HEAD_DETACH | RESET_ORIG_HEAD |
 		       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
-		       NULL, msg.buf))
+		       NULL, msg.buf, DEFAULT_REFLOG_ACTION))
 		die(_("Could not detach HEAD"));
 	strbuf_release(&msg);
 
@@ -2138,8 +2146,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		strbuf_addf(&msg, "rebase finished: %s onto %s",
 			options.head_name ? options.head_name : "detached HEAD",
 			oid_to_hex(&options.onto->object.oid));
-		reset_head(NULL, "Fast-forwarded", options.head_name,
-			   RESET_HEAD_REFS_ONLY, "HEAD", msg.buf);
+		reset_head(the_repository, NULL, "Fast-forwarded", options.head_name,
+			   RESET_HEAD_REFS_ONLY, "HEAD", msg.buf,
+			   DEFAULT_REFLOG_ACTION);
 		strbuf_release(&msg);
 		ret = !!finish_rebase(&options);
 		goto cleanup;
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#39 lore raw

PATCH v2 11/17 rebase: extract create_autostash()

In reply to Denton Liu

In a future commit, we will lib-ify this code. In preparation for this, extract the code into the create_autostash() function so that it can be cleaned up before it is finally lib-ified.

This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.

---
 builtin/rebase.c | 94 +++++++++++++++++++++++++-----------------------
 1 file changed, 50 insertions(+), 44 deletions(-)
builtin/rebase.c +50 -44
index a6d7527f4c..b60be5c875 100644
@@ -1232,6 +1232,55 @@ static int check_exec_cmd(const char *cmd)
 	return 0;
 }
 
+static void create_autostash(struct rebase_options *options)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct lock_file lock_file = LOCK_INIT;
+	int fd;
+
+	fd = hold_locked_index(&lock_file, 0);
+	refresh_cache(REFRESH_QUIET);
+	if (0 <= fd)
+		repo_update_index_if_able(the_repository, &lock_file);
+	rollback_lock_file(&lock_file);
+
+	if (has_unstaged_changes(the_repository, 1) ||
+	    has_uncommitted_changes(the_repository, 1)) {
+		const char *autostash =
+			state_dir_path("autostash", options);
+		struct child_process stash = CHILD_PROCESS_INIT;
+		struct object_id oid;
+
+		argv_array_pushl(&stash.args,
+				 "stash", "create", "autostash", NULL);
+		stash.git_cmd = 1;
+		stash.no_stdin = 1;
+		strbuf_reset(&buf);
+		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
+			die(_("Cannot autostash"));
+		strbuf_trim_trailing_newline(&buf);
+		if (get_oid(buf.buf, &oid))
+			die(_("Unexpected stash response: '%s'"),
+			    buf.buf);
+		strbuf_reset(&buf);
+		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
+
+		if (safe_create_leading_directories_const(autostash))
+			die(_("Could not create directory for '%s'"),
+			    options->state_dir);
+		write_file(autostash, "%s", oid_to_hex(&oid));
+		printf(_("Created autostash: %s\n"), buf.buf);
+		if (reset_head(the_repository, NULL, "reset --hard",
+			       NULL, RESET_HEAD_HARD, NULL, NULL,
+			       DEFAULT_REFLOG_ACTION) < 0)
+			die(_("could not reset --hard"));
+
+		if (discard_index(the_repository->index) < 0 ||
+			repo_read_index(the_repository) < 0)
+			die(_("could not read index"));
+	}
+	strbuf_release(&buf);
+}
 
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
@@ -1832,50 +1881,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		die(_("could not read index"));
 
 	if (options.autostash) {
-		struct lock_file lock_file = LOCK_INIT;
-		int fd;
-
-		fd = hold_locked_index(&lock_file, 0);
-		refresh_cache(REFRESH_QUIET);
-		if (0 <= fd)
-			repo_update_index_if_able(the_repository, &lock_file);
-		rollback_lock_file(&lock_file);
-
-		if (has_unstaged_changes(the_repository, 1) ||
-		    has_uncommitted_changes(the_repository, 1)) {
-			const char *autostash =
-				state_dir_path("autostash", &options);
-			struct child_process stash = CHILD_PROCESS_INIT;
-			struct object_id oid;
-
-			argv_array_pushl(&stash.args,
-					 "stash", "create", "autostash", NULL);
-			stash.git_cmd = 1;
-			stash.no_stdin = 1;
-			strbuf_reset(&buf);
-			if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
-				die(_("Cannot autostash"));
-			strbuf_trim_trailing_newline(&buf);
-			if (get_oid(buf.buf, &oid))
-				die(_("Unexpected stash response: '%s'"),
-				    buf.buf);
-			strbuf_reset(&buf);
-			strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
-
-			if (safe_create_leading_directories_const(autostash))
-				die(_("Could not create directory for '%s'"),
-				    options.state_dir);
-			write_file(autostash, "%s", oid_to_hex(&oid));
-			printf(_("Created autostash: %s\n"), buf.buf);
-			if (reset_head(the_repository, NULL, "reset --hard",
-				       NULL, RESET_HEAD_HARD, NULL, NULL,
-				       DEFAULT_REFLOG_ACTION) < 0)
-				die(_("could not reset --hard"));
-
-			if (discard_index(the_repository->index) < 0 ||
-				repo_read_index(the_repository) < 0)
-				die(_("could not read index"));
-		}
+		create_autostash(&options);
 	}
 
 	if (require_clean_work_tree(the_repository, "rebase",
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#45 lore raw

PATCH v2 17/17 pull: pass --autostash to merge

In reply to Denton Liu

Before, --autostash only worked with git pull --rebase. However, in the last patch, merge learned --autostash as well so there's no reason why we should have this restriction anymore. Teach pull to pass --autostash to merge, just like it did for rebase.

---
 Documentation/git-pull.txt      |  9 -------
 Documentation/merge-options.txt |  4 +--
 builtin/pull.c                  |  9 ++++---
 t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------
 4 files changed, 42 insertions(+), 23 deletions(-)
Documentation/git-pull.txt +0 -9
index dfb901f8b8..ba3772de9f 100644
@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.
 --no-rebase::
 	Override earlier --rebase.
 
---autostash::
---no-autostash::
-	Before starting rebase, stash local modifications away (see
-	linkgit:git-stash[1]) if needed, and apply the stash entry when
-	done. `--no-autostash` is useful to override the `rebase.autoStash`
-	configuration variable (see linkgit:git-config[1]).
-+
-This option is only valid when "--rebase" is used.
-
 Options related to fetching
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
Documentation/merge-options.txt +2 -2
index 34493eb58b..ae56cca826 100644
@@ -155,6 +155,8 @@ ifndef::git-pull[]
 	Note that not all merge strategies may support progress
 	reporting.
 
+endif::git-pull[]
+
 --autostash::
 --no-autostash::
 	Automatically create a temporary stash entry before the operation
@@ -163,8 +165,6 @@ ifndef::git-pull[]
 	with care: the final stash application after a successful
 	rebase might result in non-trivial conflicts.
 
-endif::git-pull[]
-
 --allow-unrelated-histories::
 	By default, `git merge` command refuses to merge histories
 	that do not share a common ancestor.  This option can be
builtin/pull.c +5 -4
index d25ff13a60..ee186781ae 100644
@@ -183,7 +183,7 @@ static struct option pull_options[] = {
 		N_("verify that the named commit has a valid GPG signature"),
 		PARSE_OPT_NOARG),
 	OPT_BOOL(0, "autostash", &opt_autostash,
-		N_("automatically stash/stash pop before and after rebase")),
+		N_("automatically stash/stash pop before and after")),
 	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
 		N_("merge strategy to use"),
 		0),
@@ -671,6 +671,10 @@ static int run_merge(void)
 	argv_array_pushv(&args, opt_strategy_opts.argv);
 	if (opt_gpg_sign)
 		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");
 	if (opt_allow_unrelated_histories > 0)
 		argv_array_push(&args, "--allow-unrelated-histories");
 
@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 	if (get_oid("HEAD", &orig_head))
 		oidclr(&orig_head);
 
-	if (!opt_rebase && opt_autostash != -1)
-		die(_("--[no-]autostash option is only valid with --rebase."));
-
 	autostash = config_autostash;
 	if (opt_rebase) {
 		if (opt_autostash != -1)
t/t5520-pull.sh +35 -8

55 lines not on this page · raw message

Signature · 1 line
2.24.1.810.g65a2f617f4
author
#42 lore raw

PATCH v2 14/17 sequencer: unlink autostash in apply_autostash()

In reply to Denton Liu

Explicitly remove autostash file in apply_autostash() once it has been applied successfully.

This is currently a no-op because the only users of this function will unlink the state (including the autostash file) after this function runs. However, in the future, we will introduce a user of the function that does not explicitly remove the state so we do it here.

---
 sequencer.c | 1 +
 1 file changed, 1 insertion(+)
sequencer.c +1 -0
index ba04ee2de8..cc0391ca35 100644
@@ -3834,6 +3834,7 @@ int apply_autostash(const char *path)
 				  " \"git stash drop\" at any time.\n"));
 	}
 
+	unlink(path);
 	strbuf_release(&stash_sha1);
 	return ret;
 }
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#44 lore raw

PATCH v2 16/17 t5520: make test_pull_autostash() accept expect_parent_num

In reply to Denton Liu

Before, test_pull_autostash() was hardcoded to run test_cmp_rev HEAD^ copy to test that a rebase happened. However, in a future patch, we plan on testing merging as well. Make test_pull_autostash() accept a parent number as an argument so that, in the future, we can test if a merge happened in addition to a rebase.

---
 t/t5520-pull.sh | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)
t/t5520-pull.sh +8 -6
index 602d996a33..218f469d0a 100755
@@ -10,11 +10,13 @@ modify () {
 }
 
 test_pull_autostash () {
+	expect_parent_num="$1" &&
+	shift &&
 	git reset --hard before-rebase &&
 	echo dirty >new_file &&
 	git add new_file &&
 	git pull "$@" . copy &&
-	test_cmp_rev HEAD^ copy &&
+	test_cmp_rev HEAD^"$expect_parent_num" copy &&
 	echo dirty >expect &&
 	test_cmp expect new_file &&
 	echo "modified again" >expect &&
@@ -356,22 +358,22 @@ test_expect_success '--rebase fails with multiple branches' '
 
 test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' '
 	test_config rebase.autostash true &&
-	test_pull_autostash --rebase
+	test_pull_autostash 1 --rebase
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash=true' '
 	test_config rebase.autostash true &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash=false' '
 	test_config rebase.autostash false &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash unset' '
 	test_unconfig rebase.autostash &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '
@@ -409,7 +411,7 @@ test_expect_success 'pull.rebase' '
 
 test_expect_success 'pull --autostash & pull.rebase=true' '
 	test_config pull.rebase true &&
-	test_pull_autostash --autostash
+	test_pull_autostash 1 --autostash
 '
 
 test_expect_success 'pull --no-autostash & pull.rebase=true' '
Signature · 1 line
2.24.1.810.g65a2f617f4
author
#43 lore raw

PATCH v2 15/17 merge: teach --autostash option

In reply to Denton Liu

In rebase, one can pass the --autostash option to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.

Implement the --autostash option and corresponding merge.autoStash option in merge which stashes before merging and then pops after.

This option is useful when a developer has some local changes on a topic branch but they realize that their work depends on another branch. Previously, they had to run something like

git fetch ...
git stash push
git merge FETCH_HEAD
git stash pop

but now, that is reduced to

	git fetch ...
	git merge --autostash FETCH_HEAD
---
 Documentation/config/merge.txt  | 10 +++++
 Documentation/merge-options.txt |  8 ++++
 branch.c                        |  1 +
 builtin/commit.c                |  2 +
 builtin/merge.c                 | 17 +++++++++
 path.c                          |  1 +
 path.h                          |  4 +-
 t/t3033-merge-toplevel.sh       | 22 +++++++++++
 t/t7600-merge.sh                | 65 +++++++++++++++++++++++++++++++++
 9 files changed, 129 insertions(+), 1 deletion(-)
Documentation/config/merge.txt +10 -0
index 6a313937f8..88b29127bf 100644
@@ -70,6 +70,16 @@ merge.stat::
 	Whether to print the diffstat between ORIG_HEAD and the merge result
 	at the end of the merge.  True by default.
 
+merge.autoStash::
+	When set to true, automatically create a temporary stash entry
+	before the operation begins, and apply it after the operation
+	ends.  This means that you can run rebase on a dirty worktree.
+	However, use with care: the final stash application after a
+	successful rebase might result in non-trivial conflicts.
+	This option can be overridden by the `--no-autostash` and
+	`--autostash` options of linkgit:git-merge[1].
+	Defaults to false.
+
 merge.tool::
 	Controls which merge tool is used by linkgit:git-mergetool[1].
 	The list below shows the valid built-in values.
Documentation/merge-options.txt +8 -0
index 40dc4f5e8c..34493eb58b 100644
@@ -155,6 +155,14 @@ ifndef::git-pull[]
 	Note that not all merge strategies may support progress
 	reporting.
 
+--autostash::
+--no-autostash::
+	Automatically create a temporary stash entry before the operation
+	begins, and apply it after the operation ends.  This means
+	that you can run rebase on a dirty worktree.  However, use
+	with care: the final stash application after a successful
+	rebase might result in non-trivial conflicts.
+
 endif::git-pull[]
 
 --allow-unrelated-histories::
branch.c +1 -0
index 579494738a..bf2536c70d 100644
@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
 	unlink(git_path_merge_rr(r));
 	unlink(git_path_merge_msg(r));
 	unlink(git_path_merge_mode(r));
+	apply_autostash(git_path_merge_autostash(r));
 }
 
 void remove_branch_state(struct repository *r, int verbose)
builtin/commit.c +2 -0
index e48c1fd90a..ebb7f4d80e 100644
@@ -1679,6 +1679,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	unlink(git_path_merge_mode(the_repository));
 	unlink(git_path_squash_msg(the_repository));
 
+	apply_autostash(git_path_merge_autostash(the_repository));
+
 	if (commit_index_files())
 		die(_("repository has been updated, but unable to write\n"
 		      "new_index file. Check that disk is not full and quota is\n"
builtin/merge.c +17 -0
index 062e911441..a940bd4cbf 100644
@@ -81,6 +81,7 @@ static int show_progress = -1;
 static int default_to_upstream = 1;
 static int signoff;
 static const char *sign_commit;
+static int autostash;
 static int no_verify;
 
 static struct strategy all_strategy[] = {
@@ -285,6 +286,8 @@ static struct option builtin_merge_options[] = {
 	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
 	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
 	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+	OPT_BOOL(0, "autostash", &autostash,
+	      N_("automatically stash/stash pop before and after")),
 	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
 	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
 	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ -440,6 +443,7 @@ static void finish(struct commit *head_commit,
 		strbuf_addf(&reflog_message, "%s: %s",
 			getenv("GIT_REFLOG_ACTION"), msg);
 	}
+	apply_autostash(git_path_merge_autostash(the_repository));
 	if (squash) {
 		squash_message(head_commit, remoteheads);
 	} else {
@@ -631,6 +635,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 	} else if (!strcmp(k, "commit.gpgsign")) {
 		sign_commit = git_config_bool(k, v) ? "" : NULL;
 		return 0;
+	} else if (!strcmp(k, "merge.autostash")) {
+		autostash = git_config_bool(k, v);
+		return 0;
 	}
 
 	status = fmt_merge_msg_config(k, v, cb);
@@ -1288,6 +1295,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
 		/* Invoke 'git reset --merge' */
 		ret = cmd_reset(nargc, nargv, prefix);
+		apply_autostash(git_path_merge_autostash(the_repository));
 		goto done;
 	}
 
@@ -1508,6 +1516,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 			goto done;
 		}
 
+		if (autostash)
+			create_autostash(the_repository,
+					 git_path_merge_autostash(the_repository),
+					 "merge");
 		if (checkout_fast_forward(the_repository,
 					  &head_commit->object.oid,
 					  &commit->object.oid,
@@ -1574,6 +1586,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	if (fast_forward == FF_ONLY)
 		die(_("Not possible to fast-forward, aborting."));
 
+	if (autostash)
+		create_autostash(the_repository,
+				 git_path_merge_autostash(the_repository),
+				 "merge");
+
 	/* We are going to make a new commit. */
 	git_committer_info(IDENT_STRICT);
 
path.c +1 -0
index a76eec8b96..d8c4072f1a 100644
@@ -1533,5 +1533,6 @@ REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
 REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
 REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE")
 REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
+REPO_GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
 REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")
 REPO_GIT_PATH_FUNC(shallow, "shallow")
path.h +3 -1
index 14d6dcad16..1f1bf8f87a 100644
@@ -177,11 +177,12 @@ struct path_cache {
 	const char *merge_rr;
 	const char *merge_mode;
 	const char *merge_head;
+	const char *merge_autostash;
 	const char *fetch_head;
 	const char *shallow;
 };
 
-#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 
 const char *git_path_cherry_pick_head(struct repository *r);
 const char *git_path_revert_head(struct repository *r);
@@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);
 const char *git_path_merge_rr(struct repository *r);
 const char *git_path_merge_mode(struct repository *r);
 const char *git_path_merge_head(struct repository *r);
+const char *git_path_merge_autostash(struct repository *r);
 const char *git_path_fetch_head(struct repository *r);
 const char *git_path_shallow(struct repository *r);
 
t/t3033-merge-toplevel.sh +22 -0
index d314599428..e29c284b9b 100755
@@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' '
 	test_must_fail git merge five
 '
 
+test_expect_success 'refuse two-project merge by default, quit before --autostash happens' '
+	t3033_reset &&
+	git reset --hard four &&
+	echo change >>one.t &&
+	git diff >expect &&
+	test_must_fail git merge --autostash five 2>err &&
+	test_i18ngrep ! "stash" err &&
+	git diff >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'two-project merge with --allow-unrelated-histories' '
 	t3033_reset &&
 	git reset --hard four &&
@@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' '
 	git diff --exit-code five
 '
 
+test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' '
+	t3033_reset &&
+	git reset --hard four &&
+	echo change >>one.t &&
+	git diff one.t >expect &&
+	git merge --allow-unrelated-histories --autostash five 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git diff one.t >actual &&
+	test_cmp expect actual
+'
+
 test_done
t/t7600-merge.sh +65 -0

84 lines not on this page · raw message

Signature · 1 line
2.24.1.810.g65a2f617f4
#32 lore raw

Re: [PATCH v2 04/17] sequencer: configurably warn on non-existent files

In reply to Denton Liu

sequencer.c static int write_message(const void *buf, size_t len, const char *filename, · line 430

Quoted 4 lines · Denton Liu
sequencer.c +2 -2
-static int read_oneliner(struct strbuf *buf,
-	const char *path, int skip_if_empty)
+static int read_oneliner(struct strbuf *buf, const char *path,
+			 int skip_if_empty, int warn_nonexistence)

I would have to say that this results in a rather poor API in the end, and also forces an awkward API evolution, and we need to be careful especially if we plan to make this an external API in a later step in the series.

There is a topic in flight that introduces additional callsites of read_oneliner(). It is a selfish move that breaks codebase unnecessarily to unilaterally change the function signature.

Two possible ways to improve are:

* keep read_oneliner() with existing function signature working for other topics in flight, by renaming your extended variant and making read_oneliner() a thin wrapper that calls yours with the extended feature disabled (i.e. warn_nonexistence==0); or

* update skip_if_empty that wastes an int parameter to pass only a single bit into an unsigned that is a collection of bits, after vetting that new callsites contemporary topics add always pass either 0 or 1 as the parameter. Then

	#define READ_ONELINER_SKIP_IF_EMPTY 01
	#define READ_ONELINER_WARN_MISSING  02
	static int read_oneliner(struct strbuf *buf, const char *path,

unsigned flags)

would transparently work well for those other topics.

The latter probably is more preferrable, as the end result would be a cleaner API than the former or the one presented in this series.

Thanks.

Quoted 119 lines

{

sequencer.c +14 -14
 	int ret = 0;
 	struct strbuf file_buf = STRBUF_INIT;
 
-	if (!file_exists(path))
+	if (!warn_nonexistence && !file_exists(path))
 		return 0;
 
 	if (strbuf_read_file(&file_buf, path, 0) < 0) {
@@ -2558,10 +2558,10 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
 static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
 {
 	strbuf_reset(buf);
-	if (!read_oneliner(buf, rebase_path_strategy(), 0))
+	if (!read_oneliner(buf, rebase_path_strategy(), 0, 0))
 		return;
 	opts->strategy = strbuf_detach(buf, NULL);
-	if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
+	if (!read_oneliner(buf, rebase_path_strategy_opts(), 0, 0))
 		return;
 
 	parse_strategy_opts(opts, buf->buf);
@@ -2572,7 +2572,7 @@ static int read_populate_opts(struct replay_opts *opts)
 	if (is_rebase_i(opts)) {
 		struct strbuf buf = STRBUF_INIT;
 
-		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
+		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1, 0)) {
 			if (!starts_with(buf.buf, "-S"))
 				strbuf_reset(&buf);
 			else {
@@ -2582,7 +2582,7 @@ static int read_populate_opts(struct replay_opts *opts)
 			strbuf_reset(&buf);
 		}
 
-		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
+		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1, 0)) {
 			if (!strcmp(buf.buf, "--rerere-autoupdate"))
 				opts->allow_rerere_auto = RERERE_AUTOUPDATE;
 			else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
@@ -2618,7 +2618,7 @@ static int read_populate_opts(struct replay_opts *opts)
 		strbuf_release(&buf);
 
 		if (read_oneliner(&opts->current_fixups,
-				  rebase_path_current_fixups(), 1)) {
+				  rebase_path_current_fixups(), 1, 0)) {
 			const char *p = opts->current_fixups.buf;
 			opts->current_fixup_count = 1;
 			while ((p = strchr(p, '\n'))) {
@@ -2627,7 +2627,7 @@ static int read_populate_opts(struct replay_opts *opts)
 			}
 		}
 
-		if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {
+		if (read_oneliner(&buf, rebase_path_squash_onto(), 0, 0)) {
 			if (get_oid_hex(buf.buf, &opts->squash_onto) < 0)
 				return error(_("unusable squash-onto"));
 			opts->have_squash_onto = 1;
@@ -3759,7 +3759,7 @@ static int apply_autostash(struct replay_opts *opts)
 	struct child_process child = CHILD_PROCESS_INIT;
 	int ret = 0;
 
-	if (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {
+	if (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1, 0)) {
 		strbuf_release(&stash_sha1);
 		return 0;
 	}
@@ -4093,7 +4093,7 @@ static int pick_commits(struct repository *r,
 		if (todo_list->current < todo_list->nr)
 			return 0;
 
-		if (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&
+		if (read_oneliner(&head_ref, rebase_path_head_name(), 0, 0) &&
 				starts_with(head_ref.buf, "refs/")) {
 			const char *msg;
 			struct object_id head, orig;
@@ -4106,13 +4106,13 @@ static int pick_commits(struct repository *r,
 				strbuf_release(&buf);
 				return res;
 			}
-			if (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||
+			if (!read_oneliner(&buf, rebase_path_orig_head(), 0, 0) ||
 					get_oid_hex(buf.buf, &orig)) {
 				res = error(_("could not read orig-head"));
 				goto cleanup_head_ref;
 			}
 			strbuf_reset(&buf);
-			if (!read_oneliner(&buf, rebase_path_onto(), 0)) {
+			if (!read_oneliner(&buf, rebase_path_onto(), 0, 0)) {
 				res = error(_("could not read 'onto'"));
 				goto cleanup_head_ref;
 			}
@@ -4145,7 +4145,7 @@ static int pick_commits(struct repository *r,
 				DIFF_FORMAT_DIFFSTAT;
 			log_tree_opt.disable_stdin = 1;
 
-			if (read_oneliner(&buf, rebase_path_orig_head(), 0) &&
+			if (read_oneliner(&buf, rebase_path_orig_head(), 0, 0) &&
 			    !get_oid(buf.buf, &orig) &&
 			    !get_oid("HEAD", &head)) {
 				diff_tree_oid(&orig, &head, "",
@@ -4230,7 +4230,7 @@ static int commit_staged_changes(struct repository *r,
 
 		if (get_oid("HEAD", &head))
 			return error(_("cannot amend non-existing commit"));
-		if (!read_oneliner(&rev, rebase_path_amend(), 0))
+		if (!read_oneliner(&rev, rebase_path_amend(), 0, 0))
 			return error(_("invalid file: '%s'"), rebase_path_amend());
 		if (get_oid_hex(rev.buf, &to_amend))
 			return error(_("invalid contents: '%s'"),
@@ -4391,7 +4391,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
 		struct strbuf buf = STRBUF_INIT;
 		struct object_id oid;
 
-		if (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&
+		if (read_oneliner(&buf, rebase_path_stopped_sha(), 1, 0) &&
 		    !get_oid_committish(buf.buf, &oid))
 			record_in_rewritten(&oid, peek_command(&todo_list, 0));
 		strbuf_release(&buf);
#28 lore raw

Re: [PATCH v2 01/17] Makefile: alphabetically sort += lists

In reply to Denton Liu
Quoted 3 lines · Denton Liu

Note that if we omit file prefixes, the lists aren't sorted in strictly alphabetical order (e.g. archive.o comes after archive-zip.o instead of before archive-tar.o). This is intentional ...

That's fine, but then we shouldn't label it as "alphabetically sort".

Quoted 3 lines

because the purpose of
maintaining the sorted list is to ensure line insertions are deterministic.

Truly alphabetic sorting is deterministic, too, so that is *not* the reason why you are not being strictly alphabetic. The true reason is because it is easier to apply mechanical sorting in ascii order than truly sort alphabetically. Both are equally deterministic and reproducible. Alphabetic might be easier for humans to see and understand, but you value mechanical ease of sorting and that is why you chose ascii order over alphabetical sorting.

#46 lore raw
In reply to Denton Liu
Quoted 3 lines · Denton Liu

Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.

Sigh.

I would actually have preferred to remove the autostash from rebase if the goal is to have a consistent behaviour between the two, though ;-)

But it is OK as long as it does not degrade the codepath with changes that wouldn't have been necessary if this weren't added.

Let's see how it goes. Queued.

#47 lore raw
Quoted 11 lines · Junio C Hamano
Quoted 3 lines · Denton Liu

Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.

Sigh.

I would actually have preferred to remove the autostash from rebase if the goal is to have a consistent behaviour between the two, though ;-)

I find the --autostash option to rebase useful if I want to edit a commit and I've got uncommitted changes. I wish it was called --stash though as it's not automatic as one has to set a config setting or pass it on the commandline. I'm also not convinced the config setting is a good idea as it allows rebase to run when I think I've committed everything but I haven't.

For merge --autostash I think we need to consider the interaction of --no-commit with --autostash as stash pop will refuse to run if the merge modified any of the stashed paths.

Best Wishes

Phillip

Quoted 5 lines

But it is OK as long as it does not degrade the codepath with changes that wouldn't have been necessary if this weren't added.

Let's see how it goes. Queued.

author
#51 lore raw
In reply to Junio C Hamano

Hi Junio,

Quoted 8 lines · Junio C Hamano
Quoted 3 lines · Denton Liu

Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.

Sigh.

Quoted 8 lines

I would actually have preferred to remove the autostash from rebase if the goal is to have a consistent behaviour between the two, though ;-)

But it is OK as long as it does not degrade the codepath with changes that wouldn't have been necessary if this weren't added.

Let's see how it goes. Queued.

Sorry for the radio silence for the past week. I've been offline trying to recover from a particularly bad flu. Anyway, I have a reroll of this topic planned with the flags change you suggested and addressing Philip's concerns about --no-commit.

Since this is a new topic that definitely won't be accepted for v2.25.0, I'll probably be taking my time with this ;)

Thanks,

Denton

author
#48 lore raw
In reply to Phillip Wood

Hi Phillip,

Sorry for the late reply. I'm back in school so I've been pretty busy lately.

Quoted 3 lines · Phillip Wood

For merge --autostash I think we need to consider the interaction of --no-commit with --autostash as stash pop will refuse to run if the merge modified any of the stashed paths.

The only time when we run the stash pop is when we either commit the merge or abort it. With this in mind, what do you think of the following test cases? If they look good, I can squash them into the appropriate patch and send in a reroll.

	diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
	index e0c8a0bad4..af5db58e16 100755
	--- a/t/t7600-merge.sh
	+++ b/t/t7600-merge.sh
	@@ -727,6 +727,33 @@ test_expect_success 'conflicted merge with --autostash, --abort restores stash'
		test_cmp file.1 file
	 '
	 
	+test_expect_success 'completed merge with --no-commit and --autostash' '
	+	git reset --hard c1 &&
	+	git merge-file file file.orig file.9 &&
	+	git diff >expect &&
	+	git merge --no-commit --autostash c2 &&
	+	git stash show -p MERGE_AUTOSTASH >actual &&
	+	test_cmp expect actual &&
	+	git commit 2>err &&
	+	test_i18ngrep "Applied autostash." err &&
	+	git show HEAD:file >merge-result &&
	+	test_cmp result.1-5 merge-result &&
	+	test_cmp result.1-5-9 file
	+'
	+
	+test_expect_success 'aborted merge with --no-commit and --autostash' '
	+	git reset --hard c1 &&
	+	git merge-file file file.orig file.9 &&
	+	git diff >expect &&
	+	git merge --no-commit --autostash c2 &&
	+	git stash show -p MERGE_AUTOSTASH >actual &&
	+	test_cmp expect actual &&
	+	git merge --abort 2>err &&
	+	test_i18ngrep "Applied autostash." err &&
	+	git diff >actual &&
	+	test_cmp expect actual
	+'
	+
	 test_expect_success 'merge with conflicted --autostash changes' '
		git reset --hard c1 &&
		git merge-file file file.orig file.9y &&

Thanks,

Denton

Quoted 4 lines

Best Wishes

Phillip

#49 lore raw

Hi Denton

Quoted 6 lines · Denton Liu

Hi Phillip,

Sorry for the late reply. I'm back in school so I've been pretty busy lately.

On Tue, Dec 31, 2019 at 10:34:30AM +0000, Phillip Wood wrote:

Quoted 8 lines
Quoted 3 lines

For merge --autostash I think we need to consider the interaction of --no-commit with --autostash as stash pop will refuse to run if the merge modified any of the stashed paths.

The only time when we run the stash pop is when we either commit the merge or abort it. With this in mind, what do you think of the following test cases? If they look good, I can squash them into the appropriate patch and send in a reroll.

Ah I misunderstood what happened with --autostash and --no-commit, the tests basically look fine (I've got one comment below).

One other thing - if the user runs git reset or git checkout <branch> then cleanup_branch_state() removes MERGE_HEAD, MERGE_MSG etc. If we're not already doing so then I think we should remove MERGE_AUTOSTASH as well or you can get into a situation where the user does something like

git merge --autostash <something> # results in conflicts git reset --hard <somewhere else>
git merge <something> # succeeds and confusingly pops previous stash

Running git reset doesn't make sense unless they want to discard the stashed changes as well. This is a difference with rebase where you cannot lose the stashed changes by running git reset, the only way they can get lost is by running rebase --quit. We could always add a warning about it throwing away the stashed changes in the future.

I still not keen on the name --autostash as it's not automatic. --stash would make more sense to me. We'd have to deprecate rebase --autostash in favor of rebase --stash to change it but it if we want to change the name it would be better now before adding --autostash to merge and pull - what do you think?

Quoted 11 lines
	diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
	index e0c8a0bad4..af5db58e16 100755
	--- a/t/t7600-merge.sh
	+++ b/t/t7600-merge.sh
	@@ -727,6 +727,33 @@ test_expect_success 'conflicted merge with --autostash, --abort restores stash'
		test_cmp file.1 file
	 '
	
	+test_expect_success 'completed merge with --no-commit and --autostash' '
	+	git reset --hard c1 &&
	+	git merge-file file file.orig file.9 &&

Is this a complicated way of getting some unstaged changes so we can stash them or have I missed something?

Best Wishes

Phillip

Quoted 36 lines
	+	git diff >expect &&
	+	git merge --no-commit --autostash c2 &&
	+	git stash show -p MERGE_AUTOSTASH >actual &&
	+	test_cmp expect actual &&
	+	git commit 2>err &&
	+	test_i18ngrep "Applied autostash." err &&
	+	git show HEAD:file >merge-result &&
	+	test_cmp result.1-5 merge-result &&
	+	test_cmp result.1-5-9 file
	+'
	+
	+test_expect_success 'aborted merge with --no-commit and --autostash' '
	+	git reset --hard c1 &&
	+	git merge-file file file.orig file.9 &&
	+	git diff >expect &&
	+	git merge --no-commit --autostash c2 &&
	+	git stash show -p MERGE_AUTOSTASH >actual &&
	+	test_cmp expect actual &&
	+	git merge --abort 2>err &&
	+	test_i18ngrep "Applied autostash." err &&
	+	git diff >actual &&
	+	test_cmp expect actual
	+'
	+
	 test_expect_success 'merge with conflicted --autostash changes' '
		git reset --hard c1 &&
		git merge-file file file.orig file.9y &&

Thanks,

Denton

Quoted 4 lines

Best Wishes

Phillip

author
#50 lore raw
Quoted 18 lines · Phillip Wood

Hi Denton

Quoted 6 lines · Denton Liu

Hi Phillip,

Sorry for the late reply. I'm back in school so I've been pretty busy lately.

On Tue, Dec 31, 2019 at 10:34:30AM +0000, Phillip Wood wrote:

Quoted 8 lines
Quoted 3 lines

For merge --autostash I think we need to consider the interaction of --no-commit with --autostash as stash pop will refuse to run if the merge modified any of the stashed paths.

The only time when we run the stash pop is when we either commit the merge or abort it. With this in mind, what do you think of the following test cases? If they look good, I can squash them into the appropriate patch and send in a reroll.

Quoted 7 lines

Ah I misunderstood what happened with --autostash and --no-commit, the tests basically look fine (I've got one comment below).

One other thing - if the user runs git reset or git checkout <branch> then cleanup_branch_state() removes MERGE_HEAD, MERGE_MSG etc. If we're not already doing so then I think we should remove MERGE_AUTOSTASH as well or you can get into a situation where the user does something like

In cleanup_branch_state(), I insert apply_autostash() at the very end of the function so that the stash is popped whenever this is called.

Quoted 9 lines

git merge --autostash <something> # results in conflicts git reset --hard <somewhere else>
git merge <something> # succeeds and confusingly pops previous stash

Running git reset doesn't make sense unless they want to discard the stashed changes as well. This is a difference with rebase where you cannot lose the stashed changes by running git reset, the only way they can get lost is by running rebase --quit. We could always add a warning about it throwing away the stashed changes in the future.

Currently, in rebase, if the stash cannot be popped cleanly, it is automatically pushed onto the stash stack so that the user can deal with it later. Do we want to do a similar thing where if we reset --hard with an autostash present, we store the stash and then leave the users with a clean worktree (as they'd expect)?

Quoted 5 lines

I still not keen on the name --autostash as it's not automatic. --stash would make more sense to me. We'd have to deprecate rebase --autostash in favor of rebase --stash to change it but it if we want to change the name it would be better now before adding --autostash to merge and pull - what do you think?

Even though I agree with you that --autostash would be better named as --stash, I feel that it's worse than having argument names that perform the same functionality but with different names. So I'd be inclined to keep --autostash.

Quoted 9 lines
Quoted 9 lines
	diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
	index e0c8a0bad4..af5db58e16 100755
	--- a/t/t7600-merge.sh
	+++ b/t/t7600-merge.sh
	@@ -727,6 +727,33 @@ test_expect_success 'conflicted merge with --autostash, --abort restores stash'
		test_cmp file.1 file
	 '
Quoted 6 lines
Quoted 3 lines
	+test_expect_success 'completed merge with --no-commit and --autostash' '
	+	git reset --hard c1 &&
	+	git merge-file file file.orig file.9 &&

Is this a complicated way of getting some unstaged changes so we can stash them or have I missed something?

Yes, that's exactly what this does.

Quoted 41 lines

Best Wishes

Phillip

Quoted 36 lines
	+	git diff >expect &&
	+	git merge --no-commit --autostash c2 &&
	+	git stash show -p MERGE_AUTOSTASH >actual &&
	+	test_cmp expect actual &&
	+	git commit 2>err &&
	+	test_i18ngrep "Applied autostash." err &&
	+	git show HEAD:file >merge-result &&
	+	test_cmp result.1-5 merge-result &&
	+	test_cmp result.1-5-9 file
	+'
	+
	+test_expect_success 'aborted merge with --no-commit and --autostash' '
	+	git reset --hard c1 &&
	+	git merge-file file file.orig file.9 &&
	+	git diff >expect &&
	+	git merge --no-commit --autostash c2 &&
	+	git stash show -p MERGE_AUTOSTASH >actual &&
	+	test_cmp expect actual &&
	+	git merge --abort 2>err &&
	+	test_i18ngrep "Applied autostash." err &&
	+	git diff >actual &&
	+	test_cmp expect actual
	+'
	+
	 test_expect_success 'merge with conflicted --autostash changes' '
		git reset --hard c1 &&
		git merge-file file file.orig file.9y &&

Thanks,

Denton

Quoted 4 lines

Best Wishes

Phillip

author
#52 lore raw

v3 cover merge: learn --autostash

In reply to Denton Liu

Sorry for the delay in rerolling this. It's been quite long since I've had free time. This probaly requires a fresh review since quite a lot has changed since I posted my last version.

Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.

The majority of this patchset is spent refactoring. This is so that the actual implementation in merge is done as simply as possible.

Changes since v2:

* Change patch 1 to refer to ASCII order[2]

* Make read_oneliner() accept a collective flags arg instead of two separate args[3]

* git reset --hard now leaves the worktree clean[4]

* Rebased against 'master' since the base is quite old and a new release is coming out soon

Changes since v1:

* Completely changed how the refactoring was done

* More tests and documentation

[1]: https://github.com/gitgitgadget/git/issues/394
[2]: https://lore.kernel.org/git/[email protected]/
[3]: https://lore.kernel.org/git/[email protected]/
[4]: https://lore.kernel.org/git/20200123042906.GA29009@generichostname/

Denton Liu (19):
Makefile: ASCII-sort += lists
t7600: use test_write_lines()
sequencer: use file strbuf for read_oneliner()
sequencer: make read_oneliner() accept flags
sequencer: configurably warn on non-existent files
sequencer: make read_oneliner() extern
rebase: use read_oneliner()
sequencer: make apply_rebase() accept a path
rebase: use apply_autostash() from sequencer.c
rebase: generify reset_head()
reset: extract reset_head() from rebase
rebase: extract create_autostash()
rebase: generify create_autostash()
sequencer: extract perform_autostash() from rebase
sequencer: unlink autostash in apply_autostash()
sequencer: implement save_autostash()
merge: teach --autostash option
t5520: make test_pull_autostash() accept expect_parent_num
pull: pass --autostash to merge

 Documentation/config/merge.txt  |  10 ++
 Documentation/git-pull.txt      |   9 -
 Documentation/merge-options.txt |   8 +
 Makefile                        |  78 ++++----
 branch.c                        |   1 +
 builtin/commit.c                |   2 +
 builtin/merge.c                 |  17 ++
 builtin/pull.c                  |   9 +-
 builtin/rebase.c                | 304 +++++---------------------------
 builtin/reset.c                 |   7 +-
 path.c                          |   1 +
 path.h                          |   4 +-
 reset.c                         | 140 +++++++++++++++
 reset.h                         |  20 +++
 sequencer.c                     | 152 +++++++++++-----
 sequencer.h                     |  19 ++
 t/t3033-merge-toplevel.sh       |  22 +++
 t/t5520-pull.sh                 |  57 ++++--
 t/t7600-merge.sh                | 126 +++++++++++--
 19 files changed, 604 insertions(+), 382 deletions(-)
 create mode 100644 reset.c
 create mode 100644 reset.h

Range-diff against v2:
1: 31055cdac4 ! 1: 77506a39d0 Makefile: alphabetically sort += lists
@@ Metadata
Author: Denton Liu <[email protected]>

## Commit message ##
- Makefile: alphabetically sort += lists
+ Makefile: ASCII-sort += lists

There are many += lists in the Makefile and, over time, they have gotten
- slightly out of order, alphabetically. Alphabetically sort all += lists
- to bring them back in order.
+ slightly out of ASCII order. Sort all += lists to bring them back in
+ order.

- Note that if we omit file prefixes, the lists aren't sorted in strictly
- alphabetical order (e.g. archive.o comes after archive-zip.o instead of
- before archive-tar.o). This is intentional because the purpose of
- maintaining the sorted list is to ensure line insertions are
- deterministic. As a result, we want to ensure that anybody can easily
- reproduce the sort order and that is very simple when we don't have to
- treat the prefix specially.
+ ASCII sorting was chosen over strict alphabetical order even though, if
+ we omit file prefixes, the lists aren't sorted in strictly alphabetical
+ order (e.g. archive.o comes after archive-zip.o instead of before
+ archive-tar.o). This is intentional because the purpose of maintaining
+ the sorted list is to ensure line insertions are deterministic. By using
+ ASCII ordering, it is more easily mechanically reproducible in the
+ future, such as by using :sort in Vim.

This patch is best viewed with --color-moved.

@@ Makefile: LIB_OBJS += mailinfo.o
LIB_OBJS += patch-delta.o
LIB_OBJS += patch-ids.o
LIB_OBJS += path.o
-@@ Makefile: LIB_OBJS += range-diff.o
+@@ Makefile: LIB_OBJS += quote.o
+ LIB_OBJS += range-diff.o
LIB_OBJS += reachable.o
LIB_OBJS += read-cache.o
+-LIB_OBJS += rebase.o
LIB_OBJS += rebase-interactive.o
++LIB_OBJS += rebase.o
+LIB_OBJS += ref-filter.o
LIB_OBJS += reflog-walk.o
LIB_OBJS += refs.o

 2:  2e0020b3e3 =  2:  70699b8b73 t7600: use test_write_lines()
 3:  de79e27c17 =  3:  7c37777f07 sequencer: use file strbuf for read_oneliner()
 4:  fd547aab49 <  -:  ---------- sequencer: configurably warn on non-existent files
 -:  ---------- >  4:  4f97086828 sequencer: make read_oneliner() accept flags
 -:  ---------- >  5:  e42f4bdd4d sequencer: configurably warn on non-existent files
 5:  819fb2203b !  6:  e4d0c2d902 sequencer: make read_oneliner() extern

@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
return 0;
}

+-#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
+-#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
+-
-/*
- * Reads a file that was presumably written by a shell script, i.e. with an
- * end-of-line marker that needs to be stripped.
@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
- *
- * Returns 1 if the file was read, 0 if it could not be read or does not exist.
- */
--static int read_oneliner(struct strbuf *buf, const char *path,
-- int skip_if_empty, int warn_nonexistence)
-+int read_oneliner(struct strbuf *buf, const char *path,
-+ int skip_if_empty, int warn_nonexistence)
+-static int read_oneliner(struct strbuf *buf,
++int read_oneliner(struct strbuf *buf,
+ const char *path, unsigned flags)
{
int ret = 0;
- struct strbuf file_buf = STRBUF_INIT;

## sequencer.h ##
@@ sequencer.h: void print_commit_summary(struct repository *repo,
const struct object_id *oid, unsigned int flags);

++#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
++#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
++
+/*
+ * Reads a file that was presumably written by a shell script, i.e. with an
+ * end-of-line marker that needs to be stripped.
@@ sequencer.h: void print_commit_summary(struct repository *repo,
+ *
+ * Returns 1 if the file was read, 0 if it could not be read.
+ */
-+int read_oneliner(struct strbuf *buf, const char *path,
-+ int skip_if_empty, int warn_nonexistence);
++int read_oneliner(struct strbuf *buf,
++ const char *path, unsigned flags);
int read_author_script(const char *path, char **name, char **email, char **date,
int allow_missing);
void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
6: 06c5bd48d7 ! 7: d07a50c5b2 rebase: use read_oneliner()

    @@ Commit message
         of read_oneliner(), reduce code duplication by replacing read_one() with
         read_oneliner().
     
    -    This was done with the following Coccinelle script:
    +    This was done with the following Coccinelle script
     
                 @@
                 expression a, b;
                 @@
                 - read_one(a, b)
    -            + !read_oneliner(b, a, 0, 1)
    +            + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)
    +
    +    and long lines were manually broken up.
     
      ## builtin/rebase.c ##
     @@ builtin/rebase.c: static const char *state_dir_path(const char *filename, struct rebase_options *o
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      
     -	if (read_one(state_dir_path("head-name", opts), &head_name) ||
     -	    read_one(state_dir_path("onto", opts), &buf))
    -+	if (!read_oneliner(&head_name, state_dir_path("head-name", opts), 0, 1) ||
    -+	    !read_oneliner(&buf, state_dir_path("onto", opts), 0, 1))
    ++	if (!read_oneliner(&head_name, state_dir_path("head-name", opts),
    ++			   READ_ONELINER_WARN_NON_EXISTENCE) ||
    ++	    !read_oneliner(&buf, state_dir_path("onto", opts),
    ++			   READ_ONELINER_WARN_NON_EXISTENCE))
      		return -1;
      	opts->head_name = starts_with(head_name.buf, "refs/") ?
      		xstrdup(head_name.buf) : NULL;
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      	strbuf_reset(&buf);
      	if (file_exists(state_dir_path("orig-head", opts))) {
     -		if (read_one(state_dir_path("orig-head", opts), &buf))
    -+		if (!read_oneliner(&buf, state_dir_path("orig-head", opts), 0, 1))
    ++		if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
    ++				   READ_ONELINER_WARN_NON_EXISTENCE))
      			return -1;
     -	} else if (read_one(state_dir_path("head", opts), &buf))
    -+	} else if (!read_oneliner(&buf, state_dir_path("head", opts), 0, 1))
    ++	} else if (!read_oneliner(&buf, state_dir_path("head", opts),
    ++				  READ_ONELINER_WARN_NON_EXISTENCE))
      		return -1;
      	if (get_oid(buf.buf, &opts->orig_head))
      		return error(_("invalid orig-head: '%s'"), buf.buf);
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
     -			    &buf))
    -+		if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts), 0, 1))
    ++		if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
    ++				   READ_ONELINER_WARN_NON_EXISTENCE))
      			return -1;
      		if (!strcmp(buf.buf, "--rerere-autoupdate"))
      			opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("gpg_sign_opt", opts),
     -			    &buf))
    -+		if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts), 0, 1))
    ++		if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
    ++				   READ_ONELINER_WARN_NON_EXISTENCE))
      			return -1;
      		free(opts->gpg_sign_opt);
      		opts->gpg_sign_opt = xstrdup(buf.buf);
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      	if (file_exists(state_dir_path("strategy", opts))) {
      		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("strategy", opts), &buf))
    -+		if (!read_oneliner(&buf, state_dir_path("strategy", opts), 0, 1))
    ++		if (!read_oneliner(&buf, state_dir_path("strategy", opts),
    ++				   READ_ONELINER_WARN_NON_EXISTENCE))
      			return -1;
      		free(opts->strategy);
      		opts->strategy = xstrdup(buf.buf);
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      	if (file_exists(state_dir_path("strategy_opts", opts))) {
      		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("strategy_opts", opts), &buf))
    -+		if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts), 0, 1))
    ++		if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
    ++				   READ_ONELINER_WARN_NON_EXISTENCE))
      			return -1;
      		free(opts->strategy_opts);
      		opts->strategy_opts = xstrdup(buf.buf);
    @@ builtin/rebase.c: static int apply_autostash(struct rebase_options *opts)
      		return 0;
      
     -	if (read_one(path, &autostash))
    -+	if (!read_oneliner(&autostash, path, 0, 1))
    ++	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
      		return error(_("Could not read '%s'"), path);
      	/* Ensure that the hash is not mistaken for a number */
      	strbuf_addstr(&autostash, "^0");
 7:  12c37d11ba !  8:  5bce4aeb96 sequencer: make apply_rebase() accept a path
    @@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
      	struct child_process child = CHILD_PROCESS_INIT;
      	int ret = 0;
      
    --	if (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1, 0)) {
    -+	if (!read_oneliner(&stash_sha1, path, 1, 0)) {
    +-	if (!read_oneliner(&stash_sha1, rebase_path_autostash(),
    ++	if (!read_oneliner(&stash_sha1, path,
    + 			   READ_ONELINER_SKIP_IF_EMPTY)) {
      		strbuf_release(&stash_sha1);
      		return 0;
    - 	}
     @@ sequencer.c: static int checkout_onto(struct repository *r, struct replay_opts *opts,
      		return error(_("%s: not a valid OID"), orig_head);

8: 893a2f9825 ! 9: 03bdaeebc9 rebase: use apply_autostash() from sequencer.c
@@ builtin/rebase.c: static int rebase_write_basic_state(struct rebase_options *opt
- if (!file_exists(path))
- return 0;
-
-- if (!read_oneliner(&autostash, path, 0, 1))
+- if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
- return error(_("Could not read '%s'"), path);
- /* Ensure that the hash is not mistaken for a number */
- strbuf_addstr(&autostash, "^0");
@@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,

## sequencer.h ##
@@ sequencer.h: void commit_post_rewrite(struct repository *r,
- int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
- const char *commit);
+ const struct commit *current_head,
+ const struct object_id *new_head);

+int apply_autostash(const char *path);
+
9: 8e3c0c0aec ! 10: c35c5bdb8c rebase: generify reset_head()

    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
      				strbuf_addf(&buf, "%s: checkout %s",
      					    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
      					    options.switch_to);
    --				if (reset_head(&oid, "checkout",
    -+				if (reset_head(the_repository, &oid, "checkout",
    +-				if (reset_head(&options.orig_head, "checkout",
    ++				if (reset_head(the_repository,
    ++					       &options.orig_head, "checkout",
      					       options.head_name,
      					       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
     -					       NULL, buf.buf) < 0) {
10:  981969d230 = 11:  a14a2e0849 reset: extract reset_head() from rebase
11:  1b5bbe66dc = 12:  1dbae3dfbf rebase: extract create_autostash()
12:  bba7586cba = 13:  fffc038cd6 rebase: generify create_autostash()
13:  2ff189692f ! 14:  bf55b86e2b sequencer: extract perform_autostash() from rebase

@@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,

## sequencer.h ##
@@ sequencer.h: void commit_post_rewrite(struct repository *r,
- int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
- const char *commit);
+ const struct commit *current_head,
+ const struct object_id *new_head);

+void create_autostash(struct repository *r, const char *path,
+ const char *default_reflog_action);

14:  2b16711680 = 15:  d5af261eb8 sequencer: unlink autostash in apply_autostash()
 -:  ---------- > 16:  e21e2c6416 sequencer: implement save_autostash()
15:  6e987052c0 ! 17:  9e3d4393ca merge: teach --autostash option
    @@ Commit message
                 git fetch ...
                 git merge --autostash FETCH_HEAD
     
    +    When `git reset --hard` is run to abort a merge, the working tree will
    +    be left in a clean state, as expected, with the autostash pushed onto
    +    the stash stack.
    +
         Suggested-by: Alban Gruin <[email protected]>
     
      ## Documentation/config/merge.txt ##
    @@ builtin/merge.c: static void finish(struct commit *head_commit,
      		squash_message(head_commit, remoteheads);
      	} else {
     @@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void *cb)
    - 	} else if (!strcmp(k, "commit.gpgsign")) {
    - 		sign_commit = git_config_bool(k, v) ? "" : NULL;
      		return 0;
    + 	} else if (!strcmp(k, "gpg.mintrustlevel")) {
    + 		check_trust_level = 0;
     +	} else if (!strcmp(k, "merge.autostash")) {
     +		autostash = git_config_bool(k, v);
     +		return 0;
    @@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
      	git_committer_info(IDENT_STRICT);
      
     
    + ## builtin/reset.c ##
    +@@
    + #include "cache-tree.h"
    + #include "submodule.h"
    + #include "submodule-config.h"
    ++#include "sequencer.h"
    + 
    + #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
    + 
    +@@ builtin/reset.c: int cmd_reset(int argc, const char **argv, const char *prefix)
    + 		if (reset_type == HARD && !update_ref_status && !quiet)
    + 			print_new_head_line(lookup_commit_reference(the_repository, &oid));
    + 	}
    +-	if (!pathspec.nr)
    ++	if (!pathspec.nr) {
    ++		if (reset_type == HARD)
    ++			save_autostash(git_path_merge_autostash(the_repository));
    ++
    + 		remove_branch_state(the_repository, 0);
    ++	}
    + 
    + 	return update_ref_status;
    + }
    +
      ## path.c ##
     @@ path.c: REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
      REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
    @@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '
     +	test_cmp file.1 file
     +'
     +
    ++test_expect_success 'completed merge with --no-commit and --autostash' '
    ++	git reset --hard c1 &&
    ++	git merge-file file file.orig file.9 &&
    ++	git diff >expect &&
    ++	git merge --no-commit --autostash c2 &&
    ++	git stash show -p MERGE_AUTOSTASH >actual &&
    ++	test_cmp expect actual &&
    ++	git commit 2>err &&
    ++	test_i18ngrep "Applied autostash." err &&
    ++	git show HEAD:file >merge-result &&
    ++	test_cmp result.1-5 merge-result &&
    ++	test_cmp result.1-5-9 file
    ++'
    ++
    ++test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '
    ++	git reset --hard c1 &&
    ++	git merge-file file file.orig file.9 &&
    ++	git diff >expect &&
    ++	git merge --no-commit --autostash c2 &&
    ++	git stash show -p MERGE_AUTOSTASH >actual &&
    ++	test_cmp expect actual &&
    ++	git merge --abort 2>err &&
    ++	test_i18ngrep "Applied autostash." err &&
    ++	git diff >actual &&
    ++	test_cmp expect actual
    ++'
    ++
    ++test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '
    ++	git reset --hard c1 &&
    ++	git merge-file file file.orig file.9 &&
    ++	git diff >expect &&
    ++	git merge --no-commit --autostash c2 &&
    ++	git stash show -p MERGE_AUTOSTASH >actual &&
    ++	test_cmp expect actual &&
    ++	git reset --hard 2>err &&
    ++	test_i18ngrep "Autostash exists; creating a new stash entry." err &&
    ++	git diff --exit-code
    ++'
    ++
     +test_expect_success 'merge with conflicted --autostash changes' '
     +	git reset --hard c1 &&
     +	git merge-file file file.orig file.9y &&
16:  c70643ca9f = 18:  72aa56516e t5520: make test_pull_autostash() accept expect_parent_num
17:  6ebae1d758 = 19:  17caf6d66f pull: pass --autostash to merge
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#53 lore raw

PATCH v3 1/19 Makefile: ASCII-sort += lists

There are many += lists in the Makefile and, over time, they have gotten slightly out of ASCII order. Sort all += lists to bring them back in order.

ASCII sorting was chosen over strict alphabetical order even though, if we omit file prefixes, the lists aren't sorted in strictly alphabetical order (e.g. archive.o comes after archive-zip.o instead of before archive-tar.o). This is intentional because the purpose of maintaining the sorted list is to ensure line insertions are deterministic. By using ASCII ordering, it is more easily mechanically reproducible in the future, such as by using :sort in Vim.

This patch is best viewed with --color-moved.

---
 Makefile | 77 ++++++++++++++++++++++++++++----------------------------
 1 file changed, 39 insertions(+), 38 deletions(-)
Makefile +39 -38
index 9804a0758b..f709fc3f77 100644
@@ -604,12 +604,12 @@ unexport CDPATH
 SCRIPT_SH += git-bisect.sh
 SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
+SCRIPT_SH += git-legacy-stash.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-quiltimport.sh
-SCRIPT_SH += git-legacy-stash.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-web--browse.sh
@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
 SCRIPT_LIB += git-rebase--preserve-merges
-SCRIPT_LIB += git-sh-setup
 SCRIPT_LIB += git-sh-i18n
+SCRIPT_LIB += git-sh-setup
 
 SCRIPT_PERL += git-add--interactive.perl
 SCRIPT_PERL += git-archimport.perl
@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o
 PROGRAM_OBJS += fast-import.o
 PROGRAM_OBJS += http-backend.o
 PROGRAM_OBJS += imap-send.o
+PROGRAM_OBJS += remote-testsvn.o
 PROGRAM_OBJS += sh-i18n--envsubst.o
 PROGRAM_OBJS += shell.o
-PROGRAM_OBJS += remote-testsvn.o
 
 # Binary suffix, set to .exe for Windows builds
 X =
@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
 TEST_BUILTINS_OBJS += test-example-decorate.o
 TEST_BUILTINS_OBJS += test-genrandom.o
 TEST_BUILTINS_OBJS += test-genzeros.o
+TEST_BUILTINS_OBJS += test-hash-speed.o
 TEST_BUILTINS_OBJS += test-hash.o
 TEST_BUILTINS_OBJS += test-hashmap.o
-TEST_BUILTINS_OBJS += test-hash-speed.o
 TEST_BUILTINS_OBJS += test-index-version.o
 TEST_BUILTINS_OBJS += test-json-writer.o
 TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@ -737,8 +737,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o
 TEST_BUILTINS_OBJS += test-run-command.o
 TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
 TEST_BUILTINS_OBJS += test-serve-v2.o
-TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha1-array.o
+TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha256.o
 TEST_BUILTINS_OBJS += test-sigchain.o
 TEST_BUILTINS_OBJS += test-strcmp-offset.o
@@ -748,10 +748,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
 TEST_BUILTINS_OBJS += test-subprocess.o
 TEST_BUILTINS_OBJS += test-trace2.o
 TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
-TEST_BUILTINS_OBJS += test-xml-encode.o
 TEST_BUILTINS_OBJS += test-wildmatch.o
 TEST_BUILTINS_OBJS += test-windows-named-pipe.o
 TEST_BUILTINS_OBJS += test-write-cache.o
+TEST_BUILTINS_OBJS += test-xml-encode.o
 
 # Do not add more tests here unless they have extra dependencies. Add
 # them in TEST_BUILTINS_OBJS above.
@@ -788,10 +788,10 @@ OTHER_PROGRAMS = git$X
 
 # what test wrappers are needed and 'install' will install, in bindir
 BINDIR_PROGRAMS_NEED_X += git
-BINDIR_PROGRAMS_NEED_X += git-upload-pack
 BINDIR_PROGRAMS_NEED_X += git-receive-pack
-BINDIR_PROGRAMS_NEED_X += git-upload-archive
 BINDIR_PROGRAMS_NEED_X += git-shell
+BINDIR_PROGRAMS_NEED_X += git-upload-archive
+BINDIR_PROGRAMS_NEED_X += git-upload-pack
 
 BINDIR_PROGRAMS_NO_X += git-cvsserver
 
@@ -831,9 +831,9 @@ LIB_OBJS += advice.o
 LIB_OBJS += alias.o
 LIB_OBJS += alloc.o
 LIB_OBJS += apply.o
-LIB_OBJS += archive.o
 LIB_OBJS += archive-tar.o
 LIB_OBJS += archive-zip.o
+LIB_OBJS += archive.o
 LIB_OBJS += argv-array.o
 LIB_OBJS += attr.o
 LIB_OBJS += base85.o
@@ -849,9 +849,9 @@ LIB_OBJS += checkout.o
 LIB_OBJS += color.o
 LIB_OBJS += column.o
 LIB_OBJS += combine-diff.o
-LIB_OBJS += commit.o
 LIB_OBJS += commit-graph.o
 LIB_OBJS += commit-reach.o
+LIB_OBJS += commit.o
 LIB_OBJS += compat/obstack.o
 LIB_OBJS += compat/terminal.o
 LIB_OBJS += config.o
@@ -865,17 +865,17 @@ LIB_OBJS += ctype.o
 LIB_OBJS += date.o
 LIB_OBJS += decorate.o
 LIB_OBJS += delta-islands.o
+LIB_OBJS += diff-delta.o
+LIB_OBJS += diff-lib.o
+LIB_OBJS += diff-no-index.o
+LIB_OBJS += diff.o
 LIB_OBJS += diffcore-break.o
 LIB_OBJS += diffcore-delta.o
 LIB_OBJS += diffcore-order.o
 LIB_OBJS += diffcore-pickaxe.o
 LIB_OBJS += diffcore-rename.o
-LIB_OBJS += diff-delta.o
-LIB_OBJS += diff-lib.o
-LIB_OBJS += diff-no-index.o
-LIB_OBJS += diff.o
-LIB_OBJS += dir.o
 LIB_OBJS += dir-iterator.o
+LIB_OBJS += dir.o
 LIB_OBJS += editor.o
 LIB_OBJS += entry.o
 LIB_OBJS += environment.o
@@ -893,7 +893,6 @@ LIB_OBJS += gpg-interface.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hashmap.o
-LIB_OBJS += linear-assignment.o
 LIB_OBJS += help.o
 LIB_OBJS += hex.o
 LIB_OBJS += ident.o
@@ -903,9 +902,10 @@ LIB_OBJS += kwset.o
 LIB_OBJS += levenshtein.o
 LIB_OBJS += line-log.o
 LIB_OBJS += line-range.o
-LIB_OBJS += list-objects.o
-LIB_OBJS += list-objects-filter.o
+LIB_OBJS += linear-assignment.o
 LIB_OBJS += list-objects-filter-options.o
+LIB_OBJS += list-objects-filter.o
+LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
@@ -914,31 +914,31 @@ LIB_OBJS += mailinfo.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += mem-pool.o
-LIB_OBJS += merge.o
 LIB_OBJS += merge-blobs.o
 LIB_OBJS += merge-recursive.o
+LIB_OBJS += merge.o
 LIB_OBJS += mergesort.o
 LIB_OBJS += midx.o
 LIB_OBJS += name-hash.o
 LIB_OBJS += negotiator/default.o
 LIB_OBJS += negotiator/skipping.o
-LIB_OBJS += notes.o
 LIB_OBJS += notes-cache.o
 LIB_OBJS += notes-merge.o
 LIB_OBJS += notes-utils.o
+LIB_OBJS += notes.o
 LIB_OBJS += object.o
 LIB_OBJS += oidmap.o
 LIB_OBJS += oidset.o
-LIB_OBJS += packfile.o
-LIB_OBJS += pack-bitmap.o
 LIB_OBJS += pack-bitmap-write.o
+LIB_OBJS += pack-bitmap.o
 LIB_OBJS += pack-check.o
 LIB_OBJS += pack-objects.o
 LIB_OBJS += pack-revindex.o
 LIB_OBJS += pack-write.o
+LIB_OBJS += packfile.o
 LIB_OBJS += pager.o
-LIB_OBJS += parse-options.o
 LIB_OBJS += parse-options-cb.o
+LIB_OBJS += parse-options.o
 LIB_OBJS += patch-delta.o
 LIB_OBJS += patch-ids.o
 LIB_OBJS += path.o
@@ -955,8 +955,9 @@ LIB_OBJS += quote.o
 LIB_OBJS += range-diff.o
 LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
-LIB_OBJS += rebase.o
 LIB_OBJS += rebase-interactive.o
+LIB_OBJS += rebase.o
+LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/files-backend.o
@@ -964,7 +965,6 @@ LIB_OBJS += refs/iterator.o
 LIB_OBJS += refs/packed-backend.o
 LIB_OBJS += refs/ref-cache.o
 LIB_OBJS += refspec.o
-LIB_OBJS += ref-filter.o
 LIB_OBJS += remote.o
 LIB_OBJS += replace-object.o
 LIB_OBJS += repo-settings.o
@@ -979,8 +979,8 @@ LIB_OBJS += serve.o
 LIB_OBJS += server-info.o
 LIB_OBJS += setup.o
 LIB_OBJS += sha1-array.o
-LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1-file.o
+LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1-name.o
 LIB_OBJS += shallow.o
 LIB_OBJS += sideband.o
@@ -990,9 +990,9 @@ LIB_OBJS += stable-qsort.o
 LIB_OBJS += strbuf.o
 LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
-LIB_OBJS += submodule.o
-LIB_OBJS += submodule-config.o
 LIB_OBJS += sub-process.o
+LIB_OBJS += submodule-config.o
+LIB_OBJS += submodule.o
 LIB_OBJS += symlinks.o
 LIB_OBJS += tag.o
 LIB_OBJS += tempfile.o
@@ -1011,11 +1011,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o
 LIB_OBJS += trace2/tr2_tgt_perf.o
 LIB_OBJS += trace2/tr2_tls.o
 LIB_OBJS += trailer.o
-LIB_OBJS += transport.o
 LIB_OBJS += transport-helper.o
+LIB_OBJS += transport.o
 LIB_OBJS += tree-diff.o
-LIB_OBJS += tree.o
 LIB_OBJS += tree-walk.o
+LIB_OBJS += tree.o
 LIB_OBJS += unpack-trees.o
 LIB_OBJS += upload-pack.o
 LIB_OBJS += url.o
@@ -1055,9 +1055,9 @@ BUILTIN_OBJS += builtin/checkout.o
 BUILTIN_OBJS += builtin/clean.o
 BUILTIN_OBJS += builtin/clone.o
 BUILTIN_OBJS += builtin/column.o
+BUILTIN_OBJS += builtin/commit-graph.o
 BUILTIN_OBJS += builtin/commit-tree.o
 BUILTIN_OBJS += builtin/commit.o
-BUILTIN_OBJS += builtin/commit-graph.o
 BUILTIN_OBJS += builtin/config.o
 BUILTIN_OBJS += builtin/count-objects.o
 BUILTIN_OBJS += builtin/credential.o
@@ -1088,13 +1088,13 @@ BUILTIN_OBJS += builtin/ls-remote.o
 BUILTIN_OBJS += builtin/ls-tree.o
 BUILTIN_OBJS += builtin/mailinfo.o
 BUILTIN_OBJS += builtin/mailsplit.o
-BUILTIN_OBJS += builtin/merge.o
 BUILTIN_OBJS += builtin/merge-base.o
 BUILTIN_OBJS += builtin/merge-file.o
 BUILTIN_OBJS += builtin/merge-index.o
 BUILTIN_OBJS += builtin/merge-ours.o
 BUILTIN_OBJS += builtin/merge-recursive.o
 BUILTIN_OBJS += builtin/merge-tree.o
+BUILTIN_OBJS += builtin/merge.o
 BUILTIN_OBJS += builtin/mktag.o
 BUILTIN_OBJS += builtin/mktree.o
 BUILTIN_OBJS += builtin/multi-pack-index.o
@@ -1114,9 +1114,9 @@ BUILTIN_OBJS += builtin/read-tree.o
 BUILTIN_OBJS += builtin/rebase.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
-BUILTIN_OBJS += builtin/remote.o
 BUILTIN_OBJS += builtin/remote-ext.o
 BUILTIN_OBJS += builtin/remote-fd.o
+BUILTIN_OBJS += builtin/remote.o
 BUILTIN_OBJS += builtin/repack.o
 BUILTIN_OBJS += builtin/replace.o
 BUILTIN_OBJS += builtin/rerere.o
@@ -2335,16 +2335,16 @@ reconfigure config.mak.autogen: config.status
 endif
 
 XDIFF_OBJS += xdiff/xdiffi.o
-XDIFF_OBJS += xdiff/xprepare.o
-XDIFF_OBJS += xdiff/xutils.o
 XDIFF_OBJS += xdiff/xemit.o
+XDIFF_OBJS += xdiff/xhistogram.o
 XDIFF_OBJS += xdiff/xmerge.o
 XDIFF_OBJS += xdiff/xpatience.o
-XDIFF_OBJS += xdiff/xhistogram.o
+XDIFF_OBJS += xdiff/xprepare.o
+XDIFF_OBJS += xdiff/xutils.o
 
+VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/line_buffer.o
 VCSSVN_OBJS += vcs-svn/sliding_window.o
-VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/svndiff.o
 VCSSVN_OBJS += vcs-svn/svndump.o
 
@@ -3152,9 +3152,10 @@ endif
 #
 ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS)
 ALL_COMMANDS += git
+ALL_COMMANDS += git-citool
+ALL_COMMANDS += git-gui
 ALL_COMMANDS += gitk
 ALL_COMMANDS += gitweb
-ALL_COMMANDS += git-gui git-citool
 
 .PHONY: check-docs
 check-docs::
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#54 lore raw

PATCH v3 2/19 t7600: use test_write_lines()

In reply to Denton Liu

In t7600, we were rewriting printf '%s\n' ... to create files from parameters, one per line. However, we already have a function that wraps this for us: test_write_lines(). Rewrite these instances to use that function instead of open coding it.

---
 t/t7600-merge.sh | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
t/t7600-merge.sh +11 -11
index 132608879a..4fa0ef8e3b 100755
@@ -29,15 +29,15 @@ Testing basic merge operations/option parsing.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
-printf '%s\n' 1 2 3 4 5 6 7 8 9 >file
-printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >file.1
-printf '%s\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5
-printf '%s\n' 1 2 3 4 5 6 7 8 '9 X' >file.9
-printf '%s\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y
-printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >result.1
-printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
-printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
-printf '%s\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z
+test_write_lines 1 2 3 4 5 6 7 8 9 >file
+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1
+test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5
+test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9
+test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y
+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1
+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
+test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z
 
 create_merge_msgs () {
 	echo "Merge tag 'c2'" >msg.1-5 &&
@@ -81,7 +81,7 @@ verify_head () {
 }
 
 verify_parents () {
-	printf '%s\n' "$@" >parents.expected &&
+	test_write_lines "$@" >parents.expected &&
 	>parents.actual &&
 	i=1 &&
 	while test $i -le $#
@@ -95,7 +95,7 @@ verify_parents () {
 }
 
 verify_mergeheads () {
-	printf '%s\n' "$@" >mergehead.expected &&
+	test_write_lines "$@" >mergehead.expected &&
 	while read sha1 rest
 	do
 		git rev-parse $sha1
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#55 lore raw

PATCH v3 3/19 sequencer: use file strbuf for read_oneliner()

In reply to Denton Liu

In the original read_oneliner logic, we duplicated the logic for strbuf_trim_trailing_newline() with one exception: instead of preventing buffer accesses below index 0, it would prevent buffer accesses below index orig_len. Although this is correct, it isn't worth having the duplicated logic around.

Add a second strbuf to which files are read and run strbuf_trim_trailing_newline() directly on this strbuf then concatenate this strbuf with the argument strbuf at the end of the function. The function's external behaviour is unchanged.

---
 sequencer.c | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)
sequencer.c +13 -11
index e528225e78..c49fe76fe6 100644
@@ -431,26 +431,28 @@ static int write_message(const void *buf, size_t len, const char *filename,
 static int read_oneliner(struct strbuf *buf,
 	const char *path, int skip_if_empty)
 {
-	int orig_len = buf->len;
+	int ret = 0;
+	struct strbuf file_buf = STRBUF_INIT;
 
 	if (!file_exists(path))
 		return 0;
 
-	if (strbuf_read_file(buf, path, 0) < 0) {
+	if (strbuf_read_file(&file_buf, path, 0) < 0) {
 		warning_errno(_("could not read '%s'"), path);
-		return 0;
+		goto done;
 	}
 
-	if (buf->len > orig_len && buf->buf[buf->len - 1] == '\n') {
-		if (--buf->len > orig_len && buf->buf[buf->len - 1] == '\r')
-			--buf->len;
-		buf->buf[buf->len] = '\0';
-	}
+	strbuf_trim_trailing_newline(&file_buf);
 
-	if (skip_if_empty && buf->len == orig_len)
-		return 0;
+	if (skip_if_empty && !file_buf.len)
+		goto done;
 
-	return 1;
+	strbuf_addbuf(buf, &file_buf);
+	ret = 1;
+
+done:
+	strbuf_release(&file_buf);
+	return ret;
 }
 
 static struct tree *empty_tree(struct repository *r)
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#57 lore raw

PATCH v3 4/19 sequencer: make read_oneliner() accept flags

In reply to Denton Liu

In a future commit, we will need read_oneliner() to accept flags other than just skip_if_empty. Instead of having an argument for each flag, teach read_oneliner() to accept the bitfield flags instead. For now, only recognize the READ_ONELINER_SKIP_IF_EMPTY flag. More flags will be added in a future commit.

The result of this is that parallel topics which introduce invocations of read_oneliner() will still be compatible with this new function signature since, instead of passing 1 or 0 for skip_if_empty, they'll be passing 1 or 0 to flags, which gives equivalent behavior.

Mechanically fix up invocations of read_oneliner() with the following spatch

	@@
	expression a, b;
	@@
	  read_oneliner(a, b,
	- 1
	+ READ_ONELINER_SKIP_IF_EMPTY
	  )

and manually break up long lines in the result.

---
 sequencer.c | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)
sequencer.c +14 -7
index c49fe76fe6..abb2a21e9d 100644
@@ -419,6 +419,8 @@ static int write_message(const void *buf, size_t len, const char *filename,
 	return 0;
 }
 
+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
+
 /*
  * Reads a file that was presumably written by a shell script, i.e. with an
  * end-of-line marker that needs to be stripped.
@@ -429,7 +431,7 @@ static int write_message(const void *buf, size_t len, const char *filename,
  * Returns 1 if the file was read, 0 if it could not be read or does not exist.
  */
 static int read_oneliner(struct strbuf *buf,
-	const char *path, int skip_if_empty)
+	const char *path, unsigned flags)
 {
 	int ret = 0;
 	struct strbuf file_buf = STRBUF_INIT;
@@ -444,7 +446,7 @@ static int read_oneliner(struct strbuf *buf,
 
 	strbuf_trim_trailing_newline(&file_buf);
 
-	if (skip_if_empty && !file_buf.len)
+	if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !file_buf.len)
 		goto done;
 
 	strbuf_addbuf(buf, &file_buf);
@@ -2488,7 +2490,8 @@ static int read_populate_opts(struct replay_opts *opts)
 	if (is_rebase_i(opts)) {
 		struct strbuf buf = STRBUF_INIT;
 
-		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
+		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(),
+				  READ_ONELINER_SKIP_IF_EMPTY)) {
 			if (!starts_with(buf.buf, "-S"))
 				strbuf_reset(&buf);
 			else {
@@ -2498,7 +2501,8 @@ static int read_populate_opts(struct replay_opts *opts)
 			strbuf_reset(&buf);
 		}
 
-		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
+		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(),
+				  READ_ONELINER_SKIP_IF_EMPTY)) {
 			if (!strcmp(buf.buf, "--rerere-autoupdate"))
 				opts->allow_rerere_auto = RERERE_AUTOUPDATE;
 			else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
@@ -2530,7 +2534,8 @@ static int read_populate_opts(struct replay_opts *opts)
 		strbuf_release(&buf);
 
 		if (read_oneliner(&opts->current_fixups,
-				  rebase_path_current_fixups(), 1)) {
+				  rebase_path_current_fixups(),
+				  READ_ONELINER_SKIP_IF_EMPTY)) {
 			const char *p = opts->current_fixups.buf;
 			opts->current_fixup_count = 1;
 			while ((p = strchr(p, '\n'))) {
@@ -3667,7 +3672,8 @@ static int apply_autostash(struct replay_opts *opts)
 	struct child_process child = CHILD_PROCESS_INIT;
 	int ret = 0;
 
-	if (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {
+	if (!read_oneliner(&stash_sha1, rebase_path_autostash(),
+			   READ_ONELINER_SKIP_IF_EMPTY)) {
 		strbuf_release(&stash_sha1);
 		return 0;
 	}
@@ -4291,7 +4297,8 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
 		struct strbuf buf = STRBUF_INIT;
 		struct object_id oid;
 
-		if (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&
+		if (read_oneliner(&buf, rebase_path_stopped_sha(),
+				  READ_ONELINER_SKIP_IF_EMPTY) &&
 		    !get_oid_committish(buf.buf, &oid))
 			record_in_rewritten(&oid, peek_command(&todo_list, 0));
 		strbuf_release(&buf);
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#58 lore raw

PATCH v3 5/19 sequencer: configurably warn on non-existent files

In reply to Denton Liu

In the future, we plan on externing read_oneliner(). Future users of read_oneliner() will want the ability to output warnings in the event that the path doesn't exist. Introduce the READ_ONELINER_WARN_NON_EXISTENCE flag which, if active, would issue a warning when a file doesn't exist by skipping the !file_exists() check and letting strbuf_read_file() handle that case.

---
 sequencer.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
sequencer.c +2 -1
index abb2a21e9d..92e8d38290 100644
@@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename,
 }
 
 #define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
+#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
 
 /*
  * Reads a file that was presumably written by a shell script, i.e. with an
@@ -436,7 +437,7 @@ static int read_oneliner(struct strbuf *buf,
 	int ret = 0;
 	struct strbuf file_buf = STRBUF_INIT;
 
-	if (!file_exists(path))
+	if (!(flags & READ_ONELINER_WARN_NON_EXISTENCE) && !file_exists(path))
 		return 0;
 
 	if (strbuf_read_file(&file_buf, path, 0) < 0) {
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#60 lore raw

PATCH v3 6/19 sequencer: make read_oneliner() extern

In reply to Denton Liu

The function read_oneliner() is a generally useful util function. Instead of hiding it as a static function within sequencer.c, extern it so that it can be reused by others.

---
 sequencer.c | 14 +-------------
 sequencer.h | 14 ++++++++++++++
 2 files changed, 15 insertions(+), 13 deletions(-)
sequencer.c +1 -13
index 92e8d38290..c2102cc2eb 100644
@@ -419,19 +419,7 @@ static int write_message(const void *buf, size_t len, const char *filename,
 	return 0;
 }
 
-#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
-#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
-
-/*
- * Reads a file that was presumably written by a shell script, i.e. with an
- * end-of-line marker that needs to be stripped.
- *
- * Note that only the last end-of-line marker is stripped, consistent with the
- * behavior of "$(cat path)" in a shell script.
- *
- * Returns 1 if the file was read, 0 if it could not be read or does not exist.
- */
-static int read_oneliner(struct strbuf *buf,
+int read_oneliner(struct strbuf *buf,
 	const char *path, unsigned flags)
 {
 	int ret = 0;
sequencer.h +14 -0
index 718a07426d..5c9662a60a 100644
@@ -198,6 +198,20 @@ void print_commit_summary(struct repository *repo,
 			  const struct object_id *oid,
 			  unsigned int flags);
 
+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
+#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
+
+/*
+ * Reads a file that was presumably written by a shell script, i.e. with an
+ * end-of-line marker that needs to be stripped.
+ *
+ * Note that only the last end-of-line marker is stripped, consistent with the
+ * behavior of "$(cat path)" in a shell script.
+ *
+ * Returns 1 if the file was read, 0 if it could not be read.
+ */
+int read_oneliner(struct strbuf *buf,
+	const char *path, unsigned flags);
 int read_author_script(const char *path, char **name, char **email, char **date,
 		       int allow_missing);
 void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#61 lore raw

PATCH v3 7/19 rebase: use read_oneliner()

In reply to Denton Liu

Since in sequencer.c, read_one() basically duplicates the functionality of read_oneliner(), reduce code duplication by replacing read_one() with read_oneliner().

This was done with the following Coccinelle script

	@@
	expression a, b;
	@@
	- read_one(a, b)
	+ !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)

and long lines were manually broken up.

---
 builtin/rebase.c | 37 +++++++++++++++++--------------------
 1 file changed, 17 insertions(+), 20 deletions(-)
builtin/rebase.c +17 -20
index bff53d5d16..1146463099 100644
@@ -586,15 +586,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o
 	return path.buf;
 }
 
-/* Read one file, then strip line endings */
-static int read_one(const char *path, struct strbuf *buf)
-{
-	if (strbuf_read_file(buf, path, 0) < 0)
-		return error_errno(_("could not read '%s'"), path);
-	strbuf_trim_trailing_newline(buf);
-	return 0;
-}
-
 /* Initialize the rebase options from the state directory. */
 static int read_basic_state(struct rebase_options *opts)
 {
@@ -602,8 +593,10 @@ static int read_basic_state(struct rebase_options *opts)
 	struct strbuf buf = STRBUF_INIT;
 	struct object_id oid;
 
-	if (read_one(state_dir_path("head-name", opts), &head_name) ||
-	    read_one(state_dir_path("onto", opts), &buf))
+	if (!read_oneliner(&head_name, state_dir_path("head-name", opts),
+			   READ_ONELINER_WARN_NON_EXISTENCE) ||
+	    !read_oneliner(&buf, state_dir_path("onto", opts),
+			   READ_ONELINER_WARN_NON_EXISTENCE))
 		return -1;
 	opts->head_name = starts_with(head_name.buf, "refs/") ?
 		xstrdup(head_name.buf) : NULL;
@@ -619,9 +612,11 @@ static int read_basic_state(struct rebase_options *opts)
 	 */
 	strbuf_reset(&buf);
 	if (file_exists(state_dir_path("orig-head", opts))) {
-		if (read_one(state_dir_path("orig-head", opts), &buf))
+		if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
+				   READ_ONELINER_WARN_NON_EXISTENCE))
 			return -1;
-	} else if (read_one(state_dir_path("head", opts), &buf))
+	} else if (!read_oneliner(&buf, state_dir_path("head", opts),
+				  READ_ONELINER_WARN_NON_EXISTENCE))
 		return -1;
 	if (get_oid(buf.buf, &opts->orig_head))
 		return error(_("invalid orig-head: '%s'"), buf.buf);
@@ -641,8 +636,8 @@ static int read_basic_state(struct rebase_options *opts)
 
 	if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
 		strbuf_reset(&buf);
-		if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
-			    &buf))
+		if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
+				   READ_ONELINER_WARN_NON_EXISTENCE))
 			return -1;
 		if (!strcmp(buf.buf, "--rerere-autoupdate"))
 			opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
@@ -655,8 +650,8 @@ static int read_basic_state(struct rebase_options *opts)
 
 	if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
 		strbuf_reset(&buf);
-		if (read_one(state_dir_path("gpg_sign_opt", opts),
-			    &buf))
+		if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
+				   READ_ONELINER_WARN_NON_EXISTENCE))
 			return -1;
 		free(opts->gpg_sign_opt);
 		opts->gpg_sign_opt = xstrdup(buf.buf);
@@ -664,7 +659,8 @@ static int read_basic_state(struct rebase_options *opts)
 
 	if (file_exists(state_dir_path("strategy", opts))) {
 		strbuf_reset(&buf);
-		if (read_one(state_dir_path("strategy", opts), &buf))
+		if (!read_oneliner(&buf, state_dir_path("strategy", opts),
+				   READ_ONELINER_WARN_NON_EXISTENCE))
 			return -1;
 		free(opts->strategy);
 		opts->strategy = xstrdup(buf.buf);
@@ -672,7 +668,8 @@ static int read_basic_state(struct rebase_options *opts)
 
 	if (file_exists(state_dir_path("strategy_opts", opts))) {
 		strbuf_reset(&buf);
-		if (read_one(state_dir_path("strategy_opts", opts), &buf))
+		if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
+				   READ_ONELINER_WARN_NON_EXISTENCE))
 			return -1;
 		free(opts->strategy_opts);
 		opts->strategy_opts = xstrdup(buf.buf);
@@ -724,7 +721,7 @@ static int apply_autostash(struct rebase_options *opts)
 	if (!file_exists(path))
 		return 0;
 
-	if (read_one(path, &autostash))
+	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
 		return error(_("Could not read '%s'"), path);
 	/* Ensure that the hash is not mistaken for a number */
 	strbuf_addstr(&autostash, "^0");
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#63 lore raw

PATCH v3 8/19 sequencer: make apply_rebase() accept a path

In reply to Denton Liu

In order to make apply_rebase() more generic for future extraction, make it accept a path argument so that the location from where to read the reference to the autostash commit can be customized. Remove the opts argument since it was unused before anyway.

---
 sequencer.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
sequencer.c +7 -7
index c2102cc2eb..ad40a8b3fc 100644
@@ -3655,13 +3655,13 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
 	return -1;
 }
 
-static int apply_autostash(struct replay_opts *opts)
+static int apply_autostash(const char *path)
 {
 	struct strbuf stash_sha1 = STRBUF_INIT;
 	struct child_process child = CHILD_PROCESS_INIT;
 	int ret = 0;
 
-	if (!read_oneliner(&stash_sha1, rebase_path_autostash(),
+	if (!read_oneliner(&stash_sha1, path,
 			   READ_ONELINER_SKIP_IF_EMPTY)) {
 		strbuf_release(&stash_sha1);
 		return 0;
@@ -3754,7 +3754,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,
 		return error(_("%s: not a valid OID"), orig_head);
 
 	if (run_git_checkout(r, opts, oid_to_hex(onto), action)) {
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 		sequencer_remove_state(opts);
 		return error(_("could not detach HEAD"));
 	}
@@ -4068,7 +4068,7 @@ static int pick_commits(struct repository *r,
 				run_command(&hook);
 			}
 		}
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 
 		if (!opts->quiet) {
 			if (!opts->verbose)
@@ -5077,7 +5077,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
 		todo_list_add_exec_commands(todo_list, commands);
 
 	if (count_commands(todo_list) == 0) {
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 		sequencer_remove_state(opts);
 
 		return error(_("nothing to do"));
@@ -5088,12 +5088,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
 	if (res == -1)
 		return -1;
 	else if (res == -2) {
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 		sequencer_remove_state(opts);
 
 		return -1;
 	} else if (res == -3) {
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 		sequencer_remove_state(opts);
 		todo_list_release(&new_todo);
 
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#64 lore raw

PATCH v3 9/19 rebase: use apply_autostash() from sequencer.c

In reply to Denton Liu

The apply_autostash() function in builtin/rebase.c is similar enough to the apply_autostash() function in sequencer.c that they are almost interchangeable. Make the sequencer.c version extern and use it in rebase.

The rebase version was introduced in 6defce2b02 (builtin rebase: support --autostash option, 2018-09-04) as part of the shell to C conversion. It opted to duplicate the function because, at the time, there was another in-progress project converting interactive rebase from shell to C as well and they did not want to clash with them by refactoring sequencer.c version of apply_autostash(). Since both efforts are long done, we can freely combine them together now.

---
 builtin/rebase.c | 49 ++----------------------------------------------
 sequencer.c      |  2 +-
 sequencer.h      |  2 ++
 3 files changed, 5 insertions(+), 48 deletions(-)
builtin/rebase.c +2 -47
index 1146463099..ceb115247a 100644
@@ -712,51 +712,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)
 	return 0;
 }
 
-static int apply_autostash(struct rebase_options *opts)
-{
-	const char *path = state_dir_path("autostash", opts);
-	struct strbuf autostash = STRBUF_INIT;
-	struct child_process stash_apply = CHILD_PROCESS_INIT;
-
-	if (!file_exists(path))
-		return 0;
-
-	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
-		return error(_("Could not read '%s'"), path);
-	/* Ensure that the hash is not mistaken for a number */
-	strbuf_addstr(&autostash, "^0");
-	argv_array_pushl(&stash_apply.args,
-			 "stash", "apply", autostash.buf, NULL);
-	stash_apply.git_cmd = 1;
-	stash_apply.no_stderr = stash_apply.no_stdout =
-		stash_apply.no_stdin = 1;
-	if (!run_command(&stash_apply))
-		printf(_("Applied autostash.\n"));
-	else {
-		struct argv_array args = ARGV_ARRAY_INIT;
-		int res = 0;
-
-		argv_array_pushl(&args,
-				 "stash", "store", "-m", "autostash", "-q",
-				 autostash.buf, NULL);
-		if (run_command_v_opt(args.argv, RUN_GIT_CMD))
-			res = error(_("Cannot store %s"), autostash.buf);
-		argv_array_clear(&args);
-		strbuf_release(&autostash);
-		if (res)
-			return res;
-
-		fprintf(stderr,
-			_("Applying autostash resulted in conflicts.\n"
-			  "Your changes are safe in the stash.\n"
-			  "You can run \"git stash pop\" or \"git stash drop\" "
-			  "at any time.\n"));
-	}
-
-	strbuf_release(&autostash);
-	return 0;
-}
-
 static int finish_rebase(struct rebase_options *opts)
 {
 	struct strbuf dir = STRBUF_INIT;
@@ -764,7 +719,7 @@ static int finish_rebase(struct rebase_options *opts)
 	int ret = 0;
 
 	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
-	apply_autostash(opts);
+	apply_autostash(state_dir_path("autostash", opts));
 	close_object_store(the_repository->objects);
 	/*
 	 * We ignore errors in 'gc --auto', since the
@@ -1209,7 +1164,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
 	} else if (status == 2) {
 		struct strbuf dir = STRBUF_INIT;
 
-		apply_autostash(opts);
+		apply_autostash(state_dir_path("autostash", opts));
 		strbuf_addstr(&dir, opts->state_dir);
 		remove_dir_recursively(&dir, 0);
 		strbuf_release(&dir);
sequencer.c +1 -1
index ad40a8b3fc..b52668f8de 100644
@@ -3655,7 +3655,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
 	return -1;
 }
 
-static int apply_autostash(const char *path)
+int apply_autostash(const char *path)
 {
 	struct strbuf stash_sha1 = STRBUF_INIT;
 	struct child_process child = CHILD_PROCESS_INIT;
sequencer.h +2 -0
index 5c9662a60a..bab910f012 100644
@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,
 			 const struct commit *current_head,
 			 const struct object_id *new_head);
 
+int apply_autostash(const char *path);
+
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
 void print_commit_summary(struct repository *repo,
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#66 lore raw

PATCH v3 10/19 rebase: generify reset_head()

In reply to Denton Liu

In the future, we plan on lib-ifying reset_head() so we need it to be more generic. Make it more generic by making it accept a struct repository argument instead of implicitly using the non-repo functions. Also, make it accept a const char *default_reflog_action argument so that the default action of "rebase" isn't hardcoded in.

---
 builtin/rebase.c | 64 ++++++++++++++++++++++++++++--------------------
 1 file changed, 37 insertions(+), 27 deletions(-)
builtin/rebase.c +37 -27
index ceb115247a..52eb45b6c4 100644
@@ -28,6 +28,8 @@
 #include "sequencer.h"
 #include "rebase-interactive.h"
 
+#define DEFAULT_REFLOG_ACTION "rebase"
+
 static char const * const builtin_rebase_usage[] = {
 	N_("git rebase [-i] [options] [--exec <cmd>] "
 		"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
@@ -772,9 +774,10 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)
 #define RESET_HEAD_REFS_ONLY (1<<3)
 #define RESET_ORIG_HEAD (1<<4)
 
-static int reset_head(struct object_id *oid, const char *action,
+static int reset_head(struct repository *r, struct object_id *oid, const char *action,
 		      const char *switch_to_branch, unsigned flags,
-		      const char *reflog_orig_head, const char *reflog_head)
+		      const char *reflog_orig_head, const char *reflog_head,
+		      const char *default_reflog_action)
 {
 	unsigned detach_head = flags & RESET_HEAD_DETACH;
 	unsigned reset_hard = flags & RESET_HEAD_HARD;
@@ -796,7 +799,7 @@ static int reset_head(struct object_id *oid, const char *action,
 	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
 		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
 
-	if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
+	if (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {
 		ret = -1;
 		goto leave_reset_head;
 	}
@@ -815,26 +818,26 @@ static int reset_head(struct object_id *oid, const char *action,
 	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
 	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
 	unpack_tree_opts.head_idx = 1;
-	unpack_tree_opts.src_index = the_repository->index;
-	unpack_tree_opts.dst_index = the_repository->index;
+	unpack_tree_opts.src_index = r->index;
+	unpack_tree_opts.dst_index = r->index;
 	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
 	unpack_tree_opts.update = 1;
 	unpack_tree_opts.merge = 1;
 	if (!detach_head)
 		unpack_tree_opts.reset = 1;
 
-	if (repo_read_index_unmerged(the_repository) < 0) {
+	if (repo_read_index_unmerged(r) < 0) {
 		ret = error(_("could not read index"));
 		goto leave_reset_head;
 	}
 
-	if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {
+	if (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {
 		ret = error(_("failed to find tree of %s"),
 			    oid_to_hex(&head_oid));
 		goto leave_reset_head;
 	}
 
-	if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {
+	if (!fill_tree_descriptor(r, &desc[nr++], oid)) {
 		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
 		goto leave_reset_head;
 	}
@@ -845,16 +848,16 @@ static int reset_head(struct object_id *oid, const char *action,
 	}
 
 	tree = parse_tree_indirect(oid);
-	prime_cache_tree(the_repository, the_repository->index, tree);
+	prime_cache_tree(r, r->index, tree);
 
-	if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {
+	if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {
 		ret = error(_("could not write index"));
 		goto leave_reset_head;
 	}
 
 reset_head_refs:
 	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
-	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
+	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : default_reflog_action);
 	prefix_len = msg.len;
 
 	if (update_orig_head) {
@@ -916,8 +919,10 @@ static int move_to_original_branch(struct rebase_options *opts)
 		    opts->head_name, oid_to_hex(&opts->onto->object.oid));
 	strbuf_addf(&head_reflog, "rebase finished: returning to %s",
 		    opts->head_name);
-	ret = reset_head(NULL, "", opts->head_name, RESET_HEAD_REFS_ONLY,
-			 orig_head_reflog.buf, head_reflog.buf);
+	ret = reset_head(the_repository, NULL, "", opts->head_name,
+			 RESET_HEAD_REFS_ONLY,
+			 orig_head_reflog.buf, head_reflog.buf,
+			 DEFAULT_REFLOG_ACTION);
 
 	strbuf_release(&orig_head_reflog);
 	strbuf_release(&head_reflog);
@@ -1005,8 +1010,9 @@ static int run_am(struct rebase_options *opts)
 		free(rebased_patches);
 		argv_array_clear(&am.args);
 
-		reset_head(&opts->orig_head, "checkout", opts->head_name, 0,
-			   "HEAD", NULL);
+		reset_head(the_repository, &opts->orig_head, "checkout",
+			   opts->head_name, 0,
+			   "HEAD", NULL, DEFAULT_REFLOG_ACTION);
 		error(_("\ngit encountered an error while preparing the "
 			"patches to replay\n"
 			"these revisions:\n"
@@ -1661,8 +1667,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		rerere_clear(the_repository, &merge_rr);
 		string_list_clear(&merge_rr, 1);
 
-		if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
-			       NULL, NULL) < 0)
+		if (reset_head(the_repository, NULL, "reset", NULL, RESET_HEAD_HARD,
+			       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)
 			die(_("could not discard worktree changes"));
 		remove_branch_state(the_repository, 0);
 		if (read_basic_state(&options))
@@ -1679,9 +1685,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 		if (read_basic_state(&options))
 			exit(1);
-		if (reset_head(&options.orig_head, "reset",
+		if (reset_head(the_repository, &options.orig_head, "reset",
 			       options.head_name, RESET_HEAD_HARD,
-			       NULL, NULL) < 0)
+			       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)
 			die(_("could not move back to %s"),
 			    oid_to_hex(&options.orig_head));
 		remove_branch_state(the_repository, 0);
@@ -2073,8 +2079,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				    options.state_dir);
 			write_file(autostash, "%s", oid_to_hex(&oid));
 			printf(_("Created autostash: %s\n"), buf.buf);
-			if (reset_head(NULL, "reset --hard",
-				       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
+			if (reset_head(the_repository, NULL, "reset --hard",
+				       NULL, RESET_HEAD_HARD, NULL, NULL,
+				       DEFAULT_REFLOG_ACTION) < 0)
 				die(_("could not reset --hard"));
 
 			if (discard_index(the_repository->index) < 0 ||
@@ -2114,10 +2121,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				strbuf_addf(&buf, "%s: checkout %s",
 					    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
 					    options.switch_to);
-				if (reset_head(&options.orig_head, "checkout",
+				if (reset_head(the_repository,
+					       &options.orig_head, "checkout",
 					       options.head_name,
 					       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
-					       NULL, buf.buf) < 0) {
+					       NULL, buf.buf,
+					       DEFAULT_REFLOG_ACTION) < 0) {
 					ret = !!error(_("could not switch to "
 							"%s"),
 						      options.switch_to);
@@ -2189,10 +2198,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	strbuf_addf(&msg, "%s: checkout %s",
 		    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
-	if (reset_head(&options.onto->object.oid, "checkout", NULL,
+	if (reset_head(the_repository, &options.onto->object.oid, "checkout", NULL,
 		       RESET_HEAD_DETACH | RESET_ORIG_HEAD |
 		       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
-		       NULL, msg.buf))
+		       NULL, msg.buf, DEFAULT_REFLOG_ACTION))
 		die(_("Could not detach HEAD"));
 	strbuf_release(&msg);
 
@@ -2207,8 +2216,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		strbuf_addf(&msg, "rebase finished: %s onto %s",
 			options.head_name ? options.head_name : "detached HEAD",
 			oid_to_hex(&options.onto->object.oid));
-		reset_head(NULL, "Fast-forwarded", options.head_name,
-			   RESET_HEAD_REFS_ONLY, "HEAD", msg.buf);
+		reset_head(the_repository, NULL, "Fast-forwarded", options.head_name,
+			   RESET_HEAD_REFS_ONLY, "HEAD", msg.buf,
+			   DEFAULT_REFLOG_ACTION);
 		strbuf_release(&msg);
 		ret = !!finish_rebase(&options);
 		goto cleanup;
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#67 lore raw

PATCH v3 11/19 reset: extract reset_head() from rebase

In reply to Denton Liu

Continue the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved.

---
 Makefile         |   1 +
 builtin/rebase.c | 139 +---------------------------------------------
 reset.c          | 140 +++++++++++++++++++++++++++++++++++++++++++++++
 reset.h          |  20 +++++++
 4 files changed, 162 insertions(+), 138 deletions(-)
 create mode 100644 reset.c
 create mode 100644 reset.h
Makefile +1 -0
index f709fc3f77..338fb55b73 100644
@@ -970,6 +970,7 @@ LIB_OBJS += replace-object.o
 LIB_OBJS += repo-settings.o
 LIB_OBJS += repository.o
 LIB_OBJS += rerere.o
+LIB_OBJS += reset.o
 LIB_OBJS += resolve-undo.o
 LIB_OBJS += revision.o
 LIB_OBJS += run-command.o
builtin/rebase.c +1 -138

151 lines not on this page · raw message

reset.c new file +140 -0

140 lines not on this page · raw message

reset.h new file +20 -0
new file mode 100644
index 0000000000..12f83c78e2
@@ -0,0 +1,20 @@
+#ifndef RESET_H
+#define RESET_H
+
+#include "hash.h"
+#include "repository.h"
+
+#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
+
+#define RESET_HEAD_DETACH (1<<0)
+#define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
+#define RESET_HEAD_REFS_ONLY (1<<3)
+#define RESET_ORIG_HEAD (1<<4)
+
+int reset_head(struct repository *r, struct object_id *oid, const char *action,
+	       const char *switch_to_branch, unsigned flags,
+	       const char *reflog_orig_head, const char *reflog_head,
+	       const char *default_reflog_action);
+
+#endif
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#69 lore raw

PATCH v3 12/19 rebase: extract create_autostash()

In reply to Denton Liu

In a future commit, we will lib-ify this code. In preparation for this, extract the code into the create_autostash() function so that it can be cleaned up before it is finally lib-ified.

This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.

---
 builtin/rebase.c | 94 +++++++++++++++++++++++++-----------------------
 1 file changed, 50 insertions(+), 44 deletions(-)
builtin/rebase.c +50 -44
index caff67b00d..037effc510 100644
@@ -1274,6 +1274,55 @@ static int check_exec_cmd(const char *cmd)
 	return 0;
 }
 
+static void create_autostash(struct rebase_options *options)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct lock_file lock_file = LOCK_INIT;
+	int fd;
+
+	fd = hold_locked_index(&lock_file, 0);
+	refresh_cache(REFRESH_QUIET);
+	if (0 <= fd)
+		repo_update_index_if_able(the_repository, &lock_file);
+	rollback_lock_file(&lock_file);
+
+	if (has_unstaged_changes(the_repository, 1) ||
+	    has_uncommitted_changes(the_repository, 1)) {
+		const char *autostash =
+			state_dir_path("autostash", options);
+		struct child_process stash = CHILD_PROCESS_INIT;
+		struct object_id oid;
+
+		argv_array_pushl(&stash.args,
+				 "stash", "create", "autostash", NULL);
+		stash.git_cmd = 1;
+		stash.no_stdin = 1;
+		strbuf_reset(&buf);
+		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
+			die(_("Cannot autostash"));
+		strbuf_trim_trailing_newline(&buf);
+		if (get_oid(buf.buf, &oid))
+			die(_("Unexpected stash response: '%s'"),
+			    buf.buf);
+		strbuf_reset(&buf);
+		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
+
+		if (safe_create_leading_directories_const(autostash))
+			die(_("Could not create directory for '%s'"),
+			    options->state_dir);
+		write_file(autostash, "%s", oid_to_hex(&oid));
+		printf(_("Created autostash: %s\n"), buf.buf);
+		if (reset_head(the_repository, NULL, "reset --hard",
+			       NULL, RESET_HEAD_HARD, NULL, NULL,
+			       DEFAULT_REFLOG_ACTION) < 0)
+			die(_("could not reset --hard"));
+
+		if (discard_index(the_repository->index) < 0 ||
+			repo_read_index(the_repository) < 0)
+			die(_("could not read index"));
+	}
+	strbuf_release(&buf);
+}
 
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
@@ -1907,50 +1956,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		die(_("could not read index"));
 
 	if (options.autostash) {
-		struct lock_file lock_file = LOCK_INIT;
-		int fd;
-
-		fd = hold_locked_index(&lock_file, 0);
-		refresh_cache(REFRESH_QUIET);
-		if (0 <= fd)
-			repo_update_index_if_able(the_repository, &lock_file);
-		rollback_lock_file(&lock_file);
-
-		if (has_unstaged_changes(the_repository, 1) ||
-		    has_uncommitted_changes(the_repository, 1)) {
-			const char *autostash =
-				state_dir_path("autostash", &options);
-			struct child_process stash = CHILD_PROCESS_INIT;
-			struct object_id oid;
-
-			argv_array_pushl(&stash.args,
-					 "stash", "create", "autostash", NULL);
-			stash.git_cmd = 1;
-			stash.no_stdin = 1;
-			strbuf_reset(&buf);
-			if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
-				die(_("Cannot autostash"));
-			strbuf_trim_trailing_newline(&buf);
-			if (get_oid(buf.buf, &oid))
-				die(_("Unexpected stash response: '%s'"),
-				    buf.buf);
-			strbuf_reset(&buf);
-			strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
-
-			if (safe_create_leading_directories_const(autostash))
-				die(_("Could not create directory for '%s'"),
-				    options.state_dir);
-			write_file(autostash, "%s", oid_to_hex(&oid));
-			printf(_("Created autostash: %s\n"), buf.buf);
-			if (reset_head(the_repository, NULL, "reset --hard",
-				       NULL, RESET_HEAD_HARD, NULL, NULL,
-				       DEFAULT_REFLOG_ACTION) < 0)
-				die(_("could not reset --hard"));
-
-			if (discard_index(the_repository->index) < 0 ||
-				repo_read_index(the_repository) < 0)
-				die(_("could not read index"));
-		}
+		create_autostash(&options);
 	}
 
 	if (require_clean_work_tree(the_repository, "rebase",
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#70 lore raw

PATCH v3 13/19 rebase: generify create_autostash()

In reply to Denton Liu

In the future, we plan on lib-ifying create_autostash() so we need it to be more generic. Make it more generic by making it accept a struct repository argument instead of implicitly using the non-repo functions and the_repository. Also, make it accept a path argument so that we no longer rely have to rely on struct rebase_options. Finally, make it accept a default_reflog_action argument so we no longer have to rely on DEFAULT_REFLOG_ACTION.

---
 builtin/rebase.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)
builtin/rebase.c +16 -16
index 037effc510..3370c8f2c4 100644
@@ -1274,22 +1274,21 @@ static int check_exec_cmd(const char *cmd)
 	return 0;
 }
 
-static void create_autostash(struct rebase_options *options)
+static void create_autostash(struct repository *r, const char *path,
+			     const char *default_reflog_action)
 {
 	struct strbuf buf = STRBUF_INIT;
 	struct lock_file lock_file = LOCK_INIT;
 	int fd;
 
-	fd = hold_locked_index(&lock_file, 0);
-	refresh_cache(REFRESH_QUIET);
+	fd = repo_hold_locked_index(r, &lock_file, 0);
+	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
 	if (0 <= fd)
-		repo_update_index_if_able(the_repository, &lock_file);
+		repo_update_index_if_able(r, &lock_file);
 	rollback_lock_file(&lock_file);
 
-	if (has_unstaged_changes(the_repository, 1) ||
-	    has_uncommitted_changes(the_repository, 1)) {
-		const char *autostash =
-			state_dir_path("autostash", options);
+	if (has_unstaged_changes(r, 1) ||
+	    has_uncommitted_changes(r, 1)) {
 		struct child_process stash = CHILD_PROCESS_INIT;
 		struct object_id oid;
 
@@ -1307,18 +1306,18 @@ static void create_autostash(struct rebase_options *options)
 		strbuf_reset(&buf);
 		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
 
-		if (safe_create_leading_directories_const(autostash))
+		if (safe_create_leading_directories_const(path))
 			die(_("Could not create directory for '%s'"),
-			    options->state_dir);
-		write_file(autostash, "%s", oid_to_hex(&oid));
+			    path);
+		write_file(path, "%s", oid_to_hex(&oid));
 		printf(_("Created autostash: %s\n"), buf.buf);
-		if (reset_head(the_repository, NULL, "reset --hard",
+		if (reset_head(r, NULL, "reset --hard",
 			       NULL, RESET_HEAD_HARD, NULL, NULL,
-			       DEFAULT_REFLOG_ACTION) < 0)
+			       default_reflog_action) < 0)
 			die(_("could not reset --hard"));
 
-		if (discard_index(the_repository->index) < 0 ||
-			repo_read_index(the_repository) < 0)
+		if (discard_index(r->index) < 0 ||
+			repo_read_index(r) < 0)
 			die(_("could not read index"));
 	}
 	strbuf_release(&buf);
@@ -1956,7 +1955,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		die(_("could not read index"));
 
 	if (options.autostash) {
-		create_autostash(&options);
+		create_autostash(the_repository, state_dir_path("autostash", &options),
+				 DEFAULT_REFLOG_ACTION);
 	}
 
 	if (require_clean_work_tree(the_repository, "rebase",
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#71 lore raw

PATCH v3 14/19 sequencer: extract perform_autostash() from rebase

In reply to Denton Liu

Lib-ify the autostash code by extracting perform_autostash() from rebase into sequencer. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved.

---
 builtin/rebase.c | 49 -----------------------------------------------
 sequencer.c      | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |  2 ++
 3 files changed, 52 insertions(+), 49 deletions(-)
builtin/rebase.c +0 -49
index 3370c8f2c4..b6821be62b 100644
@@ -1274,55 +1274,6 @@ static int check_exec_cmd(const char *cmd)
 	return 0;
 }
 
-static void create_autostash(struct repository *r, const char *path,
-			     const char *default_reflog_action)
-{
-	struct strbuf buf = STRBUF_INIT;
-	struct lock_file lock_file = LOCK_INIT;
-	int fd;
-
-	fd = repo_hold_locked_index(r, &lock_file, 0);
-	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
-	if (0 <= fd)
-		repo_update_index_if_able(r, &lock_file);
-	rollback_lock_file(&lock_file);
-
-	if (has_unstaged_changes(r, 1) ||
-	    has_uncommitted_changes(r, 1)) {
-		struct child_process stash = CHILD_PROCESS_INIT;
-		struct object_id oid;
-
-		argv_array_pushl(&stash.args,
-				 "stash", "create", "autostash", NULL);
-		stash.git_cmd = 1;
-		stash.no_stdin = 1;
-		strbuf_reset(&buf);
-		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
-			die(_("Cannot autostash"));
-		strbuf_trim_trailing_newline(&buf);
-		if (get_oid(buf.buf, &oid))
-			die(_("Unexpected stash response: '%s'"),
-			    buf.buf);
-		strbuf_reset(&buf);
-		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
-
-		if (safe_create_leading_directories_const(path))
-			die(_("Could not create directory for '%s'"),
-			    path);
-		write_file(path, "%s", oid_to_hex(&oid));
-		printf(_("Created autostash: %s\n"), buf.buf);
-		if (reset_head(r, NULL, "reset --hard",
-			       NULL, RESET_HEAD_HARD, NULL, NULL,
-			       default_reflog_action) < 0)
-			die(_("could not reset --hard"));
-
-		if (discard_index(r->index) < 0 ||
-			repo_read_index(r) < 0)
-			die(_("could not read index"));
-	}
-	strbuf_release(&buf);
-}
-
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
 	struct rebase_options options = REBASE_OPTIONS_INIT;
sequencer.c +50 -0

62 lines not on this page · raw message

sequencer.h +2 -0
index bab910f012..44ecfee7f3 100644
@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,
 			 const struct commit *current_head,
 			 const struct object_id *new_head);
 
+void create_autostash(struct repository *r, const char *path,
+		      const char *default_reflog_action);
 int apply_autostash(const char *path);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#72 lore raw

PATCH v3 15/19 sequencer: unlink autostash in apply_autostash()

In reply to Denton Liu

Explicitly remove autostash file in apply_autostash() once it has been applied successfully.

This is currently a no-op because the only users of this function will unlink the state (including the autostash file) after this function runs. However, in the future, we will introduce a user of the function that does not explicitly remove the state so we do it here.

---
 sequencer.c | 1 +
 1 file changed, 1 insertion(+)
sequencer.c +1 -0
index b2951843a1..81b6c700d1 100644
@@ -3746,6 +3746,7 @@ int apply_autostash(const char *path)
 				  " \"git stash drop\" at any time.\n"));
 	}
 
+	unlink(path);
 	strbuf_release(&stash_sha1);
 	return ret;
 }
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#73 lore raw

PATCH v3 16/19 sequencer: implement save_autostash()

In reply to Denton Liu

Extract common functionality of apply_autostash() into apply_save_autostash() and use it to implement save_autostash(). This function will be used in a future commit.

---
 sequencer.c | 37 +++++++++++++++++++++++++++----------
 sequencer.h |  1 +
 2 files changed, 28 insertions(+), 10 deletions(-)
sequencer.c +27 -10
index 81b6c700d1..d67d47b332 100644
@@ -3705,7 +3705,7 @@ void create_autostash(struct repository *r, const char *path,
 	strbuf_release(&buf);
 }
 
-int apply_autostash(const char *path)
+static int apply_save_autostash(const char *path, int attempt_apply)
 {
 	struct strbuf stash_sha1 = STRBUF_INIT;
 	struct child_process child = CHILD_PROCESS_INIT;
@@ -3718,13 +3718,17 @@ int apply_autostash(const char *path)
 	}
 	strbuf_trim(&stash_sha1);
 
-	child.git_cmd = 1;
-	child.no_stdout = 1;
-	child.no_stderr = 1;
-	argv_array_push(&child.args, "stash");
-	argv_array_push(&child.args, "apply");
-	argv_array_push(&child.args, stash_sha1.buf);
-	if (!run_command(&child))
+	if (attempt_apply) {
+		child.git_cmd = 1;
+		child.no_stdout = 1;
+		child.no_stderr = 1;
+		argv_array_push(&child.args, "stash");
+		argv_array_push(&child.args, "apply");
+		argv_array_push(&child.args, stash_sha1.buf);
+		ret = run_command(&child);
+	}
+
+	if (attempt_apply && !ret)
 		fprintf(stderr, _("Applied autostash.\n"));
 	else {
 		struct child_process store = CHILD_PROCESS_INIT;
@@ -3740,10 +3744,13 @@ int apply_autostash(const char *path)
 			ret = error(_("cannot store %s"), stash_sha1.buf);
 		else
 			fprintf(stderr,
-				_("Applying autostash resulted in conflicts.\n"
+				_("%s\n"
 				  "Your changes are safe in the stash.\n"
 				  "You can run \"git stash pop\" or"
-				  " \"git stash drop\" at any time.\n"));
+				  " \"git stash drop\" at any time.\n"),
+				attempt_apply ?
+				_("Applying autostash resulted in conflicts.") :
+				_("Autostash exists; creating a new stash entry."));
 	}
 
 	unlink(path);
@@ -3751,6 +3758,16 @@ int apply_autostash(const char *path)
 	return ret;
 }
 
+int save_autostash(const char *path)
+{
+	return apply_save_autostash(path, 0);
+}
+
+int apply_autostash(const char *path)
+{
+	return apply_save_autostash(path, 1);
+}
+
 static const char *reflog_message(struct replay_opts *opts,
 	const char *sub_action, const char *fmt, ...)
 {
sequencer.h +1 -0
index 44ecfee7f3..1cb0e4e4b7 100644
@@ -193,6 +193,7 @@ void commit_post_rewrite(struct repository *r,
 
 void create_autostash(struct repository *r, const char *path,
 		      const char *default_reflog_action);
+int save_autostash(const char *path);
 int apply_autostash(const char *path);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#83 lore raw

PATCH v3 18/19 t5520: make test_pull_autostash() accept expect_parent_num

In reply to Denton Liu

Before, test_pull_autostash() was hardcoded to run test_cmp_rev HEAD^ copy to test that a rebase happened. However, in a future patch, we plan on testing merging as well. Make test_pull_autostash() accept a parent number as an argument so that, in the future, we can test if a merge happened in addition to a rebase.

---
 t/t5520-pull.sh | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)
t/t5520-pull.sh +8 -6
index 2f86fca042..f610dc14de 100755
@@ -10,11 +10,13 @@ modify () {
 }
 
 test_pull_autostash () {
+	expect_parent_num="$1" &&
+	shift &&
 	git reset --hard before-rebase &&
 	echo dirty >new_file &&
 	git add new_file &&
 	git pull "$@" . copy &&
-	test_cmp_rev HEAD^ copy &&
+	test_cmp_rev HEAD^"$expect_parent_num" copy &&
 	echo dirty >expect &&
 	test_cmp expect new_file &&
 	echo "modified again" >expect &&
@@ -369,22 +371,22 @@ test_expect_success '--rebase fails with multiple branches' '
 
 test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' '
 	test_config rebase.autostash true &&
-	test_pull_autostash --rebase
+	test_pull_autostash 1 --rebase
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash=true' '
 	test_config rebase.autostash true &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash=false' '
 	test_config rebase.autostash false &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash unset' '
 	test_unconfig rebase.autostash &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '
@@ -422,7 +424,7 @@ test_expect_success 'pull.rebase' '
 
 test_expect_success 'pull --autostash & pull.rebase=true' '
 	test_config pull.rebase true &&
-	test_pull_autostash --autostash
+	test_pull_autostash 1 --autostash
 '
 
 test_expect_success 'pull --no-autostash & pull.rebase=true' '
Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#75 lore raw

PATCH v3 17/19 merge: teach --autostash option

In reply to Denton Liu

In rebase, one can pass the --autostash option to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.

Implement the --autostash option and corresponding merge.autoStash option in merge which stashes before merging and then pops after.

This option is useful when a developer has some local changes on a topic branch but they realize that their work depends on another branch. Previously, they had to run something like

git fetch ...
git stash push
git merge FETCH_HEAD
git stash pop

but now, that is reduced to

	git fetch ...
	git merge --autostash FETCH_HEAD

When git reset --hard is run to abort a merge, the working tree will be left in a clean state, as expected, with the autostash pushed onto the stash stack.

---
 Documentation/config/merge.txt  |  10 +++
 Documentation/merge-options.txt |   8 +++
 branch.c                        |   1 +
 builtin/commit.c                |   2 +
 builtin/merge.c                 |  17 ++++++
 builtin/reset.c                 |   7 ++-
 path.c                          |   1 +
 path.h                          |   4 +-
 t/t3033-merge-toplevel.sh       |  22 +++++++
 t/t7600-merge.sh                | 104 ++++++++++++++++++++++++++++++++
 10 files changed, 174 insertions(+), 2 deletions(-)
Documentation/config/merge.txt +10 -0
index 6a313937f8..88b29127bf 100644
@@ -70,6 +70,16 @@ merge.stat::
 	Whether to print the diffstat between ORIG_HEAD and the merge result
 	at the end of the merge.  True by default.
 
+merge.autoStash::
+	When set to true, automatically create a temporary stash entry
+	before the operation begins, and apply it after the operation
+	ends.  This means that you can run rebase on a dirty worktree.
+	However, use with care: the final stash application after a
+	successful rebase might result in non-trivial conflicts.
+	This option can be overridden by the `--no-autostash` and
+	`--autostash` options of linkgit:git-merge[1].
+	Defaults to false.
+
 merge.tool::
 	Controls which merge tool is used by linkgit:git-mergetool[1].
 	The list below shows the valid built-in values.
Documentation/merge-options.txt +8 -0
index 40dc4f5e8c..34493eb58b 100644
@@ -155,6 +155,14 @@ ifndef::git-pull[]
 	Note that not all merge strategies may support progress
 	reporting.
 
+--autostash::
+--no-autostash::
+	Automatically create a temporary stash entry before the operation
+	begins, and apply it after the operation ends.  This means
+	that you can run rebase on a dirty worktree.  However, use
+	with care: the final stash application after a successful
+	rebase might result in non-trivial conflicts.
+
 endif::git-pull[]
 
 --allow-unrelated-histories::
branch.c +1 -0
index 579494738a..bf2536c70d 100644
@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
 	unlink(git_path_merge_rr(r));
 	unlink(git_path_merge_msg(r));
 	unlink(git_path_merge_mode(r));
+	apply_autostash(git_path_merge_autostash(r));
 }
 
 void remove_branch_state(struct repository *r, int verbose)
builtin/commit.c +2 -0
index 7ba33a3bec..c11894423a 100644
@@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	unlink(git_path_merge_mode(the_repository));
 	unlink(git_path_squash_msg(the_repository));
 
+	apply_autostash(git_path_merge_autostash(the_repository));
+
 	if (commit_index_files())
 		die(_("repository has been updated, but unable to write\n"
 		      "new_index file. Check that disk is not full and quota is\n"
builtin/merge.c +17 -0
index d127d2225f..e038bef5ad 100644
@@ -82,6 +82,7 @@ static int show_progress = -1;
 static int default_to_upstream = 1;
 static int signoff;
 static const char *sign_commit;
+static int autostash;
 static int no_verify;
 
 static struct strategy all_strategy[] = {
@@ -286,6 +287,8 @@ static struct option builtin_merge_options[] = {
 	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
 	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
 	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+	OPT_BOOL(0, "autostash", &autostash,
+	      N_("automatically stash/stash pop before and after")),
 	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
 	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
 	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ -441,6 +444,7 @@ static void finish(struct commit *head_commit,
 		strbuf_addf(&reflog_message, "%s: %s",
 			getenv("GIT_REFLOG_ACTION"), msg);
 	}
+	apply_autostash(git_path_merge_autostash(the_repository));
 	if (squash) {
 		squash_message(head_commit, remoteheads);
 	} else {
@@ -634,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 		return 0;
 	} else if (!strcmp(k, "gpg.mintrustlevel")) {
 		check_trust_level = 0;
+	} else if (!strcmp(k, "merge.autostash")) {
+		autostash = git_config_bool(k, v);
+		return 0;
 	}
 
 	status = fmt_merge_msg_config(k, v, cb);
@@ -1291,6 +1298,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
 		/* Invoke 'git reset --merge' */
 		ret = cmd_reset(nargc, nargv, prefix);
+		apply_autostash(git_path_merge_autostash(the_repository));
 		goto done;
 	}
 
@@ -1513,6 +1521,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 			goto done;
 		}
 
+		if (autostash)
+			create_autostash(the_repository,
+					 git_path_merge_autostash(the_repository),
+					 "merge");
 		if (checkout_fast_forward(the_repository,
 					  &head_commit->object.oid,
 					  &commit->object.oid,
@@ -1579,6 +1591,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	if (fast_forward == FF_ONLY)
 		die(_("Not possible to fast-forward, aborting."));
 
+	if (autostash)
+		create_autostash(the_repository,
+				 git_path_merge_autostash(the_repository),
+				 "merge");
+
 	/* We are going to make a new commit. */
 	git_committer_info(IDENT_STRICT);
 
builtin/reset.c +6 -1
index 18228c312e..038c8532eb 100644
@@ -25,6 +25,7 @@
 #include "cache-tree.h"
 #include "submodule.h"
 #include "submodule-config.h"
+#include "sequencer.h"
 
 #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
 
@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		if (reset_type == HARD && !update_ref_status && !quiet)
 			print_new_head_line(lookup_commit_reference(the_repository, &oid));
 	}
-	if (!pathspec.nr)
+	if (!pathspec.nr) {
+		if (reset_type == HARD)
+			save_autostash(git_path_merge_autostash(the_repository));
+
 		remove_branch_state(the_repository, 0);
+	}
 
 	return update_ref_status;
 }
path.c +1 -0
index 88cf593007..d764738146 100644
@@ -1535,5 +1535,6 @@ REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
 REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
 REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE")
 REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
+REPO_GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
 REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")
 REPO_GIT_PATH_FUNC(shallow, "shallow")
path.h +3 -1
index 14d6dcad16..1f1bf8f87a 100644
@@ -177,11 +177,12 @@ struct path_cache {
 	const char *merge_rr;
 	const char *merge_mode;
 	const char *merge_head;
+	const char *merge_autostash;
 	const char *fetch_head;
 	const char *shallow;
 };
 
-#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 
 const char *git_path_cherry_pick_head(struct repository *r);
 const char *git_path_revert_head(struct repository *r);
@@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);
 const char *git_path_merge_rr(struct repository *r);
 const char *git_path_merge_mode(struct repository *r);
 const char *git_path_merge_head(struct repository *r);
+const char *git_path_merge_autostash(struct repository *r);
 const char *git_path_fetch_head(struct repository *r);
 const char *git_path_shallow(struct repository *r);
 
t/t3033-merge-toplevel.sh +22 -0
index d314599428..e29c284b9b 100755
@@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' '
 	test_must_fail git merge five
 '
 
+test_expect_success 'refuse two-project merge by default, quit before --autostash happens' '
+	t3033_reset &&
+	git reset --hard four &&
+	echo change >>one.t &&
+	git diff >expect &&
+	test_must_fail git merge --autostash five 2>err &&
+	test_i18ngrep ! "stash" err &&
+	git diff >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'two-project merge with --allow-unrelated-histories' '
 	t3033_reset &&
 	git reset --hard four &&
@@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' '
 	git diff --exit-code five
 '
 
+test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' '
+	t3033_reset &&
+	git reset --hard four &&
+	echo change >>one.t &&
+	git diff one.t >expect &&
+	git merge --allow-unrelated-histories --autostash five 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git diff one.t >actual &&
+	test_cmp expect actual
+'
+
 test_done
t/t7600-merge.sh +104 -0

123 lines not on this page · raw message

Signature · 1 line
2.25.0.114.g5b0ca878e0
author
#84 lore raw

PATCH v3 19/19 pull: pass --autostash to merge

In reply to Denton Liu

Before, --autostash only worked with git pull --rebase. However, in the last patch, merge learned --autostash as well so there's no reason why we should have this restriction anymore. Teach pull to pass --autostash to merge, just like it did for rebase.

---
 Documentation/git-pull.txt      |  9 -------
 Documentation/merge-options.txt |  4 +--
 builtin/pull.c                  |  9 ++++---
 t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------
 4 files changed, 42 insertions(+), 23 deletions(-)
Documentation/git-pull.txt +0 -9
index dfb901f8b8..ba3772de9f 100644
@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.
 --no-rebase::
 	Override earlier --rebase.
 
---autostash::
---no-autostash::
-	Before starting rebase, stash local modifications away (see
-	linkgit:git-stash[1]) if needed, and apply the stash entry when
-	done. `--no-autostash` is useful to override the `rebase.autoStash`
-	configuration variable (see linkgit:git-config[1]).
-+
-This option is only valid when "--rebase" is used.
-
 Options related to fetching
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
Documentation/merge-options.txt +2 -2
index 34493eb58b..ae56cca826 100644
@@ -155,6 +155,8 @@ ifndef::git-pull[]
 	Note that not all merge strategies may support progress
 	reporting.
 
+endif::git-pull[]
+
 --autostash::
 --no-autostash::
 	Automatically create a temporary stash entry before the operation
@@ -163,8 +165,6 @@ ifndef::git-pull[]
 	with care: the final stash application after a successful
 	rebase might result in non-trivial conflicts.
 
-endif::git-pull[]
-
 --allow-unrelated-histories::
 	By default, `git merge` command refuses to merge histories
 	that do not share a common ancestor.  This option can be
builtin/pull.c +5 -4
index 3e624d1e00..9beb4841d1 100644
@@ -163,7 +163,7 @@ static struct option pull_options[] = {
 		N_("verify that the named commit has a valid GPG signature"),
 		PARSE_OPT_NOARG),
 	OPT_BOOL(0, "autostash", &opt_autostash,
-		N_("automatically stash/stash pop before and after rebase")),
+		N_("automatically stash/stash pop before and after")),
 	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
 		N_("merge strategy to use"),
 		0),
@@ -661,6 +661,10 @@ static int run_merge(void)
 	argv_array_pushv(&args, opt_strategy_opts.argv);
 	if (opt_gpg_sign)
 		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");
 	if (opt_allow_unrelated_histories > 0)
 		argv_array_push(&args, "--allow-unrelated-histories");
 
@@ -908,9 +912,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 	if (get_oid("HEAD", &orig_head))
 		oidclr(&orig_head);
 
-	if (!opt_rebase && opt_autostash != -1)
-		die(_("--[no-]autostash option is only valid with --rebase."));
-
 	autostash = config_autostash;
 	if (opt_rebase) {
 		if (opt_autostash != -1)
t/t5520-pull.sh +35 -8

55 lines not on this page · raw message

Signature · 1 line
2.25.0.114.g5b0ca878e0
#56 lore raw

Re: [PATCH v3 03/19] sequencer: use file strbuf for read_oneliner()

In reply to Denton Liu

Hi Denton

Quoted 10 lines · Denton Liu

In the original read_oneliner logic, we duplicated the logic for strbuf_trim_trailing_newline() with one exception: instead of preventing buffer accesses below index 0, it would prevent buffer accesses below index orig_len. Although this is correct, it isn't worth having the duplicated logic around.

Add a second strbuf to which files are read and run strbuf_trim_trailing_newline() directly on this strbuf then concatenate this strbuf with the argument strbuf at the end of the function. The function's external behaviour is unchanged.

read_oneliner() is typically called with an strbuf that is used multiple times with the caller resetting it in-between calls. This saves us from having to allocate a new buffer for each call. The change here negates that as we end up allocating a new buffer each time and then copying the result. I'd be surprised if any of the callers actually wanted to append to the buffer, I suspect they all call strbuf_reset() before passing the buffer to read_oneliner(). If that is the case maybe we should think about adding the call to strbuf_reset() inside read_oneliner() and documenting it as resetting the buffer before reading. Then we can just use strbuf_trim() on the 'real' buffer.

Best Wishes

Phillip

Quoted 51 lines
---
  sequencer.c | 24 +++++++++++++-----------
  1 file changed, 13 insertions(+), 11 deletions(-)
sequencer.c +13 -11
index e528225e78..c49fe76fe6 100644
@@ -431,26 +431,28 @@ static int write_message(const void *buf, size_t len, const char *filename,
  static int read_oneliner(struct strbuf *buf,
  	const char *path, int skip_if_empty)
  {
-	int orig_len = buf->len;
+	int ret = 0;
+	struct strbuf file_buf = STRBUF_INIT;
 
  	if (!file_exists(path))
  		return 0;
 
-	if (strbuf_read_file(buf, path, 0) < 0) {
+	if (strbuf_read_file(&file_buf, path, 0) < 0) {
  		warning_errno(_("could not read '%s'"), path);
-		return 0;
+		goto done;
  	}
 
-	if (buf->len > orig_len && buf->buf[buf->len - 1] == '\n') {
-		if (--buf->len > orig_len && buf->buf[buf->len - 1] == '\r')
-			--buf->len;
-		buf->buf[buf->len] = '\0';
-	}
+	strbuf_trim_trailing_newline(&file_buf);
 
-	if (skip_if_empty && buf->len == orig_len)
-		return 0;
+	if (skip_if_empty && !file_buf.len)
+		goto done;
 
-	return 1;
+	strbuf_addbuf(buf, &file_buf);
+	ret = 1;
+
+done:
+	strbuf_release(&file_buf);
+	return ret;
  }
 
  static struct tree *empty_tree(struct repository *r)
#59 lore raw

Re: [PATCH v3 05/19] sequencer: configurably warn on non-existent files

In reply to Denton Liu

Hi Denton

Quoted 4 lines · Denton Liu

In the future, we plan on externing read_oneliner(). Future users of read_oneliner() will want the ability to output warnings in the event that the path doesn't exist. Introduce the READ_ONELINER_WARN_NON_EXISTENCE

This is a rather cumbersome name, I think Junio's original suggestion of READ_ONELINER_WARN_MISSING is nicer

Quoted 22 lines

flag which, if active, would issue a
warning when a file doesn't exist by skipping the !file_exists() check and letting strbuf_read_file() handle that case.

---
  sequencer.c | 3 ++-
  1 file changed, 2 insertions(+), 1 deletion(-)
sequencer.c +1 -0
index abb2a21e9d..92e8d38290 100644
@@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename,
  }
 
  #define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
+#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
 
  /*
   * Reads a file that was presumably written by a shell script, i.e. with an

sequencer.c static int read_oneliner(struct strbuf *buf, · line 441

Quoted 6 lines
sequencer.c +1 -1
@@ -436,7 +437,7 @@ static int read_oneliner(struct strbuf *buf,
  	int ret = 0;
  	struct strbuf file_buf = STRBUF_INIT;
 
-	if (!file_exists(path))
+	if (!(flags & READ_ONELINER_WARN_NON_EXISTENCE) && !file_exists(path))
  		return 0;

This isn't the fault of you patch but it would be more efficient to do the read and then check for ENOENT/ENOTDIR rather than calling file_exists() first

Best Wishes

Phillip

Quoted 2 lines
sequencer.c +0 -0
  	if (strbuf_read_file(&file_buf, path, 0) < 0) {
 
#62 lore raw

Re: [PATCH v3 07/19] rebase: use read_oneliner()

In reply to Denton Liu

Hi Denton

Quoted 6 lines · Denton Liu

Since in sequencer.c, read_one() basically duplicates the functionality of read_oneliner(), reduce code duplication by replacing read_one() with read_oneliner().

This was done with the following Coccinelle script

Quoted 7 lines
	@@
	expression a, b;
	@@
	- read_one(a, b)
	+ !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)

and long lines were manually broken up.

It's great to see read_one() going away

Thanks

Phillip

Quoted 104 lines
---
  builtin/rebase.c | 37 +++++++++++++++++--------------------
  1 file changed, 17 insertions(+), 20 deletions(-)
builtin/rebase.c +17 -20
index bff53d5d16..1146463099 100644
@@ -586,15 +586,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o
  	return path.buf;
  }
 
-/* Read one file, then strip line endings */
-static int read_one(const char *path, struct strbuf *buf)
-{
-	if (strbuf_read_file(buf, path, 0) < 0)
-		return error_errno(_("could not read '%s'"), path);
-	strbuf_trim_trailing_newline(buf);
-	return 0;
-}
-
  /* Initialize the rebase options from the state directory. */
  static int read_basic_state(struct rebase_options *opts)
  {
@@ -602,8 +593,10 @@ static int read_basic_state(struct rebase_options *opts)
  	struct strbuf buf = STRBUF_INIT;
  	struct object_id oid;
 
-	if (read_one(state_dir_path("head-name", opts), &head_name) ||
-	    read_one(state_dir_path("onto", opts), &buf))
+	if (!read_oneliner(&head_name, state_dir_path("head-name", opts),
+			   READ_ONELINER_WARN_NON_EXISTENCE) ||
+	    !read_oneliner(&buf, state_dir_path("onto", opts),
+			   READ_ONELINER_WARN_NON_EXISTENCE))
  		return -1;
  	opts->head_name = starts_with(head_name.buf, "refs/") ?
  		xstrdup(head_name.buf) : NULL;
@@ -619,9 +612,11 @@ static int read_basic_state(struct rebase_options *opts)
  	 */
  	strbuf_reset(&buf);
  	if (file_exists(state_dir_path("orig-head", opts))) {
-		if (read_one(state_dir_path("orig-head", opts), &buf))
+		if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
+				   READ_ONELINER_WARN_NON_EXISTENCE))
  			return -1;
-	} else if (read_one(state_dir_path("head", opts), &buf))
+	} else if (!read_oneliner(&buf, state_dir_path("head", opts),
+				  READ_ONELINER_WARN_NON_EXISTENCE))
  		return -1;
  	if (get_oid(buf.buf, &opts->orig_head))
  		return error(_("invalid orig-head: '%s'"), buf.buf);
@@ -641,8 +636,8 @@ static int read_basic_state(struct rebase_options *opts)
 
  	if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
  		strbuf_reset(&buf);
-		if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
-			    &buf))
+		if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
+				   READ_ONELINER_WARN_NON_EXISTENCE))
  			return -1;
  		if (!strcmp(buf.buf, "--rerere-autoupdate"))
  			opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
@@ -655,8 +650,8 @@ static int read_basic_state(struct rebase_options *opts)
 
  	if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
  		strbuf_reset(&buf);
-		if (read_one(state_dir_path("gpg_sign_opt", opts),
-			    &buf))
+		if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
+				   READ_ONELINER_WARN_NON_EXISTENCE))
  			return -1;
  		free(opts->gpg_sign_opt);
  		opts->gpg_sign_opt = xstrdup(buf.buf);
@@ -664,7 +659,8 @@ static int read_basic_state(struct rebase_options *opts)
 
  	if (file_exists(state_dir_path("strategy", opts))) {
  		strbuf_reset(&buf);
-		if (read_one(state_dir_path("strategy", opts), &buf))
+		if (!read_oneliner(&buf, state_dir_path("strategy", opts),
+				   READ_ONELINER_WARN_NON_EXISTENCE))
  			return -1;
  		free(opts->strategy);
  		opts->strategy = xstrdup(buf.buf);
@@ -672,7 +668,8 @@ static int read_basic_state(struct rebase_options *opts)
 
  	if (file_exists(state_dir_path("strategy_opts", opts))) {
  		strbuf_reset(&buf);
-		if (read_one(state_dir_path("strategy_opts", opts), &buf))
+		if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
+				   READ_ONELINER_WARN_NON_EXISTENCE))
  			return -1;
  		free(opts->strategy_opts);
  		opts->strategy_opts = xstrdup(buf.buf);
@@ -724,7 +721,7 @@ static int apply_autostash(struct rebase_options *opts)
  	if (!file_exists(path))
  		return 0;
 
-	if (read_one(path, &autostash))
+	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
  		return error(_("Could not read '%s'"), path);
  	/* Ensure that the hash is not mistaken for a number */
  	strbuf_addstr(&autostash, "^0");
#65 lore raw

Re: [PATCH v3 09/19] rebase: use apply_autostash() from sequencer.c

In reply to Denton Liu

Hi Denton

Quoted 3 lines · Denton Liu

The apply_autostash() function in builtin/rebase.c is similar enough to the apply_autostash() function in sequencer.c that they are almost interchangeable.

You say they are almost interchangeable and then use them as if they are completely interchangeable. I think they are interchangeable, the only difference I could see is that the code in rebase.c redirects stdin when forking 'git stash apply' but the sequencer does not, I don't think this matters.

Best Wishes

Phillip

Make the sequencer.c version extern and use it in

Quoted 118 lines

rebase.

The rebase version was introduced in 6defce2b02 (builtin rebase: support --autostash option, 2018-09-04) as part of the shell to C conversion. It opted to duplicate the function because, at the time, there was another in-progress project converting interactive rebase from shell to C as well and they did not want to clash with them by refactoring sequencer.c version of apply_autostash(). Since both efforts are long done, we can freely combine them together now.

---
  builtin/rebase.c | 49 ++----------------------------------------------
  sequencer.c      |  2 +-
  sequencer.h      |  2 ++
  3 files changed, 5 insertions(+), 48 deletions(-)
builtin/rebase.c +2 -47
index 1146463099..ceb115247a 100644
@@ -712,51 +712,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)
  	return 0;
  }
 
-static int apply_autostash(struct rebase_options *opts)
-{
-	const char *path = state_dir_path("autostash", opts);
-	struct strbuf autostash = STRBUF_INIT;
-	struct child_process stash_apply = CHILD_PROCESS_INIT;
-
-	if (!file_exists(path))
-		return 0;
-
-	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
-		return error(_("Could not read '%s'"), path);
-	/* Ensure that the hash is not mistaken for a number */
-	strbuf_addstr(&autostash, "^0");
-	argv_array_pushl(&stash_apply.args,
-			 "stash", "apply", autostash.buf, NULL);
-	stash_apply.git_cmd = 1;
-	stash_apply.no_stderr = stash_apply.no_stdout =
-		stash_apply.no_stdin = 1;
-	if (!run_command(&stash_apply))
-		printf(_("Applied autostash.\n"));
-	else {
-		struct argv_array args = ARGV_ARRAY_INIT;
-		int res = 0;
-
-		argv_array_pushl(&args,
-				 "stash", "store", "-m", "autostash", "-q",
-				 autostash.buf, NULL);
-		if (run_command_v_opt(args.argv, RUN_GIT_CMD))
-			res = error(_("Cannot store %s"), autostash.buf);
-		argv_array_clear(&args);
-		strbuf_release(&autostash);
-		if (res)
-			return res;
-
-		fprintf(stderr,
-			_("Applying autostash resulted in conflicts.\n"
-			  "Your changes are safe in the stash.\n"
-			  "You can run \"git stash pop\" or \"git stash drop\" "
-			  "at any time.\n"));
-	}
-
-	strbuf_release(&autostash);
-	return 0;
-}
-
  static int finish_rebase(struct rebase_options *opts)
  {
  	struct strbuf dir = STRBUF_INIT;
@@ -764,7 +719,7 @@ static int finish_rebase(struct rebase_options *opts)
  	int ret = 0;
 
  	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
-	apply_autostash(opts);
+	apply_autostash(state_dir_path("autostash", opts));
  	close_object_store(the_repository->objects);
  	/*
  	 * We ignore errors in 'gc --auto', since the
@@ -1209,7 +1164,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
  	} else if (status == 2) {
  		struct strbuf dir = STRBUF_INIT;
 
-		apply_autostash(opts);
+		apply_autostash(state_dir_path("autostash", opts));
  		strbuf_addstr(&dir, opts->state_dir);
  		remove_dir_recursively(&dir, 0);
  		strbuf_release(&dir);
sequencer.c +1 -1
index ad40a8b3fc..b52668f8de 100644
@@ -3655,7 +3655,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
  	return -1;
  }
 
-static int apply_autostash(const char *path)
+int apply_autostash(const char *path)
  {
  	struct strbuf stash_sha1 = STRBUF_INIT;
  	struct child_process child = CHILD_PROCESS_INIT;
sequencer.h +2 -0
index 5c9662a60a..bab910f012 100644
@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,
  			 const struct commit *current_head,
  			 const struct object_id *new_head);
 
+int apply_autostash(const char *path);
+
  #define SUMMARY_INITIAL_COMMIT   (1 << 0)
  #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
  void print_commit_summary(struct repository *repo,
#68 lore raw

Re: [PATCH v3 11/19] reset: extract reset_head() from rebase

In reply to Denton Liu

Hi Denton

Quoted 4 lines · Denton Liu

Continue the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved.

Indeed! It looks good. Looking ahead I hope we can use this to avoid forking 'git checkout' in sequencer.c

Best Wishes

Phillip

Quoted 353 lines
---
  Makefile         |   1 +
  builtin/rebase.c | 139 +---------------------------------------------
  reset.c          | 140 +++++++++++++++++++++++++++++++++++++++++++++++
  reset.h          |  20 +++++++
  4 files changed, 162 insertions(+), 138 deletions(-)

create mode 100644 reset.c
create mode 100644 reset.h

Makefile +1 -0
index f709fc3f77..338fb55b73 100644
@@ -970,6 +970,7 @@ LIB_OBJS += replace-object.o
  LIB_OBJS += repo-settings.o
  LIB_OBJS += repository.o
  LIB_OBJS += rerere.o
+LIB_OBJS += reset.o
  LIB_OBJS += resolve-undo.o
  LIB_OBJS += revision.o
  LIB_OBJS += run-command.o
builtin/rebase.c +1 -138
index 52eb45b6c4..caff67b00d 100644
@@ -27,6 +27,7 @@
  #include "branch.h"
  #include "sequencer.h"
  #include "rebase-interactive.h"
+#include "reset.h"
 
  #define DEFAULT_REFLOG_ACTION "rebase"
 
@@ -766,144 +767,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)
  	}
  }
 
-#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
-
-#define RESET_HEAD_DETACH (1<<0)
-#define RESET_HEAD_HARD (1<<1)
-#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
-#define RESET_HEAD_REFS_ONLY (1<<3)
-#define RESET_ORIG_HEAD (1<<4)
-
-static int reset_head(struct repository *r, struct object_id *oid, const char *action,
-		      const char *switch_to_branch, unsigned flags,
-		      const char *reflog_orig_head, const char *reflog_head,
-		      const char *default_reflog_action)
-{
-	unsigned detach_head = flags & RESET_HEAD_DETACH;
-	unsigned reset_hard = flags & RESET_HEAD_HARD;
-	unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
-	unsigned refs_only = flags & RESET_HEAD_REFS_ONLY;
-	unsigned update_orig_head = flags & RESET_ORIG_HEAD;
-	struct object_id head_oid;
-	struct tree_desc desc[2] = { { NULL }, { NULL } };
-	struct lock_file lock = LOCK_INIT;
-	struct unpack_trees_options unpack_tree_opts;
-	struct tree *tree;
-	const char *reflog_action;
-	struct strbuf msg = STRBUF_INIT;
-	size_t prefix_len;
-	struct object_id *orig = NULL, oid_orig,
-		*old_orig = NULL, oid_old_orig;
-	int ret = 0, nr = 0;
-
-	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
-		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
-
-	if (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {
-		ret = -1;
-		goto leave_reset_head;
-	}
-
-	if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) {
-		ret = error(_("could not determine HEAD revision"));
-		goto leave_reset_head;
-	}
-
-	if (!oid)
-		oid = &head_oid;
-
-	if (refs_only)
-		goto reset_head_refs;
-
-	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
-	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
-	unpack_tree_opts.head_idx = 1;
-	unpack_tree_opts.src_index = r->index;
-	unpack_tree_opts.dst_index = r->index;
-	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
-	unpack_tree_opts.update = 1;
-	unpack_tree_opts.merge = 1;
-	if (!detach_head)
-		unpack_tree_opts.reset = 1;
-
-	if (repo_read_index_unmerged(r) < 0) {
-		ret = error(_("could not read index"));
-		goto leave_reset_head;
-	}
-
-	if (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {
-		ret = error(_("failed to find tree of %s"),
-			    oid_to_hex(&head_oid));
-		goto leave_reset_head;
-	}
-
-	if (!fill_tree_descriptor(r, &desc[nr++], oid)) {
-		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
-		goto leave_reset_head;
-	}
-
-	if (unpack_trees(nr, desc, &unpack_tree_opts)) {
-		ret = -1;
-		goto leave_reset_head;
-	}
-
-	tree = parse_tree_indirect(oid);
-	prime_cache_tree(r, r->index, tree);
-
-	if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {
-		ret = error(_("could not write index"));
-		goto leave_reset_head;
-	}
-
-reset_head_refs:
-	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
-	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : default_reflog_action);
-	prefix_len = msg.len;
-
-	if (update_orig_head) {
-		if (!get_oid("ORIG_HEAD", &oid_old_orig))
-			old_orig = &oid_old_orig;
-		if (!get_oid("HEAD", &oid_orig)) {
-			orig = &oid_orig;
-			if (!reflog_orig_head) {
-				strbuf_addstr(&msg, "updating ORIG_HEAD");
-				reflog_orig_head = msg.buf;
-			}
-			update_ref(reflog_orig_head, "ORIG_HEAD", orig,
-				   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
-		} else if (old_orig)
-			delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
-	}
-
-	if (!reflog_head) {
-		strbuf_setlen(&msg, prefix_len);
-		strbuf_addstr(&msg, "updating HEAD");
-		reflog_head = msg.buf;
-	}
-	if (!switch_to_branch)
-		ret = update_ref(reflog_head, "HEAD", oid, orig,
-				 detach_head ? REF_NO_DEREF : 0,
-				 UPDATE_REFS_MSG_ON_ERR);
-	else {
-		ret = update_ref(reflog_head, switch_to_branch, oid,
-				 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
-		if (!ret)
-			ret = create_symref("HEAD", switch_to_branch,
-					    reflog_head);
-	}
-	if (run_hook)
-		run_hook_le(NULL, "post-checkout",
-			    oid_to_hex(orig ? orig : &null_oid),
-			    oid_to_hex(oid), "1", NULL);
-
-leave_reset_head:
-	strbuf_release(&msg);
-	rollback_lock_file(&lock);
-	while (nr)
-		free((void *)desc[--nr].buffer);
-	return ret;
-}
-
  static int move_to_original_branch(struct rebase_options *opts)
  {
  	struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
reset.c new file +140 -0
new file mode 100644
index 0000000000..79b683bffa
@@ -0,0 +1,140 @@
+#include "git-compat-util.h"
+#include "cache-tree.h"
+#include "lockfile.h"
+#include "refs.h"
+#include "reset.h"
+#include "run-command.h"
+#include "tree-walk.h"
+#include "tree.h"
+#include "unpack-trees.h"
+
+int reset_head(struct repository *r, struct object_id *oid, const char *action,
+	       const char *switch_to_branch, unsigned flags,
+	       const char *reflog_orig_head, const char *reflog_head,
+	       const char *default_reflog_action)
+{
+	unsigned detach_head = flags & RESET_HEAD_DETACH;
+	unsigned reset_hard = flags & RESET_HEAD_HARD;
+	unsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;
+	unsigned refs_only = flags & RESET_HEAD_REFS_ONLY;
+	unsigned update_orig_head = flags & RESET_ORIG_HEAD;
+	struct object_id head_oid;
+	struct tree_desc desc[2] = { { NULL }, { NULL } };
+	struct lock_file lock = LOCK_INIT;
+	struct unpack_trees_options unpack_tree_opts;
+	struct tree *tree;
+	const char *reflog_action;
+	struct strbuf msg = STRBUF_INIT;
+	size_t prefix_len;
+	struct object_id *orig = NULL, oid_orig,
+		*old_orig = NULL, oid_old_orig;
+	int ret = 0, nr = 0;
+
+	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
+		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
+
+	if (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {
+		ret = -1;
+		goto leave_reset_head;
+	}
+
+	if ((!oid || !reset_hard) && get_oid("HEAD", &head_oid)) {
+		ret = error(_("could not determine HEAD revision"));
+		goto leave_reset_head;
+	}
+
+	if (!oid)
+		oid = &head_oid;
+
+	if (refs_only)
+		goto reset_head_refs;
+
+	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
+	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
+	unpack_tree_opts.head_idx = 1;
+	unpack_tree_opts.src_index = r->index;
+	unpack_tree_opts.dst_index = r->index;
+	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
+	unpack_tree_opts.update = 1;
+	unpack_tree_opts.merge = 1;
+	if (!detach_head)
+		unpack_tree_opts.reset = 1;
+
+	if (repo_read_index_unmerged(r) < 0) {
+		ret = error(_("could not read index"));
+		goto leave_reset_head;
+	}
+
+	if (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {
+		ret = error(_("failed to find tree of %s"),
+			    oid_to_hex(&head_oid));
+		goto leave_reset_head;
+	}
+
+	if (!fill_tree_descriptor(r, &desc[nr++], oid)) {
+		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
+		goto leave_reset_head;
+	}
+
+	if (unpack_trees(nr, desc, &unpack_tree_opts)) {
+		ret = -1;
+		goto leave_reset_head;
+	}
+
+	tree = parse_tree_indirect(oid);
+	prime_cache_tree(r, r->index, tree);
+
+	if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {
+		ret = error(_("could not write index"));
+		goto leave_reset_head;
+	}
+
+reset_head_refs:
+	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
+	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : default_reflog_action);
+	prefix_len = msg.len;
+
+	if (update_orig_head) {
+		if (!get_oid("ORIG_HEAD", &oid_old_orig))
+			old_orig = &oid_old_orig;
+		if (!get_oid("HEAD", &oid_orig)) {
+			orig = &oid_orig;
+			if (!reflog_orig_head) {
+				strbuf_addstr(&msg, "updating ORIG_HEAD");
+				reflog_orig_head = msg.buf;
+			}
+			update_ref(reflog_orig_head, "ORIG_HEAD", orig,
+				   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);
+		} else if (old_orig)
+			delete_ref(NULL, "ORIG_HEAD", old_orig, 0);
+	}
+
+	if (!reflog_head) {
+		strbuf_setlen(&msg, prefix_len);
+		strbuf_addstr(&msg, "updating HEAD");
+		reflog_head = msg.buf;
+	}
+	if (!switch_to_branch)
+		ret = update_ref(reflog_head, "HEAD", oid, orig,
+				 detach_head ? REF_NO_DEREF : 0,
+				 UPDATE_REFS_MSG_ON_ERR);
+	else {
+		ret = update_ref(reflog_head, switch_to_branch, oid,
+				 NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+		if (!ret)
+			ret = create_symref("HEAD", switch_to_branch,
+					    reflog_head);
+	}
+	if (run_hook)
+		run_hook_le(NULL, "post-checkout",
+			    oid_to_hex(orig ? orig : &null_oid),
+			    oid_to_hex(oid), "1", NULL);
+
+leave_reset_head:
+	strbuf_release(&msg);
+	rollback_lock_file(&lock);
+	while (nr)
+		free((void *)desc[--nr].buffer);
+	return ret;
+
+}
reset.h new file +20 -0
new file mode 100644
index 0000000000..12f83c78e2
@@ -0,0 +1,20 @@
+#ifndef RESET_H
+#define RESET_H
+
+#include "hash.h"
+#include "repository.h"
+
+#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
+
+#define RESET_HEAD_DETACH (1<<0)
+#define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
+#define RESET_HEAD_REFS_ONLY (1<<3)
+#define RESET_ORIG_HEAD (1<<4)
+
+int reset_head(struct repository *r, struct object_id *oid, const char *action,
+	       const char *switch_to_branch, unsigned flags,
+	       const char *reflog_orig_head, const char *reflog_head,
+	       const char *default_reflog_action);
+
+#endif
#74 lore raw

Re: [PATCH v3 16/19] sequencer: implement save_autostash()

In reply to Denton Liu

Hi Denton

Quoted 35 lines · Denton Liu

Extract common functionality of apply_autostash() into apply_save_autostash() and use it to implement save_autostash(). This function will be used in a future commit.

---
  sequencer.c | 37 +++++++++++++++++++++++++++----------
  sequencer.h |  1 +
  2 files changed, 28 insertions(+), 10 deletions(-)
sequencer.c +2 -8
index 81b6c700d1..d67d47b332 100644
@@ -3705,7 +3705,7 @@ void create_autostash(struct repository *r, const char *path,
  	strbuf_release(&buf);
  }
 
-int apply_autostash(const char *path)
+static int apply_save_autostash(const char *path, int attempt_apply)
  {
  	struct strbuf stash_sha1 = STRBUF_INIT;
  	struct child_process child = CHILD_PROCESS_INIT;
@@ -3718,13 +3718,17 @@ int apply_autostash(const char *path)
  	}
  	strbuf_trim(&stash_sha1);
 
-	child.git_cmd = 1;
-	child.no_stdout = 1;
-	child.no_stderr = 1;
-	argv_array_push(&child.args, "stash");
-	argv_array_push(&child.args, "apply");
-	argv_array_push(&child.args, stash_sha1.buf);
-	if (!run_command(&child))
+	if (attempt_apply) {

sequencer.c int apply_autostash(const char *path) · line 3731

Quoted 10 lines
sequencer.c +10 -0
+		child.git_cmd = 1;
+		child.no_stdout = 1;
+		child.no_stderr = 1;
+		argv_array_push(&child.args, "stash");
+		argv_array_push(&child.args, "apply");
+		argv_array_push(&child.args, stash_sha1.buf);
+		ret = run_command(&child);
+	}
+
+	if (attempt_apply && !ret)

If attempt_only is not set then we wont get any message - is this intended? Previously we always showed the message when applying was successful

Quoted 6 lines
sequencer.c +0 -0
  		fprintf(stderr, _("Applied autostash.\n"));
  	else {
  		struct child_process store = CHILD_PROCESS_INIT;
@@ -3740,10 +3744,13 @@ int apply_autostash(const char *path)
  			ret = error(_("cannot store %s"), stash_sha1.buf);
  		else

sequencer.c int apply_autostash(const char *path) · line 3753

Quoted 10 lines
sequencer.c +5 -2
  			fprintf(stderr,
-				_("Applying autostash resulted in conflicts.\n"
+				_("%s\n"
  				  "Your changes are safe in the stash.\n"
  				  "You can run \"git stash pop\" or"
-				  " \"git stash drop\" at any time.\n"));
+				  " \"git stash drop\" at any time.\n"),
+				attempt_apply ?
+				_("Applying autostash resulted in conflicts.") :
+				_("Autostash exists; creating a new stash entry."));

I'm a bit confused by the new message, perhaps the commit message could explain what attempt_only is used for and then I suspect the reason for this change will become clear

Best Wishes

Phillip

Quoted 33 lines
sequencer.c +10 -0
  	}
 
  	unlink(path);
@@ -3751,6 +3758,16 @@ int apply_autostash(const char *path)
  	return ret;
  }
 
+int save_autostash(const char *path)
+{
+	return apply_save_autostash(path, 0);
+}
+
+int apply_autostash(const char *path)
+{
+	return apply_save_autostash(path, 1);
+}
+
  static const char *reflog_message(struct replay_opts *opts,
  	const char *sub_action, const char *fmt, ...)
  {
sequencer.h +1 -0
index 44ecfee7f3..1cb0e4e4b7 100644
@@ -193,6 +193,7 @@ void commit_post_rewrite(struct repository *r,
 
  void create_autostash(struct repository *r, const char *path,
  		      const char *default_reflog_action);
+int save_autostash(const char *path);
  int apply_autostash(const char *path);
 
  #define SUMMARY_INITIAL_COMMIT   (1 << 0)
#76 lore raw

Re: [PATCH v3 17/19] merge: teach --autostash option

In reply to Denton Liu

Hi Denton

Quoted 45 lines · Denton Liu

In rebase, one can pass the --autostash option to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.

Implement the --autostash option and corresponding merge.autoStash option in merge which stashes before merging and then pops after.

This option is useful when a developer has some local changes on a topic branch but they realize that their work depends on another branch. Previously, they had to run something like

git fetch ...
git stash push
git merge FETCH_HEAD
git stash pop

but now, that is reduced to

	git fetch ...
	git merge --autostash FETCH_HEAD

When git reset --hard is run to abort a merge, the working tree will be left in a clean state, as expected, with the autostash pushed onto the stash stack.

---
  Documentation/config/merge.txt  |  10 +++
  Documentation/merge-options.txt |   8 +++
  branch.c                        |   1 +
  builtin/commit.c                |   2 +
  builtin/merge.c                 |  17 ++++++
  builtin/reset.c                 |   7 ++-
  path.c                          |   1 +
  path.h                          |   4 +-
  t/t3033-merge-toplevel.sh       |  22 +++++++
  t/t7600-merge.sh                | 104 ++++++++++++++++++++++++++++++++
  10 files changed, 174 insertions(+), 2 deletions(-)
Documentation/config/merge.txt +0 -0
index 6a313937f8..88b29127bf 100644

Documentation/config/merge.txt merge.stat:: · line 76

Quoted 7 lines
Documentation/config/merge.txt +4 -0
@@ -70,6 +70,16 @@ merge.stat::
  	Whether to print the diffstat between ORIG_HEAD and the merge result
  	at the end of the merge.  True by default.
 
+merge.autoStash::
+	When set to true, automatically create a temporary stash entry
+	before the operation begins, and apply it after the operation
+	ends.  This means that you can run rebase

copy and paste error - s/rebase/merge/

Quoted 15 lines

on a dirty worktree.

Documentation/config/merge.txt +6 -0
+	However, use with care: the final stash application after a
+	successful rebase might result in non-trivial conflicts.
+	This option can be overridden by the `--no-autostash` and
+	`--autostash` options of linkgit:git-merge[1].
+	Defaults to false.
+
  merge.tool::
  	Controls which merge tool is used by linkgit:git-mergetool[1].
  	The list below shows the valid built-in values.
Documentation/merge-options.txt +0 -0
index 40dc4f5e8c..34493eb58b 100644

Documentation/merge-options.txt ifndef::git-pull[] · line 163

Quoted 9 lines
Documentation/merge-options.txt +6 -0
@@ -155,6 +155,14 @@ ifndef::git-pull[]
  	Note that not all merge strategies may support progress
  	reporting.
 
+--autostash::
+--no-autostash::
+	Automatically create a temporary stash entry before the operation
+	begins, and apply it after the operation ends.  This means
+	that you can run rebase on a dirty worktree.  However, use > +	with care: the final stash application after a successful
+	rebase might result in non-trivial conflicts.

s/rebase/merge/

Quoted 21 lines
Documentation/merge-options.txt +1 -0
+
  endif::git-pull[]
 
  --allow-unrelated-histories::
branch.c +1 -0
index 579494738a..bf2536c70d 100644
@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
  	unlink(git_path_merge_rr(r));
  	unlink(git_path_merge_msg(r));
  	unlink(git_path_merge_mode(r));
+	apply_autostash(git_path_merge_autostash(r));
  }
 
  void remove_branch_state(struct repository *r, int verbose)
builtin/commit.c +0 -0
index 7ba33a3bec..c11894423a 100644

builtin/commit.c int cmd_commit(int argc, const char **argv, const char *prefix) · line 1691

Quoted 5 lines
builtin/commit.c +2 -0
@@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
  	unlink(git_path_merge_mode(the_repository));
  	unlink(git_path_squash_msg(the_repository));
 
+	apply_autostash(git_path_merge_autostash(the_repository));
+

It's hard to tell from the limited context but do we want to run commit_index_files() before applying the autostash? I wonder if this should be using remove_merge_branch_state() instead of duplicating it as well.

Quoted 16 lines
builtin/commit.c +0 -0
  	if (commit_index_files())
  		die(_("repository has been updated, but unable to write\n"
  		      "new_index file. Check that disk is not full and quota is\n"
builtin/merge.c +1 -0
index d127d2225f..e038bef5ad 100644
@@ -82,6 +82,7 @@ static int show_progress = -1;
  static int default_to_upstream = 1;
  static int signoff;
  static const char *sign_commit;
+static int autostash;
  static int no_verify;
 
  static struct strategy all_strategy[] = {

builtin/merge.c static struct option builtin_merge_options[] = { · line 291

Quoted 5 lines
builtin/merge.c +2 -0
@@ -286,6 +287,8 @@ static struct option builtin_merge_options[] = {
  	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
  	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
  	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+	OPT_BOOL(0, "autostash", &autostash,
+	      N_("automatically stash/stash pop before and after")),

If we're adding this option to several commands (rebase, merge, pull) then it might be worth defining it in parse-options.h

Quoted 65 lines
builtin/merge.c +14 -0
  	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
  	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
  	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ -441,6 +444,7 @@ static void finish(struct commit *head_commit,
  		strbuf_addf(&reflog_message, "%s: %s",
  			getenv("GIT_REFLOG_ACTION"), msg);
  	}
+	apply_autostash(git_path_merge_autostash(the_repository));
  	if (squash) {
  		squash_message(head_commit, remoteheads);
  	} else {
@@ -634,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
  		return 0;
  	} else if (!strcmp(k, "gpg.mintrustlevel")) {
  		check_trust_level = 0;
+	} else if (!strcmp(k, "merge.autostash")) {
+		autostash = git_config_bool(k, v);
+		return 0;
  	}
 
  	status = fmt_merge_msg_config(k, v, cb);
@@ -1291,6 +1298,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
  		/* Invoke 'git reset --merge' */
  		ret = cmd_reset(nargc, nargv, prefix);
+		apply_autostash(git_path_merge_autostash(the_repository));
  		goto done;
  	}
 
@@ -1513,6 +1521,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
  			goto done;
  		}
 
+		if (autostash)
+			create_autostash(the_repository,
+					 git_path_merge_autostash(the_repository),
+					 "merge");
  		if (checkout_fast_forward(the_repository,
  					  &head_commit->object.oid,
  					  &commit->object.oid,
@@ -1579,6 +1591,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
  	if (fast_forward == FF_ONLY)
  		die(_("Not possible to fast-forward, aborting."));
 
+	if (autostash)
+		create_autostash(the_repository,
+				 git_path_merge_autostash(the_repository),
+				 "merge");
+
  	/* We are going to make a new commit. */
  	git_committer_info(IDENT_STRICT);
 
builtin/reset.c +1 -0
index 18228c312e..038c8532eb 100644
@@ -25,6 +25,7 @@
  #include "cache-tree.h"
  #include "submodule.h"
  #include "submodule-config.h"
+#include "sequencer.h"
 
  #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
 

builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 445

Quoted 9 lines
builtin/reset.c +4 -1
@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
  		if (reset_type == HARD && !update_ref_status && !quiet)
  			print_new_head_line(lookup_commit_reference(the_repository, &oid));
  	}
-	if (!pathspec.nr)
+	if (!pathspec.nr) {
+		if (reset_type == HARD)
+			save_autostash(git_path_merge_autostash(the_repository));
+
  		remove_branch_state(the_repository, 0);

This removes the autostash file for all reset types but we only keep the stash in the case of 'reset --hard' which is confusing.

Best Wishes

Phillip

Quoted 209 lines
builtin/reset.c +1 -0
+	}
 
  	return update_ref_status;
  }
path.c +1 -0
index 88cf593007..d764738146 100644
@@ -1535,5 +1535,6 @@ REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
  REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
  REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE")
  REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
+REPO_GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
  REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")
  REPO_GIT_PATH_FUNC(shallow, "shallow")
path.h +3 -1
index 14d6dcad16..1f1bf8f87a 100644
@@ -177,11 +177,12 @@ struct path_cache {
  	const char *merge_rr;
  	const char *merge_mode;
  	const char *merge_head;
+	const char *merge_autostash;
  	const char *fetch_head;
  	const char *shallow;
  };
 
-#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 
  const char *git_path_cherry_pick_head(struct repository *r);
  const char *git_path_revert_head(struct repository *r);
@@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);
  const char *git_path_merge_rr(struct repository *r);
  const char *git_path_merge_mode(struct repository *r);
  const char *git_path_merge_head(struct repository *r);
+const char *git_path_merge_autostash(struct repository *r);
  const char *git_path_fetch_head(struct repository *r);
  const char *git_path_shallow(struct repository *r);
 
t/t3033-merge-toplevel.sh +22 -0
index d314599428..e29c284b9b 100755
@@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' '
  	test_must_fail git merge five
  '
 
+test_expect_success 'refuse two-project merge by default, quit before --autostash happens' '
+	t3033_reset &&
+	git reset --hard four &&
+	echo change >>one.t &&
+	git diff >expect &&
+	test_must_fail git merge --autostash five 2>err &&
+	test_i18ngrep ! "stash" err &&
+	git diff >actual &&
+	test_cmp expect actual
+'
+
  test_expect_success 'two-project merge with --allow-unrelated-histories' '
  	t3033_reset &&
  	git reset --hard four &&
@@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' '
  	git diff --exit-code five
  '
 
+test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' '
+	t3033_reset &&
+	git reset --hard four &&
+	echo change >>one.t &&
+	git diff one.t >expect &&
+	git merge --allow-unrelated-histories --autostash five 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git diff one.t >actual &&
+	test_cmp expect actual
+'
+
  test_done
t/t7600-merge.sh +104 -0
index 4fa0ef8e3b..c08e4315f4 100755
@@ -30,13 +30,17 @@ Testing basic merge operations/option parsing.
  . "$TEST_DIRECTORY"/lib-gpg.sh
 
  test_write_lines 1 2 3 4 5 6 7 8 9 >file
+cp file file.orig
  test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1
+test_write_lines 1 2 '3 X' 4 5 6 7 8 9 >file.3
  test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5
  test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9
  test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y
  test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1
  test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
+test_write_lines '1 X' 2 3 4 5 6 7 8 '9 X' >result.1-9
  test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
+test_write_lines '1 X' 2 '3 X' 4 '5 X' 6 7 8 '9 X' >result.1-3-5-9
  test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z
 
  create_merge_msgs () {
@@ -675,6 +679,106 @@ test_expect_success 'refresh the index before merging' '
  	git merge c3
  '
 
+test_expect_success 'merge with --autostash' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9 &&
+	git merge --autostash c2 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git show HEAD:file >merge-result &&
+	test_cmp result.1-5 merge-result &&
+	test_cmp result.1-5-9 file
+'
+
+test_expect_success 'merge with merge.autoStash' '
+	test_config merge.autoStash true &&
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9 &&
+	git merge c2 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git show HEAD:file >merge-result &&
+	test_cmp result.1-5 merge-result &&
+	test_cmp result.1-5-9 file
+'
+
+test_expect_success 'fast-forward merge with --autostash' '
+	git reset --hard c0 &&
+	git merge-file file file.orig file.5 &&
+	git merge --autostash c1 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	test_cmp result.1-5 file
+'
+
+test_expect_success 'octopus merge with --autostash' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.3 &&
+	git merge --autostash c2 c3 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git show HEAD:file >merge-result &&
+	test_cmp result.1-5-9 merge-result &&
+	test_cmp result.1-3-5-9 file
+'
+
+test_expect_success 'conflicted merge with --autostash, --abort restores stash' '
+	git reset --hard c3 &&
+	cp file.1 file &&
+	test_must_fail git merge --autostash c7 &&
+	git merge --abort 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	test_cmp file.1 file
+'
+
+test_expect_success 'completed merge with --no-commit and --autostash' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9 &&
+	git diff >expect &&
+	git merge --no-commit --autostash c2 &&
+	git stash show -p MERGE_AUTOSTASH >actual &&
+	test_cmp expect actual &&
+	git commit 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git show HEAD:file >merge-result &&
+	test_cmp result.1-5 merge-result &&
+	test_cmp result.1-5-9 file
+'
+
+test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9 &&
+	git diff >expect &&
+	git merge --no-commit --autostash c2 &&
+	git stash show -p MERGE_AUTOSTASH >actual &&
+	test_cmp expect actual &&
+	git merge --abort 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git diff >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9 &&
+	git diff >expect &&
+	git merge --no-commit --autostash c2 &&
+	git stash show -p MERGE_AUTOSTASH >actual &&
+	test_cmp expect actual &&
+	git reset --hard 2>err &&
+	test_i18ngrep "Autostash exists; creating a new stash entry." err &&
+	git diff --exit-code
+'
+
+test_expect_success 'merge with conflicted --autostash changes' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9y &&
+	git diff >expect &&
+	test_when_finished "test_might_fail git stash drop" &&
+	git merge --autostash c3 2>err &&
+	test_i18ngrep "Applying autostash resulted in conflicts." err &&
+	git show HEAD:file >merge-result &&
+	test_cmp result.1-9 merge-result &&
+	git stash show -p >actual &&
+	test_cmp expect actual
+'
+
  cat >expected.branch <<\EOF
  Merge branch 'c5-branch' (early part)
  EOF
#85 lore raw

Re: [PATCH v3 19/19] pull: pass --autostash to merge

In reply to Denton Liu

Hi Denton

Quoted 47 lines · Denton Liu

Before, --autostash only worked with git pull --rebase. However, in the last patch, merge learned --autostash as well so there's no reason why we should have this restriction anymore. Teach pull to pass --autostash to merge, just like it did for rebase.

---
  Documentation/git-pull.txt      |  9 -------
  Documentation/merge-options.txt |  4 +--
  builtin/pull.c                  |  9 ++++---
  t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------
  4 files changed, 42 insertions(+), 23 deletions(-)
Documentation/git-pull.txt +0 -9
index dfb901f8b8..ba3772de9f 100644
@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.
  --no-rebase::
  	Override earlier --rebase.
 
---autostash::
---no-autostash::
-	Before starting rebase, stash local modifications away (see
-	linkgit:git-stash[1]) if needed, and apply the stash entry when
-	done. `--no-autostash` is useful to override the `rebase.autoStash`
-	configuration variable (see linkgit:git-config[1]).
-+
-This option is only valid when "--rebase" is used.
-
  Options related to fetching
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
Documentation/merge-options.txt +2 -0
index 34493eb58b..ae56cca826 100644
@@ -155,6 +155,8 @@ ifndef::git-pull[]
  	Note that not all merge strategies may support progress
  	reporting.
 
+endif::git-pull[]
+
  --autostash::
  --no-autostash::
  	Automatically create a temporary stash entry before the operation

Documentation/merge-options.txt ifndef::git-pull[] · line 167

Quoted 5 lines
Documentation/merge-options.txt +0 -2
@@ -163,8 +165,6 @@ ifndef::git-pull[]
  	with care: the final stash application after a successful
  	rebase might result in non-trivial conflicts.
 
-endif::git-pull[]
-

This change means we need a neutral wording for the --autostash documentation that works with rebase/merge/pull rather than mentioning a specific command

Best Wishes

Phillip

Quoted 99 lines
Documentation/merge-options.txt +0 -0
  --allow-unrelated-histories::
  	By default, `git merge` command refuses to merge histories
  	that do not share a common ancestor.  This option can be
builtin/pull.c +5 -4
index 3e624d1e00..9beb4841d1 100644
@@ -163,7 +163,7 @@ static struct option pull_options[] = {
  		N_("verify that the named commit has a valid GPG signature"),
  		PARSE_OPT_NOARG),
  	OPT_BOOL(0, "autostash", &opt_autostash,
-		N_("automatically stash/stash pop before and after rebase")),
+		N_("automatically stash/stash pop before and after")),
  	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
  		N_("merge strategy to use"),
  		0),
@@ -661,6 +661,10 @@ static int run_merge(void)
  	argv_array_pushv(&args, opt_strategy_opts.argv);
  	if (opt_gpg_sign)
  		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");
  	if (opt_allow_unrelated_histories > 0)
  		argv_array_push(&args, "--allow-unrelated-histories");
 
@@ -908,9 +912,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
  	if (get_oid("HEAD", &orig_head))
  		oidclr(&orig_head);
 
-	if (!opt_rebase && opt_autostash != -1)
-		die(_("--[no-]autostash option is only valid with --rebase."));
-
  	autostash = config_autostash;
  	if (opt_rebase) {
  		if (opt_autostash != -1)
t/t5520-pull.sh +35 -8
index f610dc14de..37535d63a9 100755
@@ -28,7 +28,7 @@ test_pull_autostash_fail () {
  	echo dirty >new_file &&
  	git add new_file &&
  	test_must_fail git pull "$@" . copy 2>err &&
-	test_i18ngrep "uncommitted changes." err
+	test_i18ngrep "\(uncommitted changes.\)\|\(overwritten by merge:\)" err
  }
 
  test_expect_success setup '
@@ -404,13 +404,40 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
  	test_pull_autostash_fail --rebase --no-autostash
  '
 
-for i in --autostash --no-autostash
-do
-	test_expect_success "pull $i (without --rebase) is illegal" '
-		test_must_fail git pull $i . copy 2>err &&
-		test_i18ngrep "only valid with --rebase" err
-	'
-done
+test_expect_success 'pull succeeds with dirty working directory and merge.autostash set' '
+	test_config merge.autostash true &&
+	test_pull_autostash 2
+'
+
+test_expect_success 'pull --autostash & merge.autostash=true' '
+	test_config merge.autostash true &&
+	test_pull_autostash 2 --autostash
+'
+
+test_expect_success 'pull --autostash & merge.autostash=false' '
+	test_config merge.autostash false &&
+	test_pull_autostash 2 --autostash
+'
+
+test_expect_success 'pull --autostash & merge.autostash unset' '
+	test_unconfig merge.autostash &&
+	test_pull_autostash 2 --autostash
+'
+
+test_expect_success 'pull --no-autostash & merge.autostash=true' '
+	test_config merge.autostash true &&
+	test_pull_autostash_fail --no-autostash
+'
+
+test_expect_success 'pull --no-autostash & merge.autostash=false' '
+	test_config merge.autostash false &&
+	test_pull_autostash_fail --no-autostash
+'
+
+test_expect_success 'pull --no-autostash & merge.autostash unset' '
+	test_unconfig merge.autostash &&
+	test_pull_autostash_fail --no-autostash
+'
 
  test_expect_success 'pull.rebase' '
  	git reset --hard before-rebase &&
author
#77 lore raw

Re: [PATCH v3 17/19] merge: teach --autostash option

In reply to Phillip Wood

Hi Phillip,

Thanks for your detailed review on the series. I've updated everything according to your comments except for the parts below:

Quoted 15 lines · Phillip Wood
Quoted 15 lines
branch.c +1 -0
index 579494738a..bf2536c70d 100644
@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
  	unlink(git_path_merge_rr(r));
  	unlink(git_path_merge_msg(r));
  	unlink(git_path_merge_mode(r));
+	apply_autostash(git_path_merge_autostash(r));
  }
  void remove_branch_state(struct repository *r, int verbose)
builtin/commit.c +0 -0
index 7ba33a3bec..c11894423a 100644
Quoted 8 lines

builtin/commit.c int cmd_commit(int argc, const char **argv, const char *prefix) · line 1690

Quoted 5 lines
builtin/commit.c +2 -0
@@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
  	unlink(git_path_merge_mode(the_repository));
  	unlink(git_path_squash_msg(the_repository));
+	apply_autostash(git_path_merge_autostash(the_repository));
+

It's hard to tell from the limited context but do we want to run commit_index_files() before applying the autostash?

I don't think it really matters which order we run it in. When we run apply_autostash(), we only ever touch the working tree, not the index so it doesn't matter if it's run before or after. I'd prefer to keep it here because if we ever refactor this to use remove_merge_branch_state(), the person working on this will be able to perform the refactor more easily without having to worry about implicit ordering dependencies.

Quoted 2 lines

I wonder if this should
be using remove_merge_branch_state() instead of duplicating it as well.

We can _almost_ use remove_branch_state() even! Unfortunately, remove_merge_branch_state() calls unlink(git_path_merge_rr(r)) which we cannot do since repo_rerere() relies on it later. Perhaps we can can move repo_rerere() earlier?

Quoted 15 lines
Quoted 15 lines
builtin/reset.c +1 -1
index 18228c312e..038c8532eb 100644
@@ -25,6 +25,7 @@
  #include "cache-tree.h"
  #include "submodule.h"
  #include "submodule-config.h"
+#include "sequencer.h"
  #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
  		if (reset_type == HARD && !update_ref_status && !quiet)
  			print_new_head_line(lookup_commit_reference(the_repository, &oid));
  	}
-	if (!pathspec.nr)
Quoted 8 lines

builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 445

Quoted 5 lines
builtin/reset.c +4 -0
+	if (!pathspec.nr) {
+		if (reset_type == HARD)
+			save_autostash(git_path_merge_autostash(the_repository));
+
  		remove_branch_state(the_repository, 0);

This removes the autostash file for all reset types but we only keep the stash in the case of 'reset --hard' which is confusing.

I was worried that this change would be controversial... The rationale behind this change was that with reset --hard, we want to leave a clean working tree behind so we save it into the stash reflog. In all other cases, remove_branch_state() will apply the saved stash entry which should be fine since users don't expect a clean worktree with the other reset types.

I considered saving the autostash in all cases of reset but git merge --abort invokes git reset --merge behind the scenes so we'd have to consider that. Perhaps we can make all resets save the stash entry and, in the case of merge --abort, we can add some extra logic to subvert this so that the stash entry is applied?

I'm not sure about what the most intuitive behaviour is. I thought that this implementation would be the best but I'm not entirely sure. I'd appreciate some more discussion on this.

Thanks,

Denton

Quoted 7 lines

Best Wishes

Phillip

Quoted 3 lines
builtin/reset.c +1 -0
+	}
  	return update_ref_status;
  }
author
#78 lore raw

Re: [PATCH v3 17/19] merge: teach --autostash option

Quoted 31 lines · Denton Liu
Quoted 15 lines
Quoted 15 lines
builtin/reset.c +1 -1
index 18228c312e..038c8532eb 100644
@@ -25,6 +25,7 @@
  #include "cache-tree.h"
  #include "submodule.h"
  #include "submodule-config.h"
+#include "sequencer.h"
  #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
  		if (reset_type == HARD && !update_ref_status && !quiet)
  			print_new_head_line(lookup_commit_reference(the_repository, &oid));
  	}
-	if (!pathspec.nr)
Quoted 8 lines

builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 445

Quoted 5 lines
builtin/reset.c +4 -0
+	if (!pathspec.nr) {
+		if (reset_type == HARD)
+			save_autostash(git_path_merge_autostash(the_repository));
+
  		remove_branch_state(the_repository, 0);

This removes the autostash file for all reset types but we only keep the stash in the case of 'reset --hard' which is confusing.

I was worried that this change would be controversial... The rationale behind this change was that with reset --hard, we want to leave a clean working tree behind so we save it into the stash reflog. In all other cases, remove_branch_state() will apply the saved stash entry which should be fine since users don't expect a clean worktree with the other reset types.

Quoted 5 lines

I considered saving the autostash in all cases of reset but git merge --abort invokes git reset --merge behind the scenes so we'd have to consider that. Perhaps we can make all resets save the stash entry and, in the case of merge --abort, we can add some extra logic to subvert this so that the stash entry is applied?

Perhaps something like this?

-- >8 --
commit 14d0b569cb7675f00d32d3d7fad7564fcaeca458

Author: Denton Liu <[email protected]>
Date:   Fri Apr 3 06:50:34 2020 -0400

squash! merge: teach --autostash option

Stash is saved when any reset is run, when git merge --abort is run, stash is applied.

builtin/merge.c +10 -1
index 9573d77096..31b82d614c 100644
@@ -1242,6 +1242,8 @@ static int merging_a_throwaway_tag(struct commit *commit)
 	return is_throwaway_tag;
 }
 
+static GIT_PATH_FUNC(git_path_merge_autostash_saved, "MERGE_AUTOSTASH_SAVED")
+
 int cmd_merge(int argc, const char **argv, const char *prefix)
 {
 	struct object_id result_tree, stash, head_oid;
@@ -1295,9 +1297,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		if (!file_exists(git_path_merge_head(the_repository)))
 			die(_("There is no merge to abort (MERGE_HEAD missing)."));
 
+		if (file_exists(git_path_merge_autostash(the_repository))) {
+			if (rename(git_path_merge_autostash(the_repository),
+						git_path_merge_autostash_saved()))
+				die_errno(_("failed to rename autostash"));
+		}
+
 		/* Invoke 'git reset --merge' */
 		ret = cmd_reset(nargc, nargv, prefix);
-		apply_autostash(git_path_merge_autostash(the_repository));
+
+		apply_autostash(git_path_merge_autostash_saved());
 		goto done;
 	}
 
builtin/reset.c +1 -3
index 038c8532eb..060470c455 100644
@@ -439,9 +439,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			print_new_head_line(lookup_commit_reference(the_repository, &oid));
 	}
 	if (!pathspec.nr) {
-		if (reset_type == HARD)
-			save_autostash(git_path_merge_autostash(the_repository));
-
+		save_autostash(git_path_merge_autostash(the_repository));
 		remove_branch_state(the_repository, 0);
 	}
 
Quoted 16 lines

I'm not sure about what the most intuitive behaviour is. I thought that this implementation would be the best but I'm not entirely sure. I'd appreciate some more discussion on this.

Thanks,

Denton

Quoted 7 lines

Best Wishes

Phillip

Quoted 3 lines
builtin/reset.c +1 -0
+	}
  	return update_ref_status;
  }
#79 lore raw

Re: [PATCH v3 17/19] merge: teach --autostash option

Hi Denton

Quoted 42 lines · Denton Liu
Quoted 31 lines · Denton Liu
Quoted 15 lines
Quoted 15 lines
builtin/reset.c +1 -1
index 18228c312e..038c8532eb 100644
@@ -25,6 +25,7 @@
   #include "cache-tree.h"
   #include "submodule.h"
   #include "submodule-config.h"
+#include "sequencer.h"
   #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
   		if (reset_type == HARD && !update_ref_status && !quiet)
   			print_new_head_line(lookup_commit_reference(the_repository, &oid));
   	}
-	if (!pathspec.nr)
Quoted 8 lines

builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 445

Quoted 5 lines
builtin/reset.c +4 -0
+	if (!pathspec.nr) {
+		if (reset_type == HARD)
+			save_autostash(git_path_merge_autostash(the_repository));
+
   		remove_branch_state(the_repository, 0);

This removes the autostash file for all reset types but we only keep the stash in the case of 'reset --hard' which is confusing.

I was worried that this change would be controversial... The rationale behind this change was that with reset --hard, we want to leave a clean working tree behind so we save it into the stash reflog. In all other cases, remove_branch_state() will apply the saved stash entry which should be fine since users don't expect a clean worktree with the other reset types.

Quoted 5 lines

I considered saving the autostash in all cases of reset but git merge --abort invokes git reset --merge behind the scenes so we'd have to consider that. Perhaps we can make all resets save the stash entry and, in the case of merge --abort, we can add some extra logic to subvert this so that the stash entry is applied?

Perhaps something like this?

-- >8 --
commit 14d0b569cb7675f00d32d3d7fad7564fcaeca458

Quoted 7 lines
Author: Denton Liu <[email protected]>
Date:   Fri Apr 3 06:50:34 2020 -0400

squash! merge: teach --autostash option

Stash is saved when any reset is run, when git merge --abort is run, stash is applied.

I think this is the easiest behavior to understand, it avoids changing the behavior of reset, in particular it stops 'reset --mixed/--soft' from suddenly starting to touch the working tree.

Quoted 14 lines
builtin/merge.c +2 -0
index 9573d77096..31b82d614c 100644
@@ -1242,6 +1242,8 @@ static int merging_a_throwaway_tag(struct commit *commit)
  	return is_throwaway_tag;
  }
 
+static GIT_PATH_FUNC(git_path_merge_autostash_saved, "MERGE_AUTOSTASH_SAVED")
+
  int cmd_merge(int argc, const char **argv, const char *prefix)
  {
  	struct object_id result_tree, stash, head_oid;

builtin/merge.c int cmd_merge(int argc, const char **argv, const char *prefix) · line 1303

Quoted 7 lines
builtin/merge.c +4 -0
@@ -1295,9 +1297,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
  		if (!file_exists(git_path_merge_head(the_repository)))
  			die(_("There is no merge to abort (MERGE_HEAD missing)."));
 
+		if (file_exists(git_path_merge_autostash(the_repository))) {
+			if (rename(git_path_merge_autostash(the_repository),
+						git_path_merge_autostash_saved()))
+				die_errno(_("failed to rename autostash"));

This is a bit of a performance, can't we just remember the stash oid in a variable and tweak the apply code?

builtin/merge.c int cmd_merge(int argc, const char **argv, const char *prefix) · line 1309

Quoted 7 lines
builtin/merge.c +4 -1
+		}
+
  		/* Invoke 'git reset --merge' */
  		ret = cmd_reset(nargc, nargv, prefix);
-		apply_autostash(git_path_merge_autostash(the_repository));
+
+		apply_autostash(git_path_merge_autostash_saved());

Calling cmd_reset() was already a bit dodgy by our normal rules, now we're calling other functions after it though I guess given the current autostash implementation it's mostly in a separate process.

I think this is a good direction to go in
BTW what message gets printed when the stash is saved?

Best Wishes

Phillip

Quoted 34 lines
builtin/merge.c +0 -0
  		goto done;
  	}
 
builtin/reset.c +1 -3
index 038c8532eb..060470c455 100644
@@ -439,9 +439,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
  			print_new_head_line(lookup_commit_reference(the_repository, &oid));
  	}
  	if (!pathspec.nr) {
-		if (reset_type == HARD)
-			save_autostash(git_path_merge_autostash(the_repository));
-
+		save_autostash(git_path_merge_autostash(the_repository));
  		remove_branch_state(the_repository, 0);
  	}
 
Quoted 16 lines

I'm not sure about what the most intuitive behaviour is. I thought that this implementation would be the best but I'm not entirely sure. I'd appreciate some more discussion on this.

Thanks,

Denton

Quoted 7 lines

Best Wishes

Phillip

Quoted 3 lines
builtin/reset.c +1 -0
+	}
   	return update_ref_status;
   }
#81 lore raw

Re: [PATCH v3 17/19] merge: teach --autostash option

In reply to Denton Liu

Hi Denton

Quoted 4 lines · Denton Liu

Hi Phillip,

Thanks for your detailed review on the series. I've updated everything according to your comments except for the parts below:

That's great, I've added a few more comments below

Quoted 25 lines · Phillip Wood
Quoted 15 lines
Quoted 15 lines
branch.c +1 -0
index 579494738a..bf2536c70d 100644
@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
   	unlink(git_path_merge_rr(r));
   	unlink(git_path_merge_msg(r));
   	unlink(git_path_merge_mode(r));
+	apply_autostash(git_path_merge_autostash(r));
   }
   void remove_branch_state(struct repository *r, int verbose)
builtin/commit.c +0 -0
index 7ba33a3bec..c11894423a 100644
Quoted 8 lines

builtin/commit.c int cmd_commit(int argc, const char **argv, const char *prefix) · line 1690

Quoted 5 lines
builtin/commit.c +2 -0
@@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
   	unlink(git_path_merge_mode(the_repository));
   	unlink(git_path_squash_msg(the_repository));
+	apply_autostash(git_path_merge_autostash(the_repository));
+

It's hard to tell from the limited context but do we want to run commit_index_files() before applying the autostash?

Quoted 7 lines

I don't think it really matters which order we run it in. When we run apply_autostash(), we only ever touch the working tree, not the index so it doesn't matter if it's run before or after. I'd prefer to keep it here because if we ever refactor this to use remove_merge_branch_state(), the person working on this will be able to perform the refactor more easily without having to worry about implicit ordering dependencies.

Thinking a bit more about this we definitely want to commit the index files before applying the stash as that is done in a separate process so we want our index written to disk first.

'git stash apply' does touch the index while it's merging and then tries to reset it to the pre-merge state if the merge has no conflicts. If the user runs
git add new-file
git merge --autostash branch
and new-file is not in branch then 'git stash apply' will add new-file to the index

Quoted 7 lines
Quoted 2 lines

I wonder if this should
be using remove_merge_branch_state() instead of duplicating it as well.

We can _almost_ use remove_branch_state() even! Unfortunately, remove_merge_branch_state() calls unlink(git_path_merge_rr(r)) which we cannot do since repo_rerere() relies on it later. Perhaps we can can move repo_rerere() earlier?

I think we should move apply_autostash() down so that repo_rerere() is called with the index that we've committed before we apply the stash which might add conflicts or new files. We should probably run the post-commit and post-rewrite hooks before we apply the autostash as well to avoid changing the existing behaviour.

If we aim for something like
commit_index_files()
repo_rerere()
stash_oid = read_autostash_oid()
cleanup_branch_state()
run_post_commit_hook()
run_post_rewrite_hook()
print_commit_summary()
apply_autostash(stash_oid)

Then we keep the existing behaviour for rerere and the hooks, and the commit summary is printed before all the status junk that apply_autostash() spews out

We probably need to check the where 'git merge' applies the autostash as well to make sure it is writing the index to disk, calling the post-merge hook and printing any messages before applying the stash

Best Wishes

Phillip

Quoted 52 lines
Quoted 15 lines
Quoted 15 lines
builtin/reset.c +1 -1
index 18228c312e..038c8532eb 100644
@@ -25,6 +25,7 @@
   #include "cache-tree.h"
   #include "submodule.h"
   #include "submodule-config.h"
+#include "sequencer.h"
   #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
   		if (reset_type == HARD && !update_ref_status && !quiet)
   			print_new_head_line(lookup_commit_reference(the_repository, &oid));
   	}
-	if (!pathspec.nr)
Quoted 8 lines

builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 445

Quoted 5 lines
builtin/reset.c +4 -0
+	if (!pathspec.nr) {
+		if (reset_type == HARD)
+			save_autostash(git_path_merge_autostash(the_repository));
+
   		remove_branch_state(the_repository, 0);

This removes the autostash file for all reset types but we only keep the stash in the case of 'reset --hard' which is confusing.

I was worried that this change would be controversial... The rationale behind this change was that with reset --hard, we want to leave a clean working tree behind so we save it into the stash reflog. In all other cases, remove_branch_state() will apply the saved stash entry which should be fine since users don't expect a clean worktree with the other reset types.

I considered saving the autostash in all cases of reset but git merge --abort invokes git reset --merge behind the scenes so we'd have to consider that. Perhaps we can make all resets save the stash entry and, in the case of merge --abort, we can add some extra logic to subvert this so that the stash entry is applied?

I'm not sure about what the most intuitive behaviour is. I thought that this implementation would be the best but I'm not entirely sure. I'd appreciate some more discussion on this.

Thanks,

Denton

Quoted 7 lines

Best Wishes

Phillip

Quoted 3 lines
builtin/reset.c +1 -0
+	}
   	return update_ref_status;
   }
author
#80 lore raw

Re: [PATCH v3 17/19] merge: teach --autostash option

In reply to Phillip Wood

Hi Phillip,

Quoted 48 lines · Phillip Wood

Hi Denton

Quoted 45 lines · Denton Liu
Quoted 31 lines · Denton Liu
Quoted 15 lines
Quoted 15 lines
builtin/reset.c +1 -1
index 18228c312e..038c8532eb 100644
@@ -25,6 +25,7 @@
   #include "cache-tree.h"
   #include "submodule.h"
   #include "submodule-config.h"
+#include "sequencer.h"
   #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
   		if (reset_type == HARD && !update_ref_status && !quiet)
   			print_new_head_line(lookup_commit_reference(the_repository, &oid));
   	}
-	if (!pathspec.nr)
Quoted 8 lines

builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 445

Quoted 5 lines
builtin/reset.c +4 -0
+	if (!pathspec.nr) {
+		if (reset_type == HARD)
+			save_autostash(git_path_merge_autostash(the_repository));
+
   		remove_branch_state(the_repository, 0);

This removes the autostash file for all reset types but we only keep the stash in the case of 'reset --hard' which is confusing.

I was worried that this change would be controversial... The rationale behind this change was that with reset --hard, we want to leave a clean working tree behind so we save it into the stash reflog. In all other cases, remove_branch_state() will apply the saved stash entry which should be fine since users don't expect a clean worktree with the other reset types.

Quoted 5 lines

I considered saving the autostash in all cases of reset but git merge --abort invokes git reset --merge behind the scenes so we'd have to consider that. Perhaps we can make all resets save the stash entry and, in the case of merge --abort, we can add some extra logic to subvert this so that the stash entry is applied?

Perhaps something like this?

-- >8 --
commit 14d0b569cb7675f00d32d3d7fad7564fcaeca458

Author: Denton Liu <[email protected]>
Date:   Fri Apr 3 06:50:34 2020 -0400
Quoted 7 lines
Quoted 3 lines

squash! merge: teach --autostash option
Stash is saved when any reset is run, when git merge --abort is run, stash is applied.

I think this is the easiest behavior to understand, it avoids changing the behavior of reset, in particular it stops 'reset --mixed/--soft' from suddenly starting to touch the working tree.

Great, I'll use this approach then.

Quoted 14 lines
Quoted 14 lines
builtin/merge.c +2 -0
index 9573d77096..31b82d614c 100644
@@ -1242,6 +1242,8 @@ static int merging_a_throwaway_tag(struct commit *commit)
  	return is_throwaway_tag;
  }
+static GIT_PATH_FUNC(git_path_merge_autostash_saved, "MERGE_AUTOSTASH_SAVED")
+
  int cmd_merge(int argc, const char **argv, const char *prefix)
  {
  	struct object_id result_tree, stash, head_oid;
@@ -1295,9 +1297,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
  		if (!file_exists(git_path_merge_head(the_repository)))
Quoted 8 lines

builtin/merge.c int cmd_merge(int argc, const char **argv, const char *prefix) · line 1303

Quoted 5 lines
builtin/merge.c +4 -0
  			die(_("There is no merge to abort (MERGE_HEAD missing)."));
+		if (file_exists(git_path_merge_autostash(the_repository))) {
+			if (rename(git_path_merge_autostash(the_repository),
+						git_path_merge_autostash_saved()))
+				die_errno(_("failed to rename autostash"));

This is a bit of a performance, can't we just remember the stash oid in a variable and tweak the apply code?

This makes me a little bit sad because we basically have to duplicate the file-handling portion of apply_save_autostash() but I agree that doing your way should be better.

Quoted 12 lines

builtin/merge.c int cmd_merge(int argc, const char **argv, const char *prefix) · line 1309

Quoted 7 lines
builtin/merge.c +4 -1
+		}
+
  		/* Invoke 'git reset --merge' */
  		ret = cmd_reset(nargc, nargv, prefix);
-		apply_autostash(git_path_merge_autostash(the_repository));
+
+		apply_autostash(git_path_merge_autostash_saved());

Calling cmd_reset() was already a bit dodgy by our normal rules, now we're calling other functions after it though I guess given the current autostash implementation it's mostly in a separate process.

Quoted 2 lines

I think this is a good direction to go in
BTW what message gets printed when the stash is saved?

This is the message that is printed:

	$ git reset --hard
	HEAD is now at c4c4222 commit 1
	Autostash exists; creating a new stash entry.
	Your changes are safe in the stash.
	You can run "git stash pop" or "git stash drop" at any time.

Thanks,

Denton

Quoted 36 lines

Best Wishes

Phillip

Quoted 32 lines
builtin/merge.c +0 -0
  		goto done;
  	}
builtin/reset.c +1 -3
index 038c8532eb..060470c455 100644
@@ -439,9 +439,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
  			print_new_head_line(lookup_commit_reference(the_repository, &oid));
  	}
  	if (!pathspec.nr) {
-		if (reset_type == HARD)
-			save_autostash(git_path_merge_autostash(the_repository));
-
+		save_autostash(git_path_merge_autostash(the_repository));
  		remove_branch_state(the_repository, 0);
  	}
Quoted 16 lines

I'm not sure about what the most intuitive behaviour is. I thought that this implementation would be the best but I'm not entirely sure. I'd appreciate some more discussion on this.

Thanks,

Denton

Quoted 7 lines

Best Wishes

Phillip

Quoted 3 lines
builtin/reset.c +1 -0
+	}
   	return update_ref_status;
   }
author
#82 lore raw

Re: [PATCH v3 17/19] merge: teach --autostash option

In reply to Phillip Wood

Hi Phillip,

Quoted 70 lines · Phillip Wood
Quoted 25 lines · Phillip Wood
Quoted 15 lines
Quoted 15 lines
branch.c +1 -0
index 579494738a..bf2536c70d 100644
@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
   	unlink(git_path_merge_rr(r));
   	unlink(git_path_merge_msg(r));
   	unlink(git_path_merge_mode(r));
+	apply_autostash(git_path_merge_autostash(r));
   }
   void remove_branch_state(struct repository *r, int verbose)
builtin/commit.c +0 -0
index 7ba33a3bec..c11894423a 100644
Quoted 8 lines

builtin/commit.c int cmd_commit(int argc, const char **argv, const char *prefix) · line 1690

Quoted 5 lines
builtin/commit.c +2 -0
@@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
   	unlink(git_path_merge_mode(the_repository));
   	unlink(git_path_squash_msg(the_repository));
+	apply_autostash(git_path_merge_autostash(the_repository));
+

It's hard to tell from the limited context but do we want to run commit_index_files() before applying the autostash?

Quoted 7 lines

I don't think it really matters which order we run it in. When we run apply_autostash(), we only ever touch the working tree, not the index so it doesn't matter if it's run before or after. I'd prefer to keep it here because if we ever refactor this to use remove_merge_branch_state(), the person working on this will be able to perform the refactor more easily without having to worry about implicit ordering dependencies.

Thinking a bit more about this we definitely want to commit the index files before applying the stash as that is done in a separate process so we want our index written to disk first.

'git stash apply' does touch the index while it's merging and then tries to reset it to the pre-merge state if the merge has no conflicts. If the user runs
git add new-file
git merge --autostash branch
and new-file is not in branch then 'git stash apply' will add new-file to the index

Quoted 7 lines
Quoted 2 lines

I wonder if this should
be using remove_merge_branch_state() instead of duplicating it as well.

We can _almost_ use remove_branch_state() even! Unfortunately, remove_merge_branch_state() calls unlink(git_path_merge_rr(r)) which we cannot do since repo_rerere() relies on it later. Perhaps we can can move repo_rerere() earlier?

I think we should move apply_autostash() down so that repo_rerere() is called with the index that we've committed before we apply the stash which might add conflicts or new files. We should probably run the post-commit and post-rewrite hooks before we apply the autostash as well to avoid changing the existing behaviour.

If we aim for something like
commit_index_files()
repo_rerere()
stash_oid = read_autostash_oid()
cleanup_branch_state()
run_post_commit_hook()
run_post_rewrite_hook()
print_commit_summary()
apply_autostash(stash_oid)

Quoted 3 lines

Then we keep the existing behaviour for rerere and the hooks, and the commit summary is printed before all the status junk that apply_autostash() spews out

Makes sense, I'll make this change.

Quoted 3 lines

We probably need to check the where 'git merge' applies the autostash as well to make sure it is writing the index to disk, calling the post-merge hook and printing any messages before applying the stash

It currently applies the autostash in finish(). After reviewing it, I've moved the apply_autostash() call to the end of that function.

Actually, going back to review some more callsites of apply_autostash(), the one in remove_merge_branch_state() doesn't really make sense. We should really be calling save_autostash() in there so that we don't accidentally dirty the worktree unexpectedly. The bonus to this is that I've removed the save_autostash() call in git reset so it should just be able to call remove_branch_state() and have everything just work.

As a result of these changes, we now only introduce three callsites of apply_autostash(), which are all explicit:

1. At the end of git commit
2. At the end of finish() in git merge
3. At the end of git merge --abort

I'll send up a new version later today.

Thanks for helping shape this topic up,

Denton

Quoted 3 lines

Best Wishes

Phillip

author
#88 lore raw

PATCH v4 2/23 t7600: use test_write_lines()

In reply to Denton Liu

In t7600, we were rewriting printf '%s\n' ... to create files from parameters, one per line. However, we already have a function that wraps this for us: test_write_lines(). Rewrite these instances to use that function instead of open coding it.

---
 t/t7600-merge.sh | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)
t/t7600-merge.sh +11 -11
index 132608879a..4fa0ef8e3b 100755
@@ -29,15 +29,15 @@ Testing basic merge operations/option parsing.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
-printf '%s\n' 1 2 3 4 5 6 7 8 9 >file
-printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >file.1
-printf '%s\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5
-printf '%s\n' 1 2 3 4 5 6 7 8 '9 X' >file.9
-printf '%s\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y
-printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >result.1
-printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
-printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
-printf '%s\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z
+test_write_lines 1 2 3 4 5 6 7 8 9 >file
+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1
+test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5
+test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9
+test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y
+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1
+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
+test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z
 
 create_merge_msgs () {
 	echo "Merge tag 'c2'" >msg.1-5 &&
@@ -81,7 +81,7 @@ verify_head () {
 }
 
 verify_parents () {
-	printf '%s\n' "$@" >parents.expected &&
+	test_write_lines "$@" >parents.expected &&
 	>parents.actual &&
 	i=1 &&
 	while test $i -le $#
@@ -95,7 +95,7 @@ verify_parents () {
 }
 
 verify_mergeheads () {
-	printf '%s\n' "$@" >mergehead.expected &&
+	test_write_lines "$@" >mergehead.expected &&
 	while read sha1 rest
 	do
 		git rev-parse $sha1
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#86 lore raw

v4 cover merge: learn --autostash

In reply to Denton Liu

Hi Phillip,

I implemented all of your comments. Patches 3-5, 11 need careful review as they're new. 'merge: teach --autostash option' also probably needs some more review as I've changed the callsites of apply_autostash() and save_autostash().

Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.

The majority of this patchset is spent refactoring. This is so that the actual implementation in merge is done as simply as possible.

Changes since v3:

* Replace 'sequencer: use file strbuf for read_oneliner()' with patches 3-5

* Rename flag to READ_ONELINER_WARN_MISSING

* Add 'sequencer: rename stash_sha1 to stash_oid'

* Many changes to 'merge: teach --autostash option'

Changes since v2:

* Change patch 1 to refer to ASCII order[2]

* Make read_oneliner() accept a collective flags arg instead of two separate args[3]

* git reset --hard now leaves the worktree clean[4]

* Rebased against 'master' since the base is quite old and a new release is coming out soon

Changes since v1:

* Completely changed how the refactoring was done

* More tests and documentation

[1]: https://github.com/gitgitgadget/git/issues/394
[2]: https://lore.kernel.org/git/[email protected]/
[3]: https://lore.kernel.org/git/[email protected]/
[4]: https://lore.kernel.org/git/20200123042906.GA29009@generichostname/

Denton Liu (23):
Makefile: ASCII-sort += lists
t7600: use test_write_lines()
sequencer: stop leaking buf
sequencer: reuse strbuf_trim() in read_oneliner()
sequencer: make file exists check more efficient
sequencer: make read_oneliner() accept flags
sequencer: configurably warn on non-existent files
sequencer: make read_oneliner() extern
rebase: use read_oneliner()
sequencer: make apply_rebase() accept a path
sequencer: rename stash_sha1 to stash_oid
rebase: use apply_autostash() from sequencer.c
rebase: generify reset_head()
reset: extract reset_head() from rebase
rebase: extract create_autostash()
rebase: generify create_autostash()
sequencer: extract perform_autostash() from rebase
sequencer: unlink autostash in apply_autostash()
sequencer: implement save_autostash()
sequencer: implement apply_autostash_oid()
merge: teach --autostash option
t5520: make test_pull_autostash() accept expect_parent_num
pull: pass --autostash to merge

 Documentation/config/merge.txt  |  10 +
 Documentation/git-merge.txt     |   6 +-
 Documentation/git-pull.txt      |   9 -
 Documentation/merge-options.txt |   8 +
 Makefile                        |  78 ++++----
 branch.c                        |   1 +
 builtin/commit.c                |   2 +
 builtin/merge.c                 |  27 +++
 builtin/pull.c                  |   9 +-
 builtin/rebase.c                | 312 +++++---------------------------
 builtin/reset.c                 |   4 +-
 parse-options.h                 |   1 +
 path.c                          |   1 +
 path.h                          |   4 +-
 reset.c                         | 140 ++++++++++++++
 reset.h                         |  20 ++
 sequencer.c                     | 187 +++++++++++++------
 sequencer.h                     |  20 ++
 t/t3033-merge-toplevel.sh       |  22 +++
 t/t5520-pull.sh                 |  57 ++++--
 t/t7600-merge.sh                | 140 ++++++++++++--
 21 files changed, 653 insertions(+), 405 deletions(-)
 create mode 100644 reset.c
 create mode 100644 reset.h

Range-diff against v3:

 1:  67511a47b2 =  1:  c26771e934 Makefile: ASCII-sort += lists
 2:  6d76cea5b4 =  2:  059e0e8e43 t7600: use test_write_lines()
 3:  7c254b34b0 <  -:  ---------- sequencer: use file strbuf for read_oneliner()
 -:  ---------- >  3:  76585e5b13 sequencer: stop leaking buf
 -:  ---------- >  4:  c7a3cfa200 sequencer: reuse strbuf_trim() in read_oneliner()
 -:  ---------- >  5:  58da8e9555 sequencer: make file exists check more efficient
 4:  dc88da2ebf !  6:  6ead240957 sequencer: make read_oneliner() accept flags

@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
+
/*
- * Reads a file that was presumably written by a shell script, i.e. with an
- * end-of-line marker that needs to be stripped.
+ * Resets a strbuf then reads a file that was presumably written by a shell
+ * script, i.e. with an end-of-line marker that needs to be stripped.
@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,
* Returns 1 if the file was read, 0 if it could not be read or does not exist. */
@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
- const char *path, int skip_if_empty)
+ const char *path, unsigned flags)
{
- int ret = 0;
- struct strbuf file_buf = STRBUF_INIT;
+ strbuf_reset(buf);
+ if (strbuf_read_file(buf, path, 0) < 0) {
@@ sequencer.c: static int read_oneliner(struct strbuf *buf,

- strbuf_trim_trailing_newline(&file_buf);
+ strbuf_trim(buf);

-- if (skip_if_empty && !file_buf.len)
-+ if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !file_buf.len)
- goto done;
+- if (skip_if_empty && !buf->len)
++ if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !buf->len)
+ return 0;

- strbuf_addbuf(buf, &file_buf);
+ return 1;
@@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
- if (is_rebase_i(opts)) {
struct strbuf buf = STRBUF_INIT;
+ int ret = 0;

- if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
+ if (read_oneliner(&buf, rebase_path_gpg_sign_opt(),
@@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
strbuf_reset(&buf);
else {
@@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
- strbuf_reset(&buf);
+ }
}

- if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
@@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
opts->allow_rerere_auto = RERERE_AUTOUPDATE;
else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
@@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
- strbuf_release(&buf);
+ read_strategy_opts(opts, &buf);

if (read_oneliner(&opts->current_fixups,
- rebase_path_current_fixups(), 1)) {

 5:  7702543d91 <  -:  ---------- sequencer: configurably warn on non-existent files
 -:  ---------- >  7:  2e7922b259 sequencer: configurably warn on non-existent files
 6:  361bc2e885 !  8:  6c3c37994b sequencer: make read_oneliner() extern

@@ Commit message
Instead of hiding it as a static function within sequencer.c, extern it so that it can be reused by others.

+ This patch is best viewed with --color-moved.
+
## sequencer.c ##
@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,
return 0;
}

-#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
--#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
+-#define READ_ONELINER_WARN_MISSING (1 << 1)
-
-/*
-- * Reads a file that was presumably written by a shell script, i.e. with an -- * end-of-line marker that needs to be stripped.
+- * Resets a strbuf then reads a file that was presumably written by a shell +- * script, i.e. with an end-of-line marker that needs to be stripped.
- *
- * Note that only the last end-of-line marker is stripped, consistent with the
- * behavior of "$(cat path)" in a shell script.
@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
+int read_oneliner(struct strbuf *buf,
const char *path, unsigned flags)
{
- int ret = 0;
+ strbuf_reset(buf);

## sequencer.h ##
@@ sequencer.h: void print_commit_summary(struct repository *repo,
@@ sequencer.h: void print_commit_summary(struct repository *repo,
unsigned int flags);

+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
-+#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
++#define READ_ONELINER_WARN_MISSING (1 << 1)
+
+/*
-+ * Reads a file that was presumably written by a shell script, i.e. with an -+ * end-of-line marker that needs to be stripped.
++ * Resets a strbuf then reads a file that was presumably written by a shell ++ * script, i.e. with an end-of-line marker that needs to be stripped.
+ *
+ * Note that only the last end-of-line marker is stripped, consistent with the
+ * behavior of "$(cat path)" in a shell script.
+ *
-+ * Returns 1 if the file was read, 0 if it could not be read.
++ * Returns 1 if the file was read, 0 if it could not be read or does not exist.
+ */
+int read_oneliner(struct strbuf *buf,
+ const char *path, unsigned flags);
7: 47816abff1 ! 9: 689f34a2a5 rebase: use read_oneliner()

    @@ Commit message
     
         Since in sequencer.c, read_one() basically duplicates the functionality
         of read_oneliner(), reduce code duplication by replacing read_one() with
    -    read_oneliner().
    +    read_oneliner(). Also, delete strbuf_reset() calls prior to
    +    read_oneliner() as read_oneliner() already resets the strbuf.
     
    -    This was done with the following Coccinelle script
    +    This was mostly done with the following Coccinelle script
     
                 @@
                 expression a, b;
    @@ Commit message
                 - read_one(a, b)
                 + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)
     
    -    and long lines were manually broken up.
    +    Long lines were broken up and strbuf_reset()s were deleted manually.
     
      ## builtin/rebase.c ##
     @@ builtin/rebase.c: static const char *state_dir_path(const char *filename, struct rebase_options *o
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
     -	if (read_one(state_dir_path("head-name", opts), &head_name) ||
     -	    read_one(state_dir_path("onto", opts), &buf))
     +	if (!read_oneliner(&head_name, state_dir_path("head-name", opts),
    -+			   READ_ONELINER_WARN_NON_EXISTENCE) ||
    ++			   READ_ONELINER_WARN_MISSING) ||
     +	    !read_oneliner(&buf, state_dir_path("onto", opts),
    -+			   READ_ONELINER_WARN_NON_EXISTENCE))
    ++			   READ_ONELINER_WARN_MISSING))
      		return -1;
      	opts->head_name = starts_with(head_name.buf, "refs/") ?
      		xstrdup(head_name.buf) : NULL;
     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    + 	 * head. Fall back to reading from head to cover for the case that the
    + 	 * user upgraded git with an ongoing interactive rebase.
      	 */
    - 	strbuf_reset(&buf);
    +-	strbuf_reset(&buf);
      	if (file_exists(state_dir_path("orig-head", opts))) {
     -		if (read_one(state_dir_path("orig-head", opts), &buf))
     +		if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
    -+				   READ_ONELINER_WARN_NON_EXISTENCE))
    ++				   READ_ONELINER_WARN_MISSING))
      			return -1;
     -	} else if (read_one(state_dir_path("head", opts), &buf))
     +	} else if (!read_oneliner(&buf, state_dir_path("head", opts),
    -+				  READ_ONELINER_WARN_NON_EXISTENCE))
    ++				  READ_ONELINER_WARN_MISSING))
      		return -1;
      	if (get_oid(buf.buf, &opts->orig_head))
      		return error(_("invalid orig-head: '%s'"), buf.buf);
     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    + 	}
      
      	if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
    - 		strbuf_reset(&buf);
    +-		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
     -			    &buf))
     +		if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
    -+				   READ_ONELINER_WARN_NON_EXISTENCE))
    ++				   READ_ONELINER_WARN_MISSING))
      			return -1;
      		if (!strcmp(buf.buf, "--rerere-autoupdate"))
      			opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    + 	}
      
      	if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
    - 		strbuf_reset(&buf);
    +-		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("gpg_sign_opt", opts),
     -			    &buf))
     +		if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
    -+				   READ_ONELINER_WARN_NON_EXISTENCE))
    ++				   READ_ONELINER_WARN_MISSING))
      			return -1;
      		free(opts->gpg_sign_opt);
      		opts->gpg_sign_opt = xstrdup(buf.buf);
    -@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    + 	}
      
      	if (file_exists(state_dir_path("strategy", opts))) {
    - 		strbuf_reset(&buf);
    +-		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("strategy", opts), &buf))
     +		if (!read_oneliner(&buf, state_dir_path("strategy", opts),
    -+				   READ_ONELINER_WARN_NON_EXISTENCE))
    ++				   READ_ONELINER_WARN_MISSING))
      			return -1;
      		free(opts->strategy);
      		opts->strategy = xstrdup(buf.buf);
    -@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    + 	}
      
      	if (file_exists(state_dir_path("strategy_opts", opts))) {
    - 		strbuf_reset(&buf);
    +-		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("strategy_opts", opts), &buf))
     +		if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
    -+				   READ_ONELINER_WARN_NON_EXISTENCE))
    ++				   READ_ONELINER_WARN_MISSING))
      			return -1;
      		free(opts->strategy_opts);
      		opts->strategy_opts = xstrdup(buf.buf);
    @@ builtin/rebase.c: static int apply_autostash(struct rebase_options *opts)
      		return 0;
      
     -	if (read_one(path, &autostash))
    -+	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
    ++	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))
      		return error(_("Could not read '%s'"), path);
      	/* Ensure that the hash is not mistaken for a number */
      	strbuf_addstr(&autostash, "^0");
 8:  7e9a86c337 = 10:  e0b8e409af sequencer: make apply_rebase() accept a path
 -:  ---------- > 11:  a52583beff sequencer: rename stash_sha1 to stash_oid
 9:  76a486d8b0 ! 12:  389b17df33 rebase: use apply_autostash() from sequencer.c

@@ Commit message

The apply_autostash() function in builtin/rebase.c is similar enough to the apply_autostash() function in sequencer.c that they are almost
- interchangeable. Make the sequencer.c version extern and use it in
- rebase.
+ interchangeable, except for the type of arg they accept. Make the
+ sequencer.c version extern and use it in rebase.

The rebase version was introduced in 6defce2b02 (builtin rebase: support --autostash option, 2018-09-04) as part of the shell to C conversion.
@@ builtin/rebase.c: static int rebase_write_basic_state(struct rebase_options *opt
- if (!file_exists(path))
- return 0;
-
-- if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
+- if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))
- return error(_("Could not read '%s'"), path);
- /* Ensure that the hash is not mistaken for a number */
- strbuf_addstr(&autostash, "^0");
@@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
-static int apply_autostash(const char *path)
+int apply_autostash(const char *path)
{
- struct strbuf stash_sha1 = STRBUF_INIT;
+ struct strbuf stash_oid = STRBUF_INIT;
struct child_process child = CHILD_PROCESS_INIT;

## sequencer.h ##

10:  6d37e8049b = 13:  fe57e21dbc rebase: generify reset_head()
11:  18e1900a16 = 14:  045668620d reset: extract reset_head() from rebase
12:  21e228920e = 15:  d5f51cd80e rebase: extract create_autostash()
13:  7e2eab5203 = 16:  9ab10d23d4 rebase: generify create_autostash()
14:  fddf506115 ! 17:  26cca49be6 sequencer: extract perform_autostash() from rebase

@@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
+
int apply_autostash(const char *path)
{
- struct strbuf stash_sha1 = STRBUF_INIT;
+ struct strbuf stash_oid = STRBUF_INIT;

## sequencer.h ##
@@ sequencer.h: void commit_post_rewrite(struct repository *r,
15: 9394c7ae30 ! 18: e703022fda sequencer: unlink autostash in apply_autostash()

    @@ sequencer.c: int apply_autostash(const char *path)
      	}
      
     +	unlink(path);
    - 	strbuf_release(&stash_sha1);
    + 	strbuf_release(&stash_oid);
      	return ret;
      }
16:  0035b84388 ! 19:  75dc3f10a1 sequencer: implement save_autostash()
    @@ Commit message
         apply_save_autostash() and use it to implement save_autostash(). This
         function will be used in a future commit.
     
    +    The difference between save_autostash() and apply_autostash() is that
    +    the latter does not try to apply the stash. It skips that step and
    +    just stores the created entry in the stash reflog.
    +
      ## sequencer.c ##
     @@ sequencer.c: void create_autostash(struct repository *r, const char *path,
      	strbuf_release(&buf);
    @@ sequencer.c: void create_autostash(struct repository *r, const char *path,
     -int apply_autostash(const char *path)
     +static int apply_save_autostash(const char *path, int attempt_apply)
      {
    - 	struct strbuf stash_sha1 = STRBUF_INIT;
    + 	struct strbuf stash_oid = STRBUF_INIT;
      	struct child_process child = CHILD_PROCESS_INIT;
     @@ sequencer.c: int apply_autostash(const char *path)
      	}
    - 	strbuf_trim(&stash_sha1);
    + 	strbuf_trim(&stash_oid);
      
     -	child.git_cmd = 1;
     -	child.no_stdout = 1;
     -	child.no_stderr = 1;
     -	argv_array_push(&child.args, "stash");
     -	argv_array_push(&child.args, "apply");
    --	argv_array_push(&child.args, stash_sha1.buf);
    +-	argv_array_push(&child.args, stash_oid.buf);
     -	if (!run_command(&child))
     +	if (attempt_apply) {
     +		child.git_cmd = 1;
    @@ sequencer.c: int apply_autostash(const char *path)
     +		child.no_stderr = 1;
     +		argv_array_push(&child.args, "stash");
     +		argv_array_push(&child.args, "apply");
    -+		argv_array_push(&child.args, stash_sha1.buf);
    ++		argv_array_push(&child.args, stash_oid.buf);
     +		ret = run_command(&child);
     +	}
     +
    @@ sequencer.c: int apply_autostash(const char *path)
      	else {
      		struct child_process store = CHILD_PROCESS_INIT;
     @@ sequencer.c: int apply_autostash(const char *path)
    - 			ret = error(_("cannot store %s"), stash_sha1.buf);
    + 			ret = error(_("cannot store %s"), stash_oid.buf);
      		else
      			fprintf(stderr,
     -				_("Applying autostash resulted in conflicts.\n"
 -:  ---------- > 20:  598ddea6c1 sequencer: implement apply_autostash_oid()
17:  085f8e0dd2 ! 21:  7adf794192 merge: teach --autostash option

@@ Commit message
git fetch ...
git merge --autostash FETCH_HEAD

- When git reset --hard is run to abort a merge, the working tree will
- be left in a clean state, as expected, with the autostash pushed onto
- the stash stack.
+ When an autostash is generated, it is automatically reapplied to the
+ worktree only in three explicit situations:
+
+ 1. An incomplete merge is commit using git commit.
+ 2. A merge completes successfully.
+ 3. A merge is aborted using git merge --abort.
+
+ In all other situations where the merge state is removed using
+ remove_merge_branch_state() such as aborting a merge via
+ git reset --hard, the autostash is saved into the stash reflog
+ instead keeping the worktree clean.

@@ Documentation/config/merge.txt: merge.stat::
+merge.autoStash::
+ When set to true, automatically create a temporary stash entry
+ before the operation begins, and apply it after the operation
-+ ends. This means that you can run rebase on a dirty worktree.
++ ends. This means that you can run merge on a dirty worktree.
+ However, use with care: the final stash application after a
-+ successful rebase might result in non-trivial conflicts.
++ successful merge might result in non-trivial conflicts.
+ This option can be overridden by the --no-autostash and
+ --autostash options of linkgit:git-merge[1].
+ Defaults to false.
@@ Documentation/config/merge.txt: merge.stat::
Controls which merge tool is used by linkgit:git-mergetool[1]. The list below shows the valid built-in values.

+ ## Documentation/git-merge.txt ##
+@@ Documentation/git-merge.txt: will be appended to the specified message.
+
+ --abort::
+ Abort the current conflict resolution process, and
+- try to reconstruct the pre-merge state.
++ try to reconstruct the pre-merge state. If an autostash entry is
++ present, apply it back to the worktree.
+ +
+ If there were uncommitted worktree changes present when the merge
+ started, 'git merge --abort' will in some cases be unable to
+@@ Documentation/git-merge.txt: reconstruct these changes. It is therefore recommended to always
+ commit or stash your changes before running 'git merge'.
+ +
+ 'git merge --abort' is equivalent to 'git reset --merge' when
+-MERGE_HEAD is present.
++MERGE_HEAD is present unless MERGE_AUTOSTASH is also present where
++the stash entry is applied to the worktree.
+
+ --quit::
+ Forget about the current merge in progress. Leave the index
+
## Documentation/merge-options.txt ##
@@ Documentation/merge-options.txt: ifndef::git-pull[]
Note that not all merge strategies may support progress
@@ Documentation/merge-options.txt: ifndef::git-pull[]
+--no-autostash::
+ Automatically create a temporary stash entry before the operation
+ begins, and apply it after the operation ends. This means
-+ that you can run rebase on a dirty worktree. However, use
++ that you can run the operation on a dirty worktree. However, use
+ with care: the final stash application after a successful
-+ rebase might result in non-trivial conflicts.
++ merge might result in non-trivial conflicts.
+
endif::git-pull[]

@@ branch.c: void remove_merge_branch_state(struct repository *r)
unlink(git_path_merge_rr(r)); unlink(git_path_merge_msg(r)); unlink(git_path_merge_mode(r));
-+ apply_autostash(git_path_merge_autostash(r));
++ save_autostash(git_path_merge_autostash(r));
}

void remove_branch_state(struct repository *r, int verbose)

## builtin/commit.c ##
@@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix)
- unlink(git_path_merge_mode(the_repository));
- unlink(git_path_squash_msg(the_repository));
+ &oid, flags);
+ }

+ apply_autostash(git_path_merge_autostash(the_repository));
+
- if (commit_index_files())
- die(_("repository has been updated, but unable to write\n"
- "new_index file. Check that disk is not full and quota is\n"
+ UNLEAK(err);
+ UNLEAK(sb);
+ return 0;

## builtin/merge.c ##
@@ builtin/merge.c: static int show_progress = -1;
@@ builtin/merge.c: static struct option builtin_merge_options[] = {
OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-+ OPT_BOOL(0, "autostash", &autostash,
-+ N_("automatically stash/stash pop before and after")),
++ OPT_AUTOSTASH(&autostash),
OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")), OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ builtin/merge.c: static void finish(struct commit *head_commit,
- strbuf_addf(&reflog_message, "%s: %s",
- getenv("GIT_REFLOG_ACTION"), msg);
- }
+ /* Run a post-merge hook */
+ run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
+
+ apply_autostash(git_path_merge_autostash(the_repository));
- if (squash) {
- squash_message(head_commit, remoteheads);
- } else {
+ strbuf_release(&reflog_message);
+ }
+
@@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void *cb)
return 0;
} else if (!strcmp(k, "gpg.mintrustlevel")) {
@@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void

status = fmt_merge_msg_config(k, v, cb);
@@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
+ if (abort_current_merge) {
+ int nargc = 2;
+ const char *nargv[] = {"reset", "--merge", NULL};
++ struct strbuf stash_oid = STRBUF_INIT;

+ if (orig_argc != 2)
+ usage_msg_opt(_("--abort expects no arguments"),
+@@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
+ if (!file_exists(git_path_merge_head(the_repository)))
+ die(_("There is no merge to abort (MERGE_HEAD missing)."));
+
++ if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),
++ READ_ONELINER_SKIP_IF_EMPTY)) {
++ strbuf_trim(&stash_oid);
++ unlink(git_path_merge_autostash(the_repository));
++ }
++
/* Invoke 'git reset --merge' */
ret = cmd_reset(nargc, nargv, prefix);
-+ apply_autostash(git_path_merge_autostash(the_repository));
++
++ if (stash_oid.len)
++ apply_autostash_oid(stash_oid.buf);
++
++ strbuf_release(&stash_oid);
goto done;
}

@@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
git_committer_info(IDENT_STRICT);

+ ## builtin/rebase.c ##
+@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
+ { OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
+ N_("GPG-sign commits"),
+ PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+- OPT_BOOL(0, "autostash", &options.autostash,
+- N_("automatically stash/stash pop before and after")),
++ OPT_AUTOSTASH(&options.autostash),
+ OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
+ N_("add exec lines after each commit of the "
+ "editable list")),
+
## builtin/reset.c ##
@@
#include "cache-tree.h"
@@ builtin/reset.c: int cmd_reset(int argc, const char **argv, const char *prefix)
}
- if (!pathspec.nr)
+ if (!pathspec.nr) {
-+ if (reset_type == HARD)
-+ save_autostash(git_path_merge_autostash(the_repository));
-+
remove_branch_state(the_repository, 0);
+ }

return update_ref_status;
}

+ ## parse-options.h ##
+@@ parse-options.h: int parse_opt_passthru_argv(const struct option *, const char *, int);
+ #define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
+ #define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file"))
+ #define OPT_PATHSPEC_FILE_NUL(v) OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character")) ++#define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after"))
+
+ #endif
+
## path.c ##
@@ path.c: REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
@@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '
+ test_cmp file.1 file
+'
+
-+test_expect_success 'completed merge with --no-commit and --autostash' '
++test_expect_success 'completed merge (git commit) with --no-commit and --autostash' '
+ git reset --hard c1 &&
+ git merge-file file file.orig file.9 &&
+ git diff >expect &&
@@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '
+ test_cmp result.1-5-9 file
+'
+
++test_expect_success 'completed merge (git merge --continue) with --no-commit and --autostash' '
++ git reset --hard c1 &&
++ git merge-file file file.orig file.9 &&
++ git diff >expect &&
++ git merge --no-commit --autostash c2 &&
++ git stash show -p MERGE_AUTOSTASH >actual &&
++ test_cmp expect actual &&
++ git merge --continue 2>err &&
++ test_i18ngrep "Applied autostash." err &&
++ git show HEAD:file >merge-result &&
++ test_cmp result.1-5 merge-result &&
++ test_cmp result.1-5-9 file
++'
++
+test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '
+ git reset --hard c1 &&
+ git merge-file file file.orig file.9 &&

18:  4dabe214f2 = 22:  f1e54622fb t5520: make test_pull_autostash() accept expect_parent_num
19:  9b6f16f1bf ! 23:  177c7e537b pull: pass --autostash to merge

@@ Documentation/merge-options.txt: ifndef::git-pull[]
Automatically create a temporary stash entry before the operation
@@ Documentation/merge-options.txt: ifndef::git-pull[]
with care: the final stash application after a successful
- rebase might result in non-trivial conflicts.
+ merge might result in non-trivial conflicts.

-endif::git-pull[]
-

Signature · 1 line
2.26.0.159.g23e2136ad0
author
#89 lore raw

PATCH v4 3/23 sequencer: stop leaking buf

In read_populate_opts(), we call read_oneliner() _after_ calling strbuf_release(). This means that buf is leaked at the end of the function.

Always clean up the strbuf by going to done_rebase_i whether or not we return an error.

---
 sequencer.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
sequencer.c +9 -4
index e528225e78..faab0b13e8 100644
@@ -2485,6 +2485,7 @@ static int read_populate_opts(struct replay_opts *opts)
 {
 	if (is_rebase_i(opts)) {
 		struct strbuf buf = STRBUF_INIT;
+		int ret = 0;
 
 		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
 			if (!starts_with(buf.buf, "-S"))
@@ -2525,7 +2526,7 @@ static int read_populate_opts(struct replay_opts *opts)
 			opts->keep_redundant_commits = 1;
 
 		read_strategy_opts(opts, &buf);
-		strbuf_release(&buf);
+		strbuf_reset(&buf);
 
 		if (read_oneliner(&opts->current_fixups,
 				  rebase_path_current_fixups(), 1)) {
@@ -2538,12 +2539,16 @@ static int read_populate_opts(struct replay_opts *opts)
 		}
 
 		if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {
-			if (get_oid_hex(buf.buf, &opts->squash_onto) < 0)
-				return error(_("unusable squash-onto"));
+			if (get_oid_hex(buf.buf, &opts->squash_onto) < 0) {
+				ret = error(_("unusable squash-onto"));
+				goto done_rebase_i;
+			}
 			opts->have_squash_onto = 1;
 		}
 
-		return 0;
+done_rebase_i:
+		strbuf_release(&buf);
+		return ret;
 	}
 
 	if (!file_exists(git_path_opts_file()))
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#99 lore raw

PATCH v4 5/23 sequencer: make file exists check more efficient

In reply to Denton Liu

We currently check whether a file exists and return early before reading the file. Instead of accessing the file twice, always read the file and check errno to see if the file doesn't exist.

---
 sequencer.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)
sequencer.c +2 -5
index 09ca68f540..6e63224c41 100644
@@ -431,13 +431,10 @@ static int write_message(const void *buf, size_t len, const char *filename,
 static int read_oneliner(struct strbuf *buf,
 	const char *path, int skip_if_empty)
 {
-
-	if (!file_exists(path))
-		return 0;
-
 	strbuf_reset(buf);
 	if (strbuf_read_file(buf, path, 0) < 0) {
-		warning_errno(_("could not read '%s'"), path);
+		if (errno != ENOENT && errno != ENOTDIR)
+			warning_errno(_("could not read '%s'"), path);
 		return 0;
 	}
 
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#93 lore raw

PATCH v4 4/23 sequencer: reuse strbuf_trim() in read_oneliner()

In reply to Denton Liu

In the original read_oneliner() logic, we duplicated the logic for strbuf_trim_trailing_newline() with one exception: instead of preventing buffer accesses below index 0, it would prevent buffer accesses below index orig_len. Although this is correct, it isn't worth having the duplicated logic around.

Reset buf before using it then replace the trimming logic with strbuf_trim().

As a cleanup, remove all reset_strbuf()s that happen before read_oneliner() is called.

---
 sequencer.c | 18 +++++-------------
 1 file changed, 5 insertions(+), 13 deletions(-)
sequencer.c +5 -13
index faab0b13e8..09ca68f540 100644
@@ -420,8 +420,8 @@ static int write_message(const void *buf, size_t len, const char *filename,
 }
 
 /*
- * Reads a file that was presumably written by a shell script, i.e. with an
- * end-of-line marker that needs to be stripped.
+ * Resets a strbuf then reads a file that was presumably written by a shell
+ * script, i.e. with an end-of-line marker that needs to be stripped.
  *
  * Note that only the last end-of-line marker is stripped, consistent with the
  * behavior of "$(cat path)" in a shell script.
@@ -431,23 +431,19 @@ static int write_message(const void *buf, size_t len, const char *filename,
 static int read_oneliner(struct strbuf *buf,
 	const char *path, int skip_if_empty)
 {
-	int orig_len = buf->len;
 
 	if (!file_exists(path))
 		return 0;
 
+	strbuf_reset(buf);
 	if (strbuf_read_file(buf, path, 0) < 0) {
 		warning_errno(_("could not read '%s'"), path);
 		return 0;
 	}
 
-	if (buf->len > orig_len && buf->buf[buf->len - 1] == '\n') {
-		if (--buf->len > orig_len && buf->buf[buf->len - 1] == '\r')
-			--buf->len;
-		buf->buf[buf->len] = '\0';
-	}
+	strbuf_trim(buf);
 
-	if (skip_if_empty && buf->len == orig_len)
+	if (skip_if_empty && !buf->len)
 		return 0;
 
 	return 1;
@@ -2471,7 +2467,6 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
 
 static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
 {
-	strbuf_reset(buf);
 	if (!read_oneliner(buf, rebase_path_strategy(), 0))
 		return;
 	opts->strategy = strbuf_detach(buf, NULL);
@@ -2494,7 +2489,6 @@ static int read_populate_opts(struct replay_opts *opts)
 				free(opts->gpg_sign);
 				opts->gpg_sign = xstrdup(buf.buf + 2);
 			}
-			strbuf_reset(&buf);
 		}
 
 		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
@@ -2526,7 +2520,6 @@ static int read_populate_opts(struct replay_opts *opts)
 			opts->keep_redundant_commits = 1;
 
 		read_strategy_opts(opts, &buf);
-		strbuf_reset(&buf);
 
 		if (read_oneliner(&opts->current_fixups,
 				  rebase_path_current_fixups(), 1)) {
@@ -4006,7 +3999,6 @@ static int pick_commits(struct repository *r,
 				res = error(_("could not read orig-head"));
 				goto cleanup_head_ref;
 			}
-			strbuf_reset(&buf);
 			if (!read_oneliner(&buf, rebase_path_onto(), 0)) {
 				res = error(_("could not read 'onto'"));
 				goto cleanup_head_ref;
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#101 lore raw

PATCH v4 7/23 sequencer: configurably warn on non-existent files

In reply to Denton Liu

In the future, we plan on externing read_oneliner(). Future users of read_oneliner() will want the ability to output warnings in the event that the path doesn't exist. Introduce the READ_ONELINER_WARN_MISSING flag which, if active, would issue a warning when a file doesn't exist by skipping the !file_exists() check and letting strbuf_read_file() handle that case.

---
 sequencer.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
sequencer.c +3 -1
index 6c26d61670..b771e8f4ca 100644
@@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename,
 }
 
 #define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
+#define READ_ONELINER_WARN_MISSING (1 << 1)
 
 /*
  * Resets a strbuf then reads a file that was presumably written by a shell
@@ -435,7 +436,8 @@ static int read_oneliner(struct strbuf *buf,
 {
 	strbuf_reset(buf);
 	if (strbuf_read_file(buf, path, 0) < 0) {
-		if (errno != ENOENT && errno != ENOTDIR)
+		if ((flags & READ_ONELINER_WARN_MISSING) ||
+				(errno != ENOENT && errno != ENOTDIR))
 			warning_errno(_("could not read '%s'"), path);
 		return 0;
 	}
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#87 lore raw

PATCH v4 1/23 Makefile: ASCII-sort += lists

In reply to Denton Liu

There are many += lists in the Makefile and, over time, they have gotten slightly out of ASCII order. Sort all += lists to bring them back in order.

ASCII sorting was chosen over strict alphabetical order even though, if we omit file prefixes, the lists aren't sorted in strictly alphabetical order (e.g. archive.o comes after archive-zip.o instead of before archive-tar.o). This is intentional because the purpose of maintaining the sorted list is to ensure line insertions are deterministic. By using ASCII ordering, it is more easily mechanically reproducible in the future, such as by using :sort in Vim.

This patch is best viewed with --color-moved.

---
 Makefile | 77 ++++++++++++++++++++++++++++----------------------------
 1 file changed, 39 insertions(+), 38 deletions(-)
Makefile +39 -38
index 9804a0758b..f709fc3f77 100644
@@ -604,12 +604,12 @@ unexport CDPATH
 SCRIPT_SH += git-bisect.sh
 SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
+SCRIPT_SH += git-legacy-stash.sh
 SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-quiltimport.sh
-SCRIPT_SH += git-legacy-stash.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-web--browse.sh
@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh
 SCRIPT_LIB += git-mergetool--lib
 SCRIPT_LIB += git-parse-remote
 SCRIPT_LIB += git-rebase--preserve-merges
-SCRIPT_LIB += git-sh-setup
 SCRIPT_LIB += git-sh-i18n
+SCRIPT_LIB += git-sh-setup
 
 SCRIPT_PERL += git-add--interactive.perl
 SCRIPT_PERL += git-archimport.perl
@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o
 PROGRAM_OBJS += fast-import.o
 PROGRAM_OBJS += http-backend.o
 PROGRAM_OBJS += imap-send.o
+PROGRAM_OBJS += remote-testsvn.o
 PROGRAM_OBJS += sh-i18n--envsubst.o
 PROGRAM_OBJS += shell.o
-PROGRAM_OBJS += remote-testsvn.o
 
 # Binary suffix, set to .exe for Windows builds
 X =
@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
 TEST_BUILTINS_OBJS += test-example-decorate.o
 TEST_BUILTINS_OBJS += test-genrandom.o
 TEST_BUILTINS_OBJS += test-genzeros.o
+TEST_BUILTINS_OBJS += test-hash-speed.o
 TEST_BUILTINS_OBJS += test-hash.o
 TEST_BUILTINS_OBJS += test-hashmap.o
-TEST_BUILTINS_OBJS += test-hash-speed.o
 TEST_BUILTINS_OBJS += test-index-version.o
 TEST_BUILTINS_OBJS += test-json-writer.o
 TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@ -737,8 +737,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o
 TEST_BUILTINS_OBJS += test-run-command.o
 TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
 TEST_BUILTINS_OBJS += test-serve-v2.o
-TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha1-array.o
+TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha256.o
 TEST_BUILTINS_OBJS += test-sigchain.o
 TEST_BUILTINS_OBJS += test-strcmp-offset.o
@@ -748,10 +748,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
 TEST_BUILTINS_OBJS += test-subprocess.o
 TEST_BUILTINS_OBJS += test-trace2.o
 TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
-TEST_BUILTINS_OBJS += test-xml-encode.o
 TEST_BUILTINS_OBJS += test-wildmatch.o
 TEST_BUILTINS_OBJS += test-windows-named-pipe.o
 TEST_BUILTINS_OBJS += test-write-cache.o
+TEST_BUILTINS_OBJS += test-xml-encode.o
 
 # Do not add more tests here unless they have extra dependencies. Add
 # them in TEST_BUILTINS_OBJS above.
@@ -788,10 +788,10 @@ OTHER_PROGRAMS = git$X
 
 # what test wrappers are needed and 'install' will install, in bindir
 BINDIR_PROGRAMS_NEED_X += git
-BINDIR_PROGRAMS_NEED_X += git-upload-pack
 BINDIR_PROGRAMS_NEED_X += git-receive-pack
-BINDIR_PROGRAMS_NEED_X += git-upload-archive
 BINDIR_PROGRAMS_NEED_X += git-shell
+BINDIR_PROGRAMS_NEED_X += git-upload-archive
+BINDIR_PROGRAMS_NEED_X += git-upload-pack
 
 BINDIR_PROGRAMS_NO_X += git-cvsserver
 
@@ -831,9 +831,9 @@ LIB_OBJS += advice.o
 LIB_OBJS += alias.o
 LIB_OBJS += alloc.o
 LIB_OBJS += apply.o
-LIB_OBJS += archive.o
 LIB_OBJS += archive-tar.o
 LIB_OBJS += archive-zip.o
+LIB_OBJS += archive.o
 LIB_OBJS += argv-array.o
 LIB_OBJS += attr.o
 LIB_OBJS += base85.o
@@ -849,9 +849,9 @@ LIB_OBJS += checkout.o
 LIB_OBJS += color.o
 LIB_OBJS += column.o
 LIB_OBJS += combine-diff.o
-LIB_OBJS += commit.o
 LIB_OBJS += commit-graph.o
 LIB_OBJS += commit-reach.o
+LIB_OBJS += commit.o
 LIB_OBJS += compat/obstack.o
 LIB_OBJS += compat/terminal.o
 LIB_OBJS += config.o
@@ -865,17 +865,17 @@ LIB_OBJS += ctype.o
 LIB_OBJS += date.o
 LIB_OBJS += decorate.o
 LIB_OBJS += delta-islands.o
+LIB_OBJS += diff-delta.o
+LIB_OBJS += diff-lib.o
+LIB_OBJS += diff-no-index.o
+LIB_OBJS += diff.o
 LIB_OBJS += diffcore-break.o
 LIB_OBJS += diffcore-delta.o
 LIB_OBJS += diffcore-order.o
 LIB_OBJS += diffcore-pickaxe.o
 LIB_OBJS += diffcore-rename.o
-LIB_OBJS += diff-delta.o
-LIB_OBJS += diff-lib.o
-LIB_OBJS += diff-no-index.o
-LIB_OBJS += diff.o
-LIB_OBJS += dir.o
 LIB_OBJS += dir-iterator.o
+LIB_OBJS += dir.o
 LIB_OBJS += editor.o
 LIB_OBJS += entry.o
 LIB_OBJS += environment.o
@@ -893,7 +893,6 @@ LIB_OBJS += gpg-interface.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hashmap.o
-LIB_OBJS += linear-assignment.o
 LIB_OBJS += help.o
 LIB_OBJS += hex.o
 LIB_OBJS += ident.o
@@ -903,9 +902,10 @@ LIB_OBJS += kwset.o
 LIB_OBJS += levenshtein.o
 LIB_OBJS += line-log.o
 LIB_OBJS += line-range.o
-LIB_OBJS += list-objects.o
-LIB_OBJS += list-objects-filter.o
+LIB_OBJS += linear-assignment.o
 LIB_OBJS += list-objects-filter-options.o
+LIB_OBJS += list-objects-filter.o
+LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
@@ -914,31 +914,31 @@ LIB_OBJS += mailinfo.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += mem-pool.o
-LIB_OBJS += merge.o
 LIB_OBJS += merge-blobs.o
 LIB_OBJS += merge-recursive.o
+LIB_OBJS += merge.o
 LIB_OBJS += mergesort.o
 LIB_OBJS += midx.o
 LIB_OBJS += name-hash.o
 LIB_OBJS += negotiator/default.o
 LIB_OBJS += negotiator/skipping.o
-LIB_OBJS += notes.o
 LIB_OBJS += notes-cache.o
 LIB_OBJS += notes-merge.o
 LIB_OBJS += notes-utils.o
+LIB_OBJS += notes.o
 LIB_OBJS += object.o
 LIB_OBJS += oidmap.o
 LIB_OBJS += oidset.o
-LIB_OBJS += packfile.o
-LIB_OBJS += pack-bitmap.o
 LIB_OBJS += pack-bitmap-write.o
+LIB_OBJS += pack-bitmap.o
 LIB_OBJS += pack-check.o
 LIB_OBJS += pack-objects.o
 LIB_OBJS += pack-revindex.o
 LIB_OBJS += pack-write.o
+LIB_OBJS += packfile.o
 LIB_OBJS += pager.o
-LIB_OBJS += parse-options.o
 LIB_OBJS += parse-options-cb.o
+LIB_OBJS += parse-options.o
 LIB_OBJS += patch-delta.o
 LIB_OBJS += patch-ids.o
 LIB_OBJS += path.o
@@ -955,8 +955,9 @@ LIB_OBJS += quote.o
 LIB_OBJS += range-diff.o
 LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
-LIB_OBJS += rebase.o
 LIB_OBJS += rebase-interactive.o
+LIB_OBJS += rebase.o
+LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/files-backend.o
@@ -964,7 +965,6 @@ LIB_OBJS += refs/iterator.o
 LIB_OBJS += refs/packed-backend.o
 LIB_OBJS += refs/ref-cache.o
 LIB_OBJS += refspec.o
-LIB_OBJS += ref-filter.o
 LIB_OBJS += remote.o
 LIB_OBJS += replace-object.o
 LIB_OBJS += repo-settings.o
@@ -979,8 +979,8 @@ LIB_OBJS += serve.o
 LIB_OBJS += server-info.o
 LIB_OBJS += setup.o
 LIB_OBJS += sha1-array.o
-LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1-file.o
+LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1-name.o
 LIB_OBJS += shallow.o
 LIB_OBJS += sideband.o
@@ -990,9 +990,9 @@ LIB_OBJS += stable-qsort.o
 LIB_OBJS += strbuf.o
 LIB_OBJS += streaming.o
 LIB_OBJS += string-list.o
-LIB_OBJS += submodule.o
-LIB_OBJS += submodule-config.o
 LIB_OBJS += sub-process.o
+LIB_OBJS += submodule-config.o
+LIB_OBJS += submodule.o
 LIB_OBJS += symlinks.o
 LIB_OBJS += tag.o
 LIB_OBJS += tempfile.o
@@ -1011,11 +1011,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o
 LIB_OBJS += trace2/tr2_tgt_perf.o
 LIB_OBJS += trace2/tr2_tls.o
 LIB_OBJS += trailer.o
-LIB_OBJS += transport.o
 LIB_OBJS += transport-helper.o
+LIB_OBJS += transport.o
 LIB_OBJS += tree-diff.o
-LIB_OBJS += tree.o
 LIB_OBJS += tree-walk.o
+LIB_OBJS += tree.o
 LIB_OBJS += unpack-trees.o
 LIB_OBJS += upload-pack.o
 LIB_OBJS += url.o
@@ -1055,9 +1055,9 @@ BUILTIN_OBJS += builtin/checkout.o
 BUILTIN_OBJS += builtin/clean.o
 BUILTIN_OBJS += builtin/clone.o
 BUILTIN_OBJS += builtin/column.o
+BUILTIN_OBJS += builtin/commit-graph.o
 BUILTIN_OBJS += builtin/commit-tree.o
 BUILTIN_OBJS += builtin/commit.o
-BUILTIN_OBJS += builtin/commit-graph.o
 BUILTIN_OBJS += builtin/config.o
 BUILTIN_OBJS += builtin/count-objects.o
 BUILTIN_OBJS += builtin/credential.o
@@ -1088,13 +1088,13 @@ BUILTIN_OBJS += builtin/ls-remote.o
 BUILTIN_OBJS += builtin/ls-tree.o
 BUILTIN_OBJS += builtin/mailinfo.o
 BUILTIN_OBJS += builtin/mailsplit.o
-BUILTIN_OBJS += builtin/merge.o
 BUILTIN_OBJS += builtin/merge-base.o
 BUILTIN_OBJS += builtin/merge-file.o
 BUILTIN_OBJS += builtin/merge-index.o
 BUILTIN_OBJS += builtin/merge-ours.o
 BUILTIN_OBJS += builtin/merge-recursive.o
 BUILTIN_OBJS += builtin/merge-tree.o
+BUILTIN_OBJS += builtin/merge.o
 BUILTIN_OBJS += builtin/mktag.o
 BUILTIN_OBJS += builtin/mktree.o
 BUILTIN_OBJS += builtin/multi-pack-index.o
@@ -1114,9 +1114,9 @@ BUILTIN_OBJS += builtin/read-tree.o
 BUILTIN_OBJS += builtin/rebase.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
-BUILTIN_OBJS += builtin/remote.o
 BUILTIN_OBJS += builtin/remote-ext.o
 BUILTIN_OBJS += builtin/remote-fd.o
+BUILTIN_OBJS += builtin/remote.o
 BUILTIN_OBJS += builtin/repack.o
 BUILTIN_OBJS += builtin/replace.o
 BUILTIN_OBJS += builtin/rerere.o
@@ -2335,16 +2335,16 @@ reconfigure config.mak.autogen: config.status
 endif
 
 XDIFF_OBJS += xdiff/xdiffi.o
-XDIFF_OBJS += xdiff/xprepare.o
-XDIFF_OBJS += xdiff/xutils.o
 XDIFF_OBJS += xdiff/xemit.o
+XDIFF_OBJS += xdiff/xhistogram.o
 XDIFF_OBJS += xdiff/xmerge.o
 XDIFF_OBJS += xdiff/xpatience.o
-XDIFF_OBJS += xdiff/xhistogram.o
+XDIFF_OBJS += xdiff/xprepare.o
+XDIFF_OBJS += xdiff/xutils.o
 
+VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/line_buffer.o
 VCSSVN_OBJS += vcs-svn/sliding_window.o
-VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/svndiff.o
 VCSSVN_OBJS += vcs-svn/svndump.o
 
@@ -3152,9 +3152,10 @@ endif
 #
 ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS)
 ALL_COMMANDS += git
+ALL_COMMANDS += git-citool
+ALL_COMMANDS += git-gui
 ALL_COMMANDS += gitk
 ALL_COMMANDS += gitweb
-ALL_COMMANDS += git-gui git-citool
 
 .PHONY: check-docs
 check-docs::
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#104 lore raw

PATCH v4 9/23 rebase: use read_oneliner()

In reply to Denton Liu

Since in sequencer.c, read_one() basically duplicates the functionality of read_oneliner(), reduce code duplication by replacing read_one() with read_oneliner(). Also, delete strbuf_reset() calls prior to read_oneliner() as read_oneliner() already resets the strbuf.

This was mostly done with the following Coccinelle script

	@@
	expression a, b;
	@@
	- read_one(a, b)
	+ !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)

Long lines were broken up and strbuf_reset()s were deleted manually.

---
 builtin/rebase.c | 42 +++++++++++++++++-------------------------
 1 file changed, 17 insertions(+), 25 deletions(-)
builtin/rebase.c +17 -25
index bff53d5d16..91873fa0b5 100644
@@ -586,15 +586,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o
 	return path.buf;
 }
 
-/* Read one file, then strip line endings */
-static int read_one(const char *path, struct strbuf *buf)
-{
-	if (strbuf_read_file(buf, path, 0) < 0)
-		return error_errno(_("could not read '%s'"), path);
-	strbuf_trim_trailing_newline(buf);
-	return 0;
-}
-
 /* Initialize the rebase options from the state directory. */
 static int read_basic_state(struct rebase_options *opts)
 {
@@ -602,8 +593,10 @@ static int read_basic_state(struct rebase_options *opts)
 	struct strbuf buf = STRBUF_INIT;
 	struct object_id oid;
 
-	if (read_one(state_dir_path("head-name", opts), &head_name) ||
-	    read_one(state_dir_path("onto", opts), &buf))
+	if (!read_oneliner(&head_name, state_dir_path("head-name", opts),
+			   READ_ONELINER_WARN_MISSING) ||
+	    !read_oneliner(&buf, state_dir_path("onto", opts),
+			   READ_ONELINER_WARN_MISSING))
 		return -1;
 	opts->head_name = starts_with(head_name.buf, "refs/") ?
 		xstrdup(head_name.buf) : NULL;
@@ -617,11 +610,12 @@ static int read_basic_state(struct rebase_options *opts)
 	 * head. Fall back to reading from head to cover for the case that the
 	 * user upgraded git with an ongoing interactive rebase.
 	 */
-	strbuf_reset(&buf);
 	if (file_exists(state_dir_path("orig-head", opts))) {
-		if (read_one(state_dir_path("orig-head", opts), &buf))
+		if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
+				   READ_ONELINER_WARN_MISSING))
 			return -1;
-	} else if (read_one(state_dir_path("head", opts), &buf))
+	} else if (!read_oneliner(&buf, state_dir_path("head", opts),
+				  READ_ONELINER_WARN_MISSING))
 		return -1;
 	if (get_oid(buf.buf, &opts->orig_head))
 		return error(_("invalid orig-head: '%s'"), buf.buf);
@@ -640,9 +634,8 @@ static int read_basic_state(struct rebase_options *opts)
 	}
 
 	if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
-		strbuf_reset(&buf);
-		if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
-			    &buf))
+		if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
+				   READ_ONELINER_WARN_MISSING))
 			return -1;
 		if (!strcmp(buf.buf, "--rerere-autoupdate"))
 			opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
@@ -654,25 +647,24 @@ static int read_basic_state(struct rebase_options *opts)
 	}
 
 	if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
-		strbuf_reset(&buf);
-		if (read_one(state_dir_path("gpg_sign_opt", opts),
-			    &buf))
+		if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
+				   READ_ONELINER_WARN_MISSING))
 			return -1;
 		free(opts->gpg_sign_opt);
 		opts->gpg_sign_opt = xstrdup(buf.buf);
 	}
 
 	if (file_exists(state_dir_path("strategy", opts))) {
-		strbuf_reset(&buf);
-		if (read_one(state_dir_path("strategy", opts), &buf))
+		if (!read_oneliner(&buf, state_dir_path("strategy", opts),
+				   READ_ONELINER_WARN_MISSING))
 			return -1;
 		free(opts->strategy);
 		opts->strategy = xstrdup(buf.buf);
 	}
 
 	if (file_exists(state_dir_path("strategy_opts", opts))) {
-		strbuf_reset(&buf);
-		if (read_one(state_dir_path("strategy_opts", opts), &buf))
+		if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
+				   READ_ONELINER_WARN_MISSING))
 			return -1;
 		free(opts->strategy_opts);
 		opts->strategy_opts = xstrdup(buf.buf);
@@ -724,7 +716,7 @@ static int apply_autostash(struct rebase_options *opts)
 	if (!file_exists(path))
 		return 0;
 
-	if (read_one(path, &autostash))
+	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))
 		return error(_("Could not read '%s'"), path);
 	/* Ensure that the hash is not mistaken for a number */
 	strbuf_addstr(&autostash, "^0");
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#103 lore raw

PATCH v4 8/23 sequencer: make read_oneliner() extern

In reply to Denton Liu

The function read_oneliner() is a generally useful util function. Instead of hiding it as a static function within sequencer.c, extern it so that it can be reused by others.

This patch is best viewed with --color-moved.

---
 sequencer.c | 14 +-------------
 sequencer.h | 14 ++++++++++++++
 2 files changed, 15 insertions(+), 13 deletions(-)
sequencer.c +1 -13
index b771e8f4ca..d1d59867ae 100644
@@ -419,19 +419,7 @@ static int write_message(const void *buf, size_t len, const char *filename,
 	return 0;
 }
 
-#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
-#define READ_ONELINER_WARN_MISSING (1 << 1)
-
-/*
- * Resets a strbuf then reads a file that was presumably written by a shell
- * script, i.e. with an end-of-line marker that needs to be stripped.
- *
- * Note that only the last end-of-line marker is stripped, consistent with the
- * behavior of "$(cat path)" in a shell script.
- *
- * Returns 1 if the file was read, 0 if it could not be read or does not exist.
- */
-static int read_oneliner(struct strbuf *buf,
+int read_oneliner(struct strbuf *buf,
 	const char *path, unsigned flags)
 {
 	strbuf_reset(buf);
sequencer.h +14 -0
index 718a07426d..1f6eda70b6 100644
@@ -198,6 +198,20 @@ void print_commit_summary(struct repository *repo,
 			  const struct object_id *oid,
 			  unsigned int flags);
 
+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
+#define READ_ONELINER_WARN_MISSING (1 << 1)
+
+/*
+ * Resets a strbuf then reads a file that was presumably written by a shell
+ * script, i.e. with an end-of-line marker that needs to be stripped.
+ *
+ * Note that only the last end-of-line marker is stripped, consistent with the
+ * behavior of "$(cat path)" in a shell script.
+ *
+ * Returns 1 if the file was read, 0 if it could not be read or does not exist.
+ */
+int read_oneliner(struct strbuf *buf,
+	const char *path, unsigned flags);
 int read_author_script(const char *path, char **name, char **email, char **date,
 		       int allow_missing);
 void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#108 lore raw

PATCH v4 12/23 rebase: use apply_autostash() from sequencer.c

In reply to Denton Liu

The apply_autostash() function in builtin/rebase.c is similar enough to the apply_autostash() function in sequencer.c that they are almost interchangeable, except for the type of arg they accept. Make the sequencer.c version extern and use it in rebase.

The rebase version was introduced in 6defce2b02 (builtin rebase: support --autostash option, 2018-09-04) as part of the shell to C conversion. It opted to duplicate the function because, at the time, there was another in-progress project converting interactive rebase from shell to C as well and they did not want to clash with them by refactoring sequencer.c version of apply_autostash(). Since both efforts are long done, we can freely combine them together now.

---
 builtin/rebase.c | 49 ++----------------------------------------------
 sequencer.c      |  2 +-
 sequencer.h      |  2 ++
 3 files changed, 5 insertions(+), 48 deletions(-)
builtin/rebase.c +2 -47
index 91873fa0b5..29745176cb 100644
@@ -707,51 +707,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)
 	return 0;
 }
 
-static int apply_autostash(struct rebase_options *opts)
-{
-	const char *path = state_dir_path("autostash", opts);
-	struct strbuf autostash = STRBUF_INIT;
-	struct child_process stash_apply = CHILD_PROCESS_INIT;
-
-	if (!file_exists(path))
-		return 0;
-
-	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))
-		return error(_("Could not read '%s'"), path);
-	/* Ensure that the hash is not mistaken for a number */
-	strbuf_addstr(&autostash, "^0");
-	argv_array_pushl(&stash_apply.args,
-			 "stash", "apply", autostash.buf, NULL);
-	stash_apply.git_cmd = 1;
-	stash_apply.no_stderr = stash_apply.no_stdout =
-		stash_apply.no_stdin = 1;
-	if (!run_command(&stash_apply))
-		printf(_("Applied autostash.\n"));
-	else {
-		struct argv_array args = ARGV_ARRAY_INIT;
-		int res = 0;
-
-		argv_array_pushl(&args,
-				 "stash", "store", "-m", "autostash", "-q",
-				 autostash.buf, NULL);
-		if (run_command_v_opt(args.argv, RUN_GIT_CMD))
-			res = error(_("Cannot store %s"), autostash.buf);
-		argv_array_clear(&args);
-		strbuf_release(&autostash);
-		if (res)
-			return res;
-
-		fprintf(stderr,
-			_("Applying autostash resulted in conflicts.\n"
-			  "Your changes are safe in the stash.\n"
-			  "You can run \"git stash pop\" or \"git stash drop\" "
-			  "at any time.\n"));
-	}
-
-	strbuf_release(&autostash);
-	return 0;
-}
-
 static int finish_rebase(struct rebase_options *opts)
 {
 	struct strbuf dir = STRBUF_INIT;
@@ -759,7 +714,7 @@ static int finish_rebase(struct rebase_options *opts)
 	int ret = 0;
 
 	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
-	apply_autostash(opts);
+	apply_autostash(state_dir_path("autostash", opts));
 	close_object_store(the_repository->objects);
 	/*
 	 * We ignore errors in 'gc --auto', since the
@@ -1204,7 +1159,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
 	} else if (status == 2) {
 		struct strbuf dir = STRBUF_INIT;
 
-		apply_autostash(opts);
+		apply_autostash(state_dir_path("autostash", opts));
 		strbuf_addstr(&dir, opts->state_dir);
 		remove_dir_recursively(&dir, 0);
 		strbuf_release(&dir);
sequencer.c +1 -1
index 4b7985942d..dde1f6b30f 100644
@@ -3649,7 +3649,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
 	return -1;
 }
 
-static int apply_autostash(const char *path)
+int apply_autostash(const char *path)
 {
 	struct strbuf stash_oid = STRBUF_INIT;
 	struct child_process child = CHILD_PROCESS_INIT;
sequencer.h +2 -0
index 1f6eda70b6..d6ba62a089 100644
@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,
 			 const struct commit *current_head,
 			 const struct object_id *new_head);
 
+int apply_autostash(const char *path);
+
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
 void print_commit_summary(struct repository *repo,
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#107 lore raw

PATCH v4 11/23 sequencer: rename stash_sha1 to stash_oid

In reply to Denton Liu

The preferred terminology is to refer to object identifiers as "OIDs". Rename the stash_sha1 variable to stash_oid in order to conform to this.

---
 sequencer.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)
sequencer.c +8 -8
index ae9efe5825..4b7985942d 100644
@@ -3651,23 +3651,23 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
 
 static int apply_autostash(const char *path)
 {
-	struct strbuf stash_sha1 = STRBUF_INIT;
+	struct strbuf stash_oid = STRBUF_INIT;
 	struct child_process child = CHILD_PROCESS_INIT;
 	int ret = 0;
 
-	if (!read_oneliner(&stash_sha1, path,
+	if (!read_oneliner(&stash_oid, path,
 			   READ_ONELINER_SKIP_IF_EMPTY)) {
-		strbuf_release(&stash_sha1);
+		strbuf_release(&stash_oid);
 		return 0;
 	}
-	strbuf_trim(&stash_sha1);
+	strbuf_trim(&stash_oid);
 
 	child.git_cmd = 1;
 	child.no_stdout = 1;
 	child.no_stderr = 1;
 	argv_array_push(&child.args, "stash");
 	argv_array_push(&child.args, "apply");
-	argv_array_push(&child.args, stash_sha1.buf);
+	argv_array_push(&child.args, stash_oid.buf);
 	if (!run_command(&child))
 		fprintf(stderr, _("Applied autostash.\n"));
 	else {
@@ -3679,9 +3679,9 @@ static int apply_autostash(const char *path)
 		argv_array_push(&store.args, "-m");
 		argv_array_push(&store.args, "autostash");
 		argv_array_push(&store.args, "-q");
-		argv_array_push(&store.args, stash_sha1.buf);
+		argv_array_push(&store.args, stash_oid.buf);
 		if (run_command(&store))
-			ret = error(_("cannot store %s"), stash_sha1.buf);
+			ret = error(_("cannot store %s"), stash_oid.buf);
 		else
 			fprintf(stderr,
 				_("Applying autostash resulted in conflicts.\n"
@@ -3690,7 +3690,7 @@ static int apply_autostash(const char *path)
 				  " \"git stash drop\" at any time.\n"));
 	}
 
-	strbuf_release(&stash_sha1);
+	strbuf_release(&stash_oid);
 	return ret;
 }
 
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#109 lore raw

PATCH v4 13/23 rebase: generify reset_head()

In reply to Denton Liu

In the future, we plan on lib-ifying reset_head() so we need it to be more generic. Make it more generic by making it accept a struct repository argument instead of implicitly using the non-repo functions. Also, make it accept a const char *default_reflog_action argument so that the default action of "rebase" isn't hardcoded in.

---
 builtin/rebase.c | 64 ++++++++++++++++++++++++++++--------------------
 1 file changed, 37 insertions(+), 27 deletions(-)
builtin/rebase.c +37 -27
index 29745176cb..0f650a798e 100644
@@ -28,6 +28,8 @@
 #include "sequencer.h"
 #include "rebase-interactive.h"
 
+#define DEFAULT_REFLOG_ACTION "rebase"
+
 static char const * const builtin_rebase_usage[] = {
 	N_("git rebase [-i] [options] [--exec <cmd>] "
 		"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
@@ -767,9 +769,10 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)
 #define RESET_HEAD_REFS_ONLY (1<<3)
 #define RESET_ORIG_HEAD (1<<4)
 
-static int reset_head(struct object_id *oid, const char *action,
+static int reset_head(struct repository *r, struct object_id *oid, const char *action,
 		      const char *switch_to_branch, unsigned flags,
-		      const char *reflog_orig_head, const char *reflog_head)
+		      const char *reflog_orig_head, const char *reflog_head,
+		      const char *default_reflog_action)
 {
 	unsigned detach_head = flags & RESET_HEAD_DETACH;
 	unsigned reset_hard = flags & RESET_HEAD_HARD;
@@ -791,7 +794,7 @@ static int reset_head(struct object_id *oid, const char *action,
 	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
 		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
 
-	if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
+	if (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {
 		ret = -1;
 		goto leave_reset_head;
 	}
@@ -810,26 +813,26 @@ static int reset_head(struct object_id *oid, const char *action,
 	memset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));
 	setup_unpack_trees_porcelain(&unpack_tree_opts, action);
 	unpack_tree_opts.head_idx = 1;
-	unpack_tree_opts.src_index = the_repository->index;
-	unpack_tree_opts.dst_index = the_repository->index;
+	unpack_tree_opts.src_index = r->index;
+	unpack_tree_opts.dst_index = r->index;
 	unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;
 	unpack_tree_opts.update = 1;
 	unpack_tree_opts.merge = 1;
 	if (!detach_head)
 		unpack_tree_opts.reset = 1;
 
-	if (repo_read_index_unmerged(the_repository) < 0) {
+	if (repo_read_index_unmerged(r) < 0) {
 		ret = error(_("could not read index"));
 		goto leave_reset_head;
 	}
 
-	if (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {
+	if (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {
 		ret = error(_("failed to find tree of %s"),
 			    oid_to_hex(&head_oid));
 		goto leave_reset_head;
 	}
 
-	if (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {
+	if (!fill_tree_descriptor(r, &desc[nr++], oid)) {
 		ret = error(_("failed to find tree of %s"), oid_to_hex(oid));
 		goto leave_reset_head;
 	}
@@ -840,16 +843,16 @@ static int reset_head(struct object_id *oid, const char *action,
 	}
 
 	tree = parse_tree_indirect(oid);
-	prime_cache_tree(the_repository, the_repository->index, tree);
+	prime_cache_tree(r, r->index, tree);
 
-	if (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {
+	if (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {
 		ret = error(_("could not write index"));
 		goto leave_reset_head;
 	}
 
 reset_head_refs:
 	reflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
-	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : "rebase");
+	strbuf_addf(&msg, "%s: ", reflog_action ? reflog_action : default_reflog_action);
 	prefix_len = msg.len;
 
 	if (update_orig_head) {
@@ -911,8 +914,10 @@ static int move_to_original_branch(struct rebase_options *opts)
 		    opts->head_name, oid_to_hex(&opts->onto->object.oid));
 	strbuf_addf(&head_reflog, "rebase finished: returning to %s",
 		    opts->head_name);
-	ret = reset_head(NULL, "", opts->head_name, RESET_HEAD_REFS_ONLY,
-			 orig_head_reflog.buf, head_reflog.buf);
+	ret = reset_head(the_repository, NULL, "", opts->head_name,
+			 RESET_HEAD_REFS_ONLY,
+			 orig_head_reflog.buf, head_reflog.buf,
+			 DEFAULT_REFLOG_ACTION);
 
 	strbuf_release(&orig_head_reflog);
 	strbuf_release(&head_reflog);
@@ -1000,8 +1005,9 @@ static int run_am(struct rebase_options *opts)
 		free(rebased_patches);
 		argv_array_clear(&am.args);
 
-		reset_head(&opts->orig_head, "checkout", opts->head_name, 0,
-			   "HEAD", NULL);
+		reset_head(the_repository, &opts->orig_head, "checkout",
+			   opts->head_name, 0,
+			   "HEAD", NULL, DEFAULT_REFLOG_ACTION);
 		error(_("\ngit encountered an error while preparing the "
 			"patches to replay\n"
 			"these revisions:\n"
@@ -1656,8 +1662,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		rerere_clear(the_repository, &merge_rr);
 		string_list_clear(&merge_rr, 1);
 
-		if (reset_head(NULL, "reset", NULL, RESET_HEAD_HARD,
-			       NULL, NULL) < 0)
+		if (reset_head(the_repository, NULL, "reset", NULL, RESET_HEAD_HARD,
+			       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)
 			die(_("could not discard worktree changes"));
 		remove_branch_state(the_repository, 0);
 		if (read_basic_state(&options))
@@ -1674,9 +1680,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 		if (read_basic_state(&options))
 			exit(1);
-		if (reset_head(&options.orig_head, "reset",
+		if (reset_head(the_repository, &options.orig_head, "reset",
 			       options.head_name, RESET_HEAD_HARD,
-			       NULL, NULL) < 0)
+			       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)
 			die(_("could not move back to %s"),
 			    oid_to_hex(&options.orig_head));
 		remove_branch_state(the_repository, 0);
@@ -2068,8 +2074,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				    options.state_dir);
 			write_file(autostash, "%s", oid_to_hex(&oid));
 			printf(_("Created autostash: %s\n"), buf.buf);
-			if (reset_head(NULL, "reset --hard",
-				       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
+			if (reset_head(the_repository, NULL, "reset --hard",
+				       NULL, RESET_HEAD_HARD, NULL, NULL,
+				       DEFAULT_REFLOG_ACTION) < 0)
 				die(_("could not reset --hard"));
 
 			if (discard_index(the_repository->index) < 0 ||
@@ -2109,10 +2116,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 				strbuf_addf(&buf, "%s: checkout %s",
 					    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
 					    options.switch_to);
-				if (reset_head(&options.orig_head, "checkout",
+				if (reset_head(the_repository,
+					       &options.orig_head, "checkout",
 					       options.head_name,
 					       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
-					       NULL, buf.buf) < 0) {
+					       NULL, buf.buf,
+					       DEFAULT_REFLOG_ACTION) < 0) {
 					ret = !!error(_("could not switch to "
 							"%s"),
 						      options.switch_to);
@@ -2184,10 +2193,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
 	strbuf_addf(&msg, "%s: checkout %s",
 		    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);
-	if (reset_head(&options.onto->object.oid, "checkout", NULL,
+	if (reset_head(the_repository, &options.onto->object.oid, "checkout", NULL,
 		       RESET_HEAD_DETACH | RESET_ORIG_HEAD |
 		       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
-		       NULL, msg.buf))
+		       NULL, msg.buf, DEFAULT_REFLOG_ACTION))
 		die(_("Could not detach HEAD"));
 	strbuf_release(&msg);
 
@@ -2202,8 +2211,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		strbuf_addf(&msg, "rebase finished: %s onto %s",
 			options.head_name ? options.head_name : "detached HEAD",
 			oid_to_hex(&options.onto->object.oid));
-		reset_head(NULL, "Fast-forwarded", options.head_name,
-			   RESET_HEAD_REFS_ONLY, "HEAD", msg.buf);
+		reset_head(the_repository, NULL, "Fast-forwarded", options.head_name,
+			   RESET_HEAD_REFS_ONLY, "HEAD", msg.buf,
+			   DEFAULT_REFLOG_ACTION);
 		strbuf_release(&msg);
 		ret = !!finish_rebase(&options);
 		goto cleanup;
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#111 lore raw

PATCH v4 15/23 rebase: extract create_autostash()

In reply to Denton Liu

In a future commit, we will lib-ify this code. In preparation for this, extract the code into the create_autostash() function so that it can be cleaned up before it is finally lib-ified.

This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.

---
 builtin/rebase.c | 94 +++++++++++++++++++++++++-----------------------
 1 file changed, 50 insertions(+), 44 deletions(-)
builtin/rebase.c +50 -44
index 55730f6360..8349f3a03a 100644
@@ -1269,6 +1269,55 @@ static int check_exec_cmd(const char *cmd)
 	return 0;
 }
 
+static void create_autostash(struct rebase_options *options)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct lock_file lock_file = LOCK_INIT;
+	int fd;
+
+	fd = hold_locked_index(&lock_file, 0);
+	refresh_cache(REFRESH_QUIET);
+	if (0 <= fd)
+		repo_update_index_if_able(the_repository, &lock_file);
+	rollback_lock_file(&lock_file);
+
+	if (has_unstaged_changes(the_repository, 1) ||
+	    has_uncommitted_changes(the_repository, 1)) {
+		const char *autostash =
+			state_dir_path("autostash", options);
+		struct child_process stash = CHILD_PROCESS_INIT;
+		struct object_id oid;
+
+		argv_array_pushl(&stash.args,
+				 "stash", "create", "autostash", NULL);
+		stash.git_cmd = 1;
+		stash.no_stdin = 1;
+		strbuf_reset(&buf);
+		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
+			die(_("Cannot autostash"));
+		strbuf_trim_trailing_newline(&buf);
+		if (get_oid(buf.buf, &oid))
+			die(_("Unexpected stash response: '%s'"),
+			    buf.buf);
+		strbuf_reset(&buf);
+		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
+
+		if (safe_create_leading_directories_const(autostash))
+			die(_("Could not create directory for '%s'"),
+			    options->state_dir);
+		write_file(autostash, "%s", oid_to_hex(&oid));
+		printf(_("Created autostash: %s\n"), buf.buf);
+		if (reset_head(the_repository, NULL, "reset --hard",
+			       NULL, RESET_HEAD_HARD, NULL, NULL,
+			       DEFAULT_REFLOG_ACTION) < 0)
+			die(_("could not reset --hard"));
+
+		if (discard_index(the_repository->index) < 0 ||
+			repo_read_index(the_repository) < 0)
+			die(_("could not read index"));
+	}
+	strbuf_release(&buf);
+}
 
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
@@ -1902,50 +1951,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		die(_("could not read index"));
 
 	if (options.autostash) {
-		struct lock_file lock_file = LOCK_INIT;
-		int fd;
-
-		fd = hold_locked_index(&lock_file, 0);
-		refresh_cache(REFRESH_QUIET);
-		if (0 <= fd)
-			repo_update_index_if_able(the_repository, &lock_file);
-		rollback_lock_file(&lock_file);
-
-		if (has_unstaged_changes(the_repository, 1) ||
-		    has_uncommitted_changes(the_repository, 1)) {
-			const char *autostash =
-				state_dir_path("autostash", &options);
-			struct child_process stash = CHILD_PROCESS_INIT;
-			struct object_id oid;
-
-			argv_array_pushl(&stash.args,
-					 "stash", "create", "autostash", NULL);
-			stash.git_cmd = 1;
-			stash.no_stdin = 1;
-			strbuf_reset(&buf);
-			if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
-				die(_("Cannot autostash"));
-			strbuf_trim_trailing_newline(&buf);
-			if (get_oid(buf.buf, &oid))
-				die(_("Unexpected stash response: '%s'"),
-				    buf.buf);
-			strbuf_reset(&buf);
-			strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
-
-			if (safe_create_leading_directories_const(autostash))
-				die(_("Could not create directory for '%s'"),
-				    options.state_dir);
-			write_file(autostash, "%s", oid_to_hex(&oid));
-			printf(_("Created autostash: %s\n"), buf.buf);
-			if (reset_head(the_repository, NULL, "reset --hard",
-				       NULL, RESET_HEAD_HARD, NULL, NULL,
-				       DEFAULT_REFLOG_ACTION) < 0)
-				die(_("could not reset --hard"));
-
-			if (discard_index(the_repository->index) < 0 ||
-				repo_read_index(the_repository) < 0)
-				die(_("could not read index"));
-		}
+		create_autostash(&options);
 	}
 
 	if (require_clean_work_tree(the_repository, "rebase",
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#112 lore raw

PATCH v4 16/23 rebase: generify create_autostash()

In reply to Denton Liu

In the future, we plan on lib-ifying create_autostash() so we need it to be more generic. Make it more generic by making it accept a struct repository argument instead of implicitly using the non-repo functions and the_repository. Also, make it accept a path argument so that we no longer rely have to rely on struct rebase_options. Finally, make it accept a default_reflog_action argument so we no longer have to rely on DEFAULT_REFLOG_ACTION.

---
 builtin/rebase.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)
builtin/rebase.c +16 -16
index 8349f3a03a..e9b05a6cd8 100644
@@ -1269,22 +1269,21 @@ static int check_exec_cmd(const char *cmd)
 	return 0;
 }
 
-static void create_autostash(struct rebase_options *options)
+static void create_autostash(struct repository *r, const char *path,
+			     const char *default_reflog_action)
 {
 	struct strbuf buf = STRBUF_INIT;
 	struct lock_file lock_file = LOCK_INIT;
 	int fd;
 
-	fd = hold_locked_index(&lock_file, 0);
-	refresh_cache(REFRESH_QUIET);
+	fd = repo_hold_locked_index(r, &lock_file, 0);
+	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
 	if (0 <= fd)
-		repo_update_index_if_able(the_repository, &lock_file);
+		repo_update_index_if_able(r, &lock_file);
 	rollback_lock_file(&lock_file);
 
-	if (has_unstaged_changes(the_repository, 1) ||
-	    has_uncommitted_changes(the_repository, 1)) {
-		const char *autostash =
-			state_dir_path("autostash", options);
+	if (has_unstaged_changes(r, 1) ||
+	    has_uncommitted_changes(r, 1)) {
 		struct child_process stash = CHILD_PROCESS_INIT;
 		struct object_id oid;
 
@@ -1302,18 +1301,18 @@ static void create_autostash(struct rebase_options *options)
 		strbuf_reset(&buf);
 		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
 
-		if (safe_create_leading_directories_const(autostash))
+		if (safe_create_leading_directories_const(path))
 			die(_("Could not create directory for '%s'"),
-			    options->state_dir);
-		write_file(autostash, "%s", oid_to_hex(&oid));
+			    path);
+		write_file(path, "%s", oid_to_hex(&oid));
 		printf(_("Created autostash: %s\n"), buf.buf);
-		if (reset_head(the_repository, NULL, "reset --hard",
+		if (reset_head(r, NULL, "reset --hard",
 			       NULL, RESET_HEAD_HARD, NULL, NULL,
-			       DEFAULT_REFLOG_ACTION) < 0)
+			       default_reflog_action) < 0)
 			die(_("could not reset --hard"));
 
-		if (discard_index(the_repository->index) < 0 ||
-			repo_read_index(the_repository) < 0)
+		if (discard_index(r->index) < 0 ||
+			repo_read_index(r) < 0)
 			die(_("could not read index"));
 	}
 	strbuf_release(&buf);
@@ -1951,7 +1950,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		die(_("could not read index"));
 
 	if (options.autostash) {
-		create_autostash(&options);
+		create_autostash(the_repository, state_dir_path("autostash", &options),
+				 DEFAULT_REFLOG_ACTION);
 	}
 
 	if (require_clean_work_tree(the_repository, "rebase",
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#113 lore raw

PATCH v4 17/23 sequencer: extract perform_autostash() from rebase

In reply to Denton Liu

Lib-ify the autostash code by extracting perform_autostash() from rebase into sequencer. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved.

---
 builtin/rebase.c | 49 -----------------------------------------------
 sequencer.c      | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |  2 ++
 3 files changed, 52 insertions(+), 49 deletions(-)
builtin/rebase.c +0 -49
index e9b05a6cd8..b800c8bfca 100644
@@ -1269,55 +1269,6 @@ static int check_exec_cmd(const char *cmd)
 	return 0;
 }
 
-static void create_autostash(struct repository *r, const char *path,
-			     const char *default_reflog_action)
-{
-	struct strbuf buf = STRBUF_INIT;
-	struct lock_file lock_file = LOCK_INIT;
-	int fd;
-
-	fd = repo_hold_locked_index(r, &lock_file, 0);
-	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
-	if (0 <= fd)
-		repo_update_index_if_able(r, &lock_file);
-	rollback_lock_file(&lock_file);
-
-	if (has_unstaged_changes(r, 1) ||
-	    has_uncommitted_changes(r, 1)) {
-		struct child_process stash = CHILD_PROCESS_INIT;
-		struct object_id oid;
-
-		argv_array_pushl(&stash.args,
-				 "stash", "create", "autostash", NULL);
-		stash.git_cmd = 1;
-		stash.no_stdin = 1;
-		strbuf_reset(&buf);
-		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
-			die(_("Cannot autostash"));
-		strbuf_trim_trailing_newline(&buf);
-		if (get_oid(buf.buf, &oid))
-			die(_("Unexpected stash response: '%s'"),
-			    buf.buf);
-		strbuf_reset(&buf);
-		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
-
-		if (safe_create_leading_directories_const(path))
-			die(_("Could not create directory for '%s'"),
-			    path);
-		write_file(path, "%s", oid_to_hex(&oid));
-		printf(_("Created autostash: %s\n"), buf.buf);
-		if (reset_head(r, NULL, "reset --hard",
-			       NULL, RESET_HEAD_HARD, NULL, NULL,
-			       default_reflog_action) < 0)
-			die(_("could not reset --hard"));
-
-		if (discard_index(r->index) < 0 ||
-			repo_read_index(r) < 0)
-			die(_("could not read index"));
-	}
-	strbuf_release(&buf);
-}
-
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
 	struct rebase_options options = REBASE_OPTIONS_INIT;
sequencer.c +50 -0

62 lines not on this page · raw message

sequencer.h +2 -0
index d6ba62a089..cf1284f9ed 100644
@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,
 			 const struct commit *current_head,
 			 const struct object_id *new_head);
 
+void create_autostash(struct repository *r, const char *path,
+		      const char *default_reflog_action);
 int apply_autostash(const char *path);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#114 lore raw

PATCH v4 18/23 sequencer: unlink autostash in apply_autostash()

In reply to Denton Liu

Explicitly remove autostash file in apply_autostash() once it has been applied successfully.

This is currently a no-op because the only users of this function will unlink the state (including the autostash file) after this function runs. However, in the future, we will introduce a user of the function that does not explicitly remove the state so we do it here.

---
 sequencer.c | 1 +
 1 file changed, 1 insertion(+)
sequencer.c +1 -0
index 8efe0976fd..5dd1c2438e 100644
@@ -3740,6 +3740,7 @@ int apply_autostash(const char *path)
 				  " \"git stash drop\" at any time.\n"));
 	}
 
+	unlink(path);
 	strbuf_release(&stash_oid);
 	return ret;
 }
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#117 lore raw

PATCH v4 20/23 sequencer: implement apply_autostash_oid()

In reply to Denton Liu

Split apply_save_autostash() into apply_autostash_oid() and apply_save_autostash() where the former operates on an OID string and the latter reads the OID from a file before passing it into apply_save_autostash_oid().

This function is required for a future commmit which will rely on being able to apply an autostash whose OID is stored as a string.

---
 sequencer.c | 38 ++++++++++++++++++++++++++------------
 sequencer.h |  1 +
 2 files changed, 27 insertions(+), 12 deletions(-)
sequencer.c +26 -12
index fb52583bc2..b4cab9632a 100644
@@ -3699,26 +3699,18 @@ void create_autostash(struct repository *r, const char *path,
 	strbuf_release(&buf);
 }
 
-static int apply_save_autostash(const char *path, int attempt_apply)
+static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
 {
-	struct strbuf stash_oid = STRBUF_INIT;
 	struct child_process child = CHILD_PROCESS_INIT;
 	int ret = 0;
 
-	if (!read_oneliner(&stash_oid, path,
-			   READ_ONELINER_SKIP_IF_EMPTY)) {
-		strbuf_release(&stash_oid);
-		return 0;
-	}
-	strbuf_trim(&stash_oid);
-
 	if (attempt_apply) {
 		child.git_cmd = 1;
 		child.no_stdout = 1;
 		child.no_stderr = 1;
 		argv_array_push(&child.args, "stash");
 		argv_array_push(&child.args, "apply");
-		argv_array_push(&child.args, stash_oid.buf);
+		argv_array_push(&child.args, stash_oid);
 		ret = run_command(&child);
 	}
 
@@ -3733,9 +3725,9 @@ static int apply_save_autostash(const char *path, int attempt_apply)
 		argv_array_push(&store.args, "-m");
 		argv_array_push(&store.args, "autostash");
 		argv_array_push(&store.args, "-q");
-		argv_array_push(&store.args, stash_oid.buf);
+		argv_array_push(&store.args, stash_oid);
 		if (run_command(&store))
-			ret = error(_("cannot store %s"), stash_oid.buf);
+			ret = error(_("cannot store %s"), stash_oid);
 		else
 			fprintf(stderr,
 				_("%s\n"
@@ -3747,6 +3739,23 @@ static int apply_save_autostash(const char *path, int attempt_apply)
 				_("Autostash exists; creating a new stash entry."));
 	}
 
+	return ret;
+}
+
+static int apply_save_autostash(const char *path, int attempt_apply)
+{
+	struct strbuf stash_oid = STRBUF_INIT;
+	int ret = 0;
+
+	if (!read_oneliner(&stash_oid, path,
+			   READ_ONELINER_SKIP_IF_EMPTY)) {
+		strbuf_release(&stash_oid);
+		return 0;
+	}
+	strbuf_trim(&stash_oid);
+
+	ret = apply_save_autostash_oid(stash_oid.buf, attempt_apply);
+
 	unlink(path);
 	strbuf_release(&stash_oid);
 	return ret;
@@ -3762,6 +3771,11 @@ int apply_autostash(const char *path)
 	return apply_save_autostash(path, 1);
 }
 
+int apply_autostash_oid(const char *stash_oid)
+{
+	return apply_save_autostash_oid(stash_oid, 1);
+}
+
 static const char *reflog_message(struct replay_opts *opts,
 	const char *sub_action, const char *fmt, ...)
 {
sequencer.h +1 -0
index 2d09c1ac0b..a90b768b74 100644
@@ -195,6 +195,7 @@ void create_autostash(struct repository *r, const char *path,
 		      const char *default_reflog_action);
 int save_autostash(const char *path);
 int apply_autostash(const char *path);
+int apply_autostash_oid(const char *stash_oid);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#110 lore raw

PATCH v4 14/23 reset: extract reset_head() from rebase

In reply to Denton Liu

Continue the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.

This patch is best viewed with --color-moved.

---
 Makefile         |   1 +
 builtin/rebase.c | 139 +---------------------------------------------
 reset.c          | 140 +++++++++++++++++++++++++++++++++++++++++++++++
 reset.h          |  20 +++++++
 4 files changed, 162 insertions(+), 138 deletions(-)
 create mode 100644 reset.c
 create mode 100644 reset.h
Makefile +1 -0
index f709fc3f77..338fb55b73 100644
@@ -970,6 +970,7 @@ LIB_OBJS += replace-object.o
 LIB_OBJS += repo-settings.o
 LIB_OBJS += repository.o
 LIB_OBJS += rerere.o
+LIB_OBJS += reset.o
 LIB_OBJS += resolve-undo.o
 LIB_OBJS += revision.o
 LIB_OBJS += run-command.o
builtin/rebase.c +1 -138

151 lines not on this page · raw message

reset.c new file +140 -0

140 lines not on this page · raw message

reset.h new file +20 -0
new file mode 100644
index 0000000000..12f83c78e2
@@ -0,0 +1,20 @@
+#ifndef RESET_H
+#define RESET_H
+
+#include "hash.h"
+#include "repository.h"
+
+#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION"
+
+#define RESET_HEAD_DETACH (1<<0)
+#define RESET_HEAD_HARD (1<<1)
+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)
+#define RESET_HEAD_REFS_ONLY (1<<3)
+#define RESET_ORIG_HEAD (1<<4)
+
+int reset_head(struct repository *r, struct object_id *oid, const char *action,
+	       const char *switch_to_branch, unsigned flags,
+	       const char *reflog_orig_head, const char *reflog_head,
+	       const char *default_reflog_action);
+
+#endif
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#122 lore raw

PATCH v4 22/23 t5520: make test_pull_autostash() accept expect_parent_num

In reply to Denton Liu

Before, test_pull_autostash() was hardcoded to run test_cmp_rev HEAD^ copy to test that a rebase happened. However, in a future patch, we plan on testing merging as well. Make test_pull_autostash() accept a parent number as an argument so that, in the future, we can test if a merge happened in addition to a rebase.

---
 t/t5520-pull.sh | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)
t/t5520-pull.sh +8 -6
index 2f86fca042..f610dc14de 100755
@@ -10,11 +10,13 @@ modify () {
 }
 
 test_pull_autostash () {
+	expect_parent_num="$1" &&
+	shift &&
 	git reset --hard before-rebase &&
 	echo dirty >new_file &&
 	git add new_file &&
 	git pull "$@" . copy &&
-	test_cmp_rev HEAD^ copy &&
+	test_cmp_rev HEAD^"$expect_parent_num" copy &&
 	echo dirty >expect &&
 	test_cmp expect new_file &&
 	echo "modified again" >expect &&
@@ -369,22 +371,22 @@ test_expect_success '--rebase fails with multiple branches' '
 
 test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' '
 	test_config rebase.autostash true &&
-	test_pull_autostash --rebase
+	test_pull_autostash 1 --rebase
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash=true' '
 	test_config rebase.autostash true &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash=false' '
 	test_config rebase.autostash false &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash unset' '
 	test_unconfig rebase.autostash &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '
@@ -422,7 +424,7 @@ test_expect_success 'pull.rebase' '
 
 test_expect_success 'pull --autostash & pull.rebase=true' '
 	test_config pull.rebase true &&
-	test_pull_autostash --autostash
+	test_pull_autostash 1 --autostash
 '
 
 test_expect_success 'pull --no-autostash & pull.rebase=true' '
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#115 lore raw

PATCH v4 19/23 sequencer: implement save_autostash()

In reply to Denton Liu

Extract common functionality of apply_autostash() into apply_save_autostash() and use it to implement save_autostash(). This function will be used in a future commit.

The difference between save_autostash() and apply_autostash() is that the latter does not try to apply the stash. It skips that step and just stores the created entry in the stash reflog.

---
 sequencer.c | 37 +++++++++++++++++++++++++++----------
 sequencer.h |  1 +
 2 files changed, 28 insertions(+), 10 deletions(-)
sequencer.c +27 -10
index 5dd1c2438e..fb52583bc2 100644
@@ -3699,7 +3699,7 @@ void create_autostash(struct repository *r, const char *path,
 	strbuf_release(&buf);
 }
 
-int apply_autostash(const char *path)
+static int apply_save_autostash(const char *path, int attempt_apply)
 {
 	struct strbuf stash_oid = STRBUF_INIT;
 	struct child_process child = CHILD_PROCESS_INIT;
@@ -3712,13 +3712,17 @@ int apply_autostash(const char *path)
 	}
 	strbuf_trim(&stash_oid);
 
-	child.git_cmd = 1;
-	child.no_stdout = 1;
-	child.no_stderr = 1;
-	argv_array_push(&child.args, "stash");
-	argv_array_push(&child.args, "apply");
-	argv_array_push(&child.args, stash_oid.buf);
-	if (!run_command(&child))
+	if (attempt_apply) {
+		child.git_cmd = 1;
+		child.no_stdout = 1;
+		child.no_stderr = 1;
+		argv_array_push(&child.args, "stash");
+		argv_array_push(&child.args, "apply");
+		argv_array_push(&child.args, stash_oid.buf);
+		ret = run_command(&child);
+	}
+
+	if (attempt_apply && !ret)
 		fprintf(stderr, _("Applied autostash.\n"));
 	else {
 		struct child_process store = CHILD_PROCESS_INIT;
@@ -3734,10 +3738,13 @@ int apply_autostash(const char *path)
 			ret = error(_("cannot store %s"), stash_oid.buf);
 		else
 			fprintf(stderr,
-				_("Applying autostash resulted in conflicts.\n"
+				_("%s\n"
 				  "Your changes are safe in the stash.\n"
 				  "You can run \"git stash pop\" or"
-				  " \"git stash drop\" at any time.\n"));
+				  " \"git stash drop\" at any time.\n"),
+				attempt_apply ?
+				_("Applying autostash resulted in conflicts.") :
+				_("Autostash exists; creating a new stash entry."));
 	}
 
 	unlink(path);
@@ -3745,6 +3752,16 @@ int apply_autostash(const char *path)
 	return ret;
 }
 
+int save_autostash(const char *path)
+{
+	return apply_save_autostash(path, 0);
+}
+
+int apply_autostash(const char *path)
+{
+	return apply_save_autostash(path, 1);
+}
+
 static const char *reflog_message(struct replay_opts *opts,
 	const char *sub_action, const char *fmt, ...)
 {
sequencer.h +1 -0
index cf1284f9ed..2d09c1ac0b 100644
@@ -193,6 +193,7 @@ void commit_post_rewrite(struct repository *r,
 
 void create_autostash(struct repository *r, const char *path,
 		      const char *default_reflog_action);
+int save_autostash(const char *path);
 int apply_autostash(const char *path);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
Signature · 1 line
2.26.0.159.g23e2136ad0
author
#123 lore raw

PATCH v4 23/23 pull: pass --autostash to merge

In reply to Denton Liu

Before, --autostash only worked with git pull --rebase. However, in the last patch, merge learned --autostash as well so there's no reason why we should have this restriction anymore. Teach pull to pass --autostash to merge, just like it did for rebase.

---
 Documentation/git-pull.txt      |  9 -------
 Documentation/merge-options.txt |  4 +--
 builtin/pull.c                  |  9 ++++---
 t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------
 4 files changed, 42 insertions(+), 23 deletions(-)
Documentation/git-pull.txt +0 -9
index dfb901f8b8..ba3772de9f 100644
@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.
 --no-rebase::
 	Override earlier --rebase.
 
---autostash::
---no-autostash::
-	Before starting rebase, stash local modifications away (see
-	linkgit:git-stash[1]) if needed, and apply the stash entry when
-	done. `--no-autostash` is useful to override the `rebase.autoStash`
-	configuration variable (see linkgit:git-config[1]).
-+
-This option is only valid when "--rebase" is used.
-
 Options related to fetching
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
Documentation/merge-options.txt +2 -2
index 3985e6d4a9..48bfcda084 100644
@@ -155,6 +155,8 @@ ifndef::git-pull[]
 	Note that not all merge strategies may support progress
 	reporting.
 
+endif::git-pull[]
+
 --autostash::
 --no-autostash::
 	Automatically create a temporary stash entry before the operation
@@ -163,8 +165,6 @@ ifndef::git-pull[]
 	with care: the final stash application after a successful
 	merge might result in non-trivial conflicts.
 
-endif::git-pull[]
-
 --allow-unrelated-histories::
 	By default, `git merge` command refuses to merge histories
 	that do not share a common ancestor.  This option can be
builtin/pull.c +5 -4
index 3e624d1e00..9beb4841d1 100644
@@ -163,7 +163,7 @@ static struct option pull_options[] = {
 		N_("verify that the named commit has a valid GPG signature"),
 		PARSE_OPT_NOARG),
 	OPT_BOOL(0, "autostash", &opt_autostash,
-		N_("automatically stash/stash pop before and after rebase")),
+		N_("automatically stash/stash pop before and after")),
 	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
 		N_("merge strategy to use"),
 		0),
@@ -661,6 +661,10 @@ static int run_merge(void)
 	argv_array_pushv(&args, opt_strategy_opts.argv);
 	if (opt_gpg_sign)
 		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");
 	if (opt_allow_unrelated_histories > 0)
 		argv_array_push(&args, "--allow-unrelated-histories");
 
@@ -908,9 +912,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 	if (get_oid("HEAD", &orig_head))
 		oidclr(&orig_head);
 
-	if (!opt_rebase && opt_autostash != -1)
-		die(_("--[no-]autostash option is only valid with --rebase."));
-
 	autostash = config_autostash;
 	if (opt_rebase) {
 		if (opt_autostash != -1)
t/t5520-pull.sh +35 -8

55 lines not on this page · raw message

Signature · 1 line
2.26.0.159.g23e2136ad0
author
#105 lore raw

PATCH v4 10/23 sequencer: make apply_rebase() accept a path

In reply to Denton Liu

In order to make apply_rebase() more generic for future extraction, make it accept a path argument so that the location from where to read the reference to the autostash commit can be customized. Remove the opts argument since it was unused before anyway.

---
 sequencer.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)
sequencer.c +7 -7
index d1d59867ae..ae9efe5825 100644
@@ -3649,13 +3649,13 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
 	return -1;
 }
 
-static int apply_autostash(struct replay_opts *opts)
+static int apply_autostash(const char *path)
 {
 	struct strbuf stash_sha1 = STRBUF_INIT;
 	struct child_process child = CHILD_PROCESS_INIT;
 	int ret = 0;
 
-	if (!read_oneliner(&stash_sha1, rebase_path_autostash(),
+	if (!read_oneliner(&stash_sha1, path,
 			   READ_ONELINER_SKIP_IF_EMPTY)) {
 		strbuf_release(&stash_sha1);
 		return 0;
@@ -3748,7 +3748,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,
 		return error(_("%s: not a valid OID"), orig_head);
 
 	if (run_git_checkout(r, opts, oid_to_hex(onto), action)) {
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 		sequencer_remove_state(opts);
 		return error(_("could not detach HEAD"));
 	}
@@ -4061,7 +4061,7 @@ static int pick_commits(struct repository *r,
 				run_command(&hook);
 			}
 		}
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 
 		if (!opts->quiet) {
 			if (!opts->verbose)
@@ -5070,7 +5070,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
 		todo_list_add_exec_commands(todo_list, commands);
 
 	if (count_commands(todo_list) == 0) {
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 		sequencer_remove_state(opts);
 
 		return error(_("nothing to do"));
@@ -5081,12 +5081,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
 	if (res == -1)
 		return -1;
 	else if (res == -2) {
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 		sequencer_remove_state(opts);
 
 		return -1;
 	} else if (res == -3) {
-		apply_autostash(opts);
+		apply_autostash(rebase_path_autostash());
 		sequencer_remove_state(opts);
 		todo_list_release(&new_todo);
 
Signature · 1 line
2.26.0.159.g23e2136ad0