{"thread":{"id":"52048","subject":"[RFC PATCH 0/7] merge: learn --autostash","startedAt":"2019-10-16T17:26:24Z","lastAt":"2020-04-10T16:26:49Z","messageCount":152,"participants":["Denton Liu","Junio C Hamano","Phillip Wood","Johannes Schindelin","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":7},"messages":[{"id":"384189","messageId":"cover.1571246693.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":null,"subject":"[RFC PATCH 0/7] merge: learn --autostash","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-16T17:26:17Z","receivedAt":"2019-10-16T17:26:24Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Alban reported[1] that he expected merge to have an --autostash option,\njust like rebase. Since there's not really any reason why merge can't\nhave it, let's implement it in this patchset.\n\nThe actual logic isn't too bad. That change can be found in the last\npatch. The remainder is refactoring so that the change can be made\neasily. One thing I would like some attention on is if apply_autostash()\nis called in the right place. It's called somewhere above\nclose_object_store() and also in the --abort case, just like in rebase,\nbut I'm not quite sure if that's right.\n\nSince this is an RFC, we're missing tests and documentation but I've\ntested it and it seems to work reasonably well.\n\n[1]: https://github.com/gitgitgadget/git/issues/394\n\nDenton Liu (7):\n  Makefile: alphabetically sort += lists\n  autostash: extract read_one() from rebase\n  autostash: extract apply_autostash() from rebase\n  autostash: extract reset_head() from rebase\n  autostash: extract perform_autostash() from rebase\n  autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS\n  merge: teach --autostash option\n\n Makefile         |  75 +++++++--------\n autostash.c      | 239 ++++++++++++++++++++++++++++++++++++++++++++++\n autostash.h      |  24 +++++\n builtin/merge.c  |  13 +++\n builtin/pull.c   |   9 +-\n builtin/rebase.c | 240 +----------------------------------------------\n t/t5520-pull.sh  |   8 --\n 7 files changed, 323 insertions(+), 285 deletions(-)\n create mode 100644 autostash.c\n create mode 100644 autostash.h\n\n-- \n2.23.0.897.g0a19638b1e\n\n"},{"id":"384190","messageId":"02a16f9bdf740841d9a4be765e72b9fa5ae5d75c.1571246693.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1571246693.git.liu.denton@gmail.com","subject":"[RFC PATCH 1/7] Makefile: alphabetically sort += lists","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-16T17:26:21Z","receivedAt":"2019-10-16T17:26:28Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"There are many += lists in the Makefile and, over time, they have gotten\nslightly out of order, alphabetically. Alphabetically sort all += lists\nto bring them back in order.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Makefile | 75 ++++++++++++++++++++++++++++----------------------------\n 1 file changed, 38 insertions(+), 37 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex de60c8e7aa..268a273df5 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -604,12 +604,12 @@ unexport CDPATH\n SCRIPT_SH += git-bisect.sh\n SCRIPT_SH += git-difftool--helper.sh\n SCRIPT_SH += git-filter-branch.sh\n+SCRIPT_SH += git-legacy-stash.sh\n SCRIPT_SH += git-merge-octopus.sh\n SCRIPT_SH += git-merge-one-file.sh\n SCRIPT_SH += git-merge-resolve.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-quiltimport.sh\n-SCRIPT_SH += git-legacy-stash.sh\n SCRIPT_SH += git-request-pull.sh\n SCRIPT_SH += git-submodule.sh\n SCRIPT_SH += git-web--browse.sh\n@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh\n SCRIPT_LIB += git-mergetool--lib\n SCRIPT_LIB += git-parse-remote\n SCRIPT_LIB += git-rebase--preserve-merges\n-SCRIPT_LIB += git-sh-setup\n SCRIPT_LIB += git-sh-i18n\n+SCRIPT_LIB += git-sh-setup\n \n SCRIPT_PERL += git-add--interactive.perl\n SCRIPT_PERL += git-archimport.perl\n@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o\n PROGRAM_OBJS += fast-import.o\n PROGRAM_OBJS += http-backend.o\n PROGRAM_OBJS += imap-send.o\n+PROGRAM_OBJS += remote-testsvn.o\n PROGRAM_OBJS += sh-i18n--envsubst.o\n PROGRAM_OBJS += shell.o\n-PROGRAM_OBJS += remote-testsvn.o\n \n # Binary suffix, set to .exe for Windows builds\n X =\n@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o\n TEST_BUILTINS_OBJS += test-example-decorate.o\n TEST_BUILTINS_OBJS += test-genrandom.o\n TEST_BUILTINS_OBJS += test-genzeros.o\n+TEST_BUILTINS_OBJS += test-hash-speed.o\n TEST_BUILTINS_OBJS += test-hash.o\n TEST_BUILTINS_OBJS += test-hashmap.o\n-TEST_BUILTINS_OBJS += test-hash-speed.o\n TEST_BUILTINS_OBJS += test-index-version.o\n TEST_BUILTINS_OBJS += test-json-writer.o\n TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o\n@@ -735,8 +735,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o\n TEST_BUILTINS_OBJS += test-run-command.o\n TEST_BUILTINS_OBJS += test-scrap-cache-tree.o\n TEST_BUILTINS_OBJS += test-serve-v2.o\n-TEST_BUILTINS_OBJS += test-sha1.o\n TEST_BUILTINS_OBJS += test-sha1-array.o\n+TEST_BUILTINS_OBJS += test-sha1.o\n TEST_BUILTINS_OBJS += test-sha256.o\n TEST_BUILTINS_OBJS += test-sigchain.o\n TEST_BUILTINS_OBJS += test-strcmp-offset.o\n@@ -746,10 +746,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o\n TEST_BUILTINS_OBJS += test-subprocess.o\n TEST_BUILTINS_OBJS += test-trace2.o\n TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n-TEST_BUILTINS_OBJS += test-xml-encode.o\n TEST_BUILTINS_OBJS += test-wildmatch.o\n TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n TEST_BUILTINS_OBJS += test-write-cache.o\n+TEST_BUILTINS_OBJS += test-xml-encode.o\n \n # Do not add more tests here unless they have extra dependencies. Add\n # them in TEST_BUILTINS_OBJS above.\n@@ -786,10 +786,10 @@ OTHER_PROGRAMS = git$X\n \n # what test wrappers are needed and 'install' will install, in bindir\n BINDIR_PROGRAMS_NEED_X += git\n-BINDIR_PROGRAMS_NEED_X += git-upload-pack\n BINDIR_PROGRAMS_NEED_X += git-receive-pack\n-BINDIR_PROGRAMS_NEED_X += git-upload-archive\n BINDIR_PROGRAMS_NEED_X += git-shell\n+BINDIR_PROGRAMS_NEED_X += git-upload-archive\n+BINDIR_PROGRAMS_NEED_X += git-upload-pack\n \n BINDIR_PROGRAMS_NO_X += git-cvsserver\n \n@@ -827,11 +827,12 @@ LIB_OBJS += advice.o\n LIB_OBJS += alias.o\n LIB_OBJS += alloc.o\n LIB_OBJS += apply.o\n-LIB_OBJS += archive.o\n LIB_OBJS += archive-tar.o\n LIB_OBJS += archive-zip.o\n+LIB_OBJS += archive.o\n LIB_OBJS += argv-array.o\n LIB_OBJS += attr.o\n+LIB_OBJS += autostash.o\n LIB_OBJS += base85.o\n LIB_OBJS += bisect.o\n LIB_OBJS += blame.o\n@@ -845,9 +846,9 @@ LIB_OBJS += checkout.o\n LIB_OBJS += color.o\n LIB_OBJS += column.o\n LIB_OBJS += combine-diff.o\n-LIB_OBJS += commit.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n+LIB_OBJS += commit.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\n LIB_OBJS += config.o\n@@ -861,17 +862,17 @@ LIB_OBJS += ctype.o\n LIB_OBJS += date.o\n LIB_OBJS += decorate.o\n LIB_OBJS += delta-islands.o\n+LIB_OBJS += diff-delta.o\n+LIB_OBJS += diff-lib.o\n+LIB_OBJS += diff-no-index.o\n+LIB_OBJS += diff.o\n LIB_OBJS += diffcore-break.o\n LIB_OBJS += diffcore-delta.o\n LIB_OBJS += diffcore-order.o\n LIB_OBJS += diffcore-pickaxe.o\n LIB_OBJS += diffcore-rename.o\n-LIB_OBJS += diff-delta.o\n-LIB_OBJS += diff-lib.o\n-LIB_OBJS += diff-no-index.o\n-LIB_OBJS += diff.o\n-LIB_OBJS += dir.o\n LIB_OBJS += dir-iterator.o\n+LIB_OBJS += dir.o\n LIB_OBJS += editor.o\n LIB_OBJS += entry.o\n LIB_OBJS += environment.o\n@@ -889,7 +890,6 @@ LIB_OBJS += gpg-interface.o\n LIB_OBJS += graph.o\n LIB_OBJS += grep.o\n LIB_OBJS += hashmap.o\n-LIB_OBJS += linear-assignment.o\n LIB_OBJS += help.o\n LIB_OBJS += hex.o\n LIB_OBJS += ident.o\n@@ -899,9 +899,10 @@ LIB_OBJS += kwset.o\n LIB_OBJS += levenshtein.o\n LIB_OBJS += line-log.o\n LIB_OBJS += line-range.o\n-LIB_OBJS += list-objects.o\n-LIB_OBJS += list-objects-filter.o\n+LIB_OBJS += linear-assignment.o\n LIB_OBJS += list-objects-filter-options.o\n+LIB_OBJS += list-objects-filter.o\n+LIB_OBJS += list-objects.o\n LIB_OBJS += ll-merge.o\n LIB_OBJS += lockfile.o\n LIB_OBJS += log-tree.o\n@@ -910,31 +911,31 @@ LIB_OBJS += mailinfo.o\n LIB_OBJS += mailmap.o\n LIB_OBJS += match-trees.o\n LIB_OBJS += mem-pool.o\n-LIB_OBJS += merge.o\n LIB_OBJS += merge-blobs.o\n LIB_OBJS += merge-recursive.o\n+LIB_OBJS += merge.o\n LIB_OBJS += mergesort.o\n LIB_OBJS += midx.o\n LIB_OBJS += name-hash.o\n LIB_OBJS += negotiator/default.o\n LIB_OBJS += negotiator/skipping.o\n-LIB_OBJS += notes.o\n LIB_OBJS += notes-cache.o\n LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\n+LIB_OBJS += notes.o\n LIB_OBJS += object.o\n LIB_OBJS += oidmap.o\n LIB_OBJS += oidset.o\n-LIB_OBJS += packfile.o\n-LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-bitmap-write.o\n+LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-objects.o\n LIB_OBJS += pack-revindex.o\n LIB_OBJS += pack-write.o\n+LIB_OBJS += packfile.o\n LIB_OBJS += pager.o\n-LIB_OBJS += parse-options.o\n LIB_OBJS += parse-options-cb.o\n+LIB_OBJS += parse-options.o\n LIB_OBJS += patch-delta.o\n LIB_OBJS += patch-ids.o\n LIB_OBJS += path.o\n@@ -952,6 +953,7 @@ LIB_OBJS += range-diff.o\n LIB_OBJS += reachable.o\n LIB_OBJS += read-cache.o\n LIB_OBJS += rebase-interactive.o\n+LIB_OBJS += ref-filter.o\n LIB_OBJS += reflog-walk.o\n LIB_OBJS += refs.o\n LIB_OBJS += refs/files-backend.o\n@@ -959,7 +961,6 @@ LIB_OBJS += refs/iterator.o\n LIB_OBJS += refs/packed-backend.o\n LIB_OBJS += refs/ref-cache.o\n LIB_OBJS += refspec.o\n-LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace-object.o\n LIB_OBJS += repo-settings.o\n@@ -974,8 +975,8 @@ LIB_OBJS += serve.o\n LIB_OBJS += server-info.o\n LIB_OBJS += setup.o\n LIB_OBJS += sha1-array.o\n-LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1-file.o\n+LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1-name.o\n LIB_OBJS += shallow.o\n LIB_OBJS += sideband.o\n@@ -985,9 +986,9 @@ LIB_OBJS += stable-qsort.o\n LIB_OBJS += strbuf.o\n LIB_OBJS += streaming.o\n LIB_OBJS += string-list.o\n-LIB_OBJS += submodule.o\n-LIB_OBJS += submodule-config.o\n LIB_OBJS += sub-process.o\n+LIB_OBJS += submodule-config.o\n+LIB_OBJS += submodule.o\n LIB_OBJS += symlinks.o\n LIB_OBJS += tag.o\n LIB_OBJS += tempfile.o\n@@ -1006,11 +1007,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o\n LIB_OBJS += trace2/tr2_tgt_perf.o\n LIB_OBJS += trace2/tr2_tls.o\n LIB_OBJS += trailer.o\n-LIB_OBJS += transport.o\n LIB_OBJS += transport-helper.o\n+LIB_OBJS += transport.o\n LIB_OBJS += tree-diff.o\n-LIB_OBJS += tree.o\n LIB_OBJS += tree-walk.o\n+LIB_OBJS += tree.o\n LIB_OBJS += unpack-trees.o\n LIB_OBJS += upload-pack.o\n LIB_OBJS += url.o\n@@ -1050,9 +1051,9 @@ BUILTIN_OBJS += builtin/checkout.o\n BUILTIN_OBJS += builtin/clean.o\n BUILTIN_OBJS += builtin/clone.o\n BUILTIN_OBJS += builtin/column.o\n+BUILTIN_OBJS += builtin/commit-graph.o\n BUILTIN_OBJS += builtin/commit-tree.o\n BUILTIN_OBJS += builtin/commit.o\n-BUILTIN_OBJS += builtin/commit-graph.o\n BUILTIN_OBJS += builtin/config.o\n BUILTIN_OBJS += builtin/count-objects.o\n BUILTIN_OBJS += builtin/credential.o\n@@ -1083,13 +1084,13 @@ BUILTIN_OBJS += builtin/ls-remote.o\n BUILTIN_OBJS += builtin/ls-tree.o\n BUILTIN_OBJS += builtin/mailinfo.o\n BUILTIN_OBJS += builtin/mailsplit.o\n-BUILTIN_OBJS += builtin/merge.o\n BUILTIN_OBJS += builtin/merge-base.o\n BUILTIN_OBJS += builtin/merge-file.o\n BUILTIN_OBJS += builtin/merge-index.o\n BUILTIN_OBJS += builtin/merge-ours.o\n BUILTIN_OBJS += builtin/merge-recursive.o\n BUILTIN_OBJS += builtin/merge-tree.o\n+BUILTIN_OBJS += builtin/merge.o\n BUILTIN_OBJS += builtin/mktag.o\n BUILTIN_OBJS += builtin/mktree.o\n BUILTIN_OBJS += builtin/multi-pack-index.o\n@@ -1109,9 +1110,9 @@ BUILTIN_OBJS += builtin/read-tree.o\n BUILTIN_OBJS += builtin/rebase.o\n BUILTIN_OBJS += builtin/receive-pack.o\n BUILTIN_OBJS += builtin/reflog.o\n-BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/remote-ext.o\n BUILTIN_OBJS += builtin/remote-fd.o\n+BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/rerere.o\n@@ -2325,16 +2326,16 @@ reconfigure config.mak.autogen: config.status\n endif\n \n XDIFF_OBJS += xdiff/xdiffi.o\n-XDIFF_OBJS += xdiff/xprepare.o\n-XDIFF_OBJS += xdiff/xutils.o\n XDIFF_OBJS += xdiff/xemit.o\n+XDIFF_OBJS += xdiff/xhistogram.o\n XDIFF_OBJS += xdiff/xmerge.o\n XDIFF_OBJS += xdiff/xpatience.o\n-XDIFF_OBJS += xdiff/xhistogram.o\n+XDIFF_OBJS += xdiff/xprepare.o\n+XDIFF_OBJS += xdiff/xutils.o\n \n+VCSSVN_OBJS += vcs-svn/fast_export.o\n VCSSVN_OBJS += vcs-svn/line_buffer.o\n VCSSVN_OBJS += vcs-svn/sliding_window.o\n-VCSSVN_OBJS += vcs-svn/fast_export.o\n VCSSVN_OBJS += vcs-svn/svndiff.o\n VCSSVN_OBJS += vcs-svn/svndump.o\n \n@@ -3143,9 +3144,9 @@ endif\n #\n ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS)\n ALL_COMMANDS += git\n+ALL_COMMANDS += git-gui git-citool\n ALL_COMMANDS += gitk\n ALL_COMMANDS += gitweb\n-ALL_COMMANDS += git-gui git-citool\n \n .PHONY: check-docs\n check-docs::\n-- \n2.23.0.897.g0a19638b1e\n\n"},{"id":"384191","messageId":"bbd46ad8d27ab547c8efa6b55c1777becbab7302.1571246693.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1571246693.git.liu.denton@gmail.com","subject":"[RFC PATCH 2/7] autostash: extract read_one() from rebase","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-16T17:26:23Z","receivedAt":"2019-10-16T17:26:28Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Begin the process of lib-ifying the autostash code. In a future commit,\nthis will be used to implement `--autostash` in other builtins.\n\nThis patch is best viewed with `--color-moved` and\n`--color-moved-ws=allow-indentation-change`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n autostash.c      | 12 ++++++++++++\n autostash.h      |  9 +++++++++\n builtin/rebase.c | 10 +---------\n 3 files changed, 22 insertions(+), 9 deletions(-)\n create mode 100644 autostash.c\n create mode 100644 autostash.h\n\ndiff --git a/autostash.c b/autostash.c\nnew file mode 100644\nindex 0000000000..a6898e0fda\n--- /dev/null\n+++ b/autostash.c\n@@ -0,0 +1,12 @@\n+#include \"git-compat-util.h\"\n+#include \"autostash.h\"\n+#include \"gettext.h\"\n+#include \"strbuf.h\"\n+\n+int read_one(const char *path, struct strbuf *buf)\n+{\n+\tif (strbuf_read_file(buf, path, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'\"), path);\n+\tstrbuf_trim_trailing_newline(buf);\n+\treturn 0;\n+}\ndiff --git a/autostash.h b/autostash.h\nnew file mode 100644\nindex 0000000000..4a8f504f12\n--- /dev/null\n+++ b/autostash.h\n@@ -0,0 +1,9 @@\n+#ifndef AUTOSTASH_H\n+#define AUTOSTASH_H\n+\n+#include \"strbuf.h\"\n+\n+/* Read one file, then strip line endings */\n+int read_one(const char *path, struct strbuf *buf);\n+\n+#endif\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 4a20582e72..9fd7de6b2f 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -27,6 +27,7 @@\n #include \"branch.h\"\n #include \"sequencer.h\"\n #include \"rebase-interactive.h\"\n+#include \"autostash.h\"\n \n static char const * const builtin_rebase_usage[] = {\n \tN_(\"git rebase [-i] [options] [--exec <cmd>] \"\n@@ -561,15 +562,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o\n \treturn path.buf;\n }\n \n-/* Read one file, then strip line endings */\n-static int read_one(const char *path, struct strbuf *buf)\n-{\n-\tif (strbuf_read_file(buf, path, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'\"), path);\n-\tstrbuf_trim_trailing_newline(buf);\n-\treturn 0;\n-}\n-\n /* Initialize the rebase options from the state directory. */\n static int read_basic_state(struct rebase_options *opts)\n {\n-- \n2.23.0.897.g0a19638b1e\n\n"},{"id":"384192","messageId":"0f2f22ad2147b5a8649f351fbde40b22b6dbc46a.1571246693.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1571246693.git.liu.denton@gmail.com","subject":"[RFC PATCH 3/7] autostash: extract apply_autostash() from rebase","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-16T17:26:25Z","receivedAt":"2019-10-16T17:26:32Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Continue the process of lib-ifying the autostash code. In a future\ncommit, this will be used to implement `--autostash` in other builtins.\n\nThis patch is best viewed with `--color-moved` and\n`--color-moved-ws=allow-indentation-change`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n autostash.c      | 46 +++++++++++++++++++++++++++++++++++++++++++++\n autostash.h      |  2 ++\n builtin/rebase.c | 49 ++----------------------------------------------\n 3 files changed, 50 insertions(+), 47 deletions(-)\n\ndiff --git a/autostash.c b/autostash.c\nindex a6898e0fda..62ec7a7c80 100644\n--- a/autostash.c\n+++ b/autostash.c\n@@ -1,6 +1,8 @@\n #include \"git-compat-util.h\"\n #include \"autostash.h\"\n+#include \"dir.h\"\n #include \"gettext.h\"\n+#include \"run-command.h\"\n #include \"strbuf.h\"\n \n int read_one(const char *path, struct strbuf *buf)\n@@ -10,3 +12,47 @@ int read_one(const char *path, struct strbuf *buf)\n \tstrbuf_trim_trailing_newline(buf);\n \treturn 0;\n }\n+\n+int apply_autostash(const char *path)\n+{\n+\tstruct strbuf autostash = STRBUF_INIT;\n+\tstruct child_process stash_apply = CHILD_PROCESS_INIT;\n+\n+\tif (!file_exists(path))\n+\t\treturn 0;\n+\n+\tif (read_one(path, &autostash))\n+\t\treturn error(_(\"Could not read '%s'\"), path);\n+\t/* Ensure that the hash is not mistaken for a number */\n+\tstrbuf_addstr(&autostash, \"^0\");\n+\targv_array_pushl(&stash_apply.args,\n+\t\t\t \"stash\", \"apply\", autostash.buf, NULL);\n+\tstash_apply.git_cmd = 1;\n+\tstash_apply.no_stderr = stash_apply.no_stdout =\n+\t\tstash_apply.no_stdin = 1;\n+\tif (!run_command(&stash_apply))\n+\t\tprintf(_(\"Applied autostash.\\n\"));\n+\telse {\n+\t\tstruct argv_array args = ARGV_ARRAY_INIT;\n+\t\tint res = 0;\n+\n+\t\targv_array_pushl(&args,\n+\t\t\t\t \"stash\", \"store\", \"-m\", \"autostash\", \"-q\",\n+\t\t\t\t autostash.buf, NULL);\n+\t\tif (run_command_v_opt(args.argv, RUN_GIT_CMD))\n+\t\t\tres = error(_(\"Cannot store %s\"), autostash.buf);\n+\t\targv_array_clear(&args);\n+\t\tstrbuf_release(&autostash);\n+\t\tif (res)\n+\t\t\treturn res;\n+\n+\t\tfprintf(stderr,\n+\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n+\t\t\t  \"Your changes are safe in the stash.\\n\"\n+\t\t\t  \"You can run \\\"git stash pop\\\" or \\\"git stash drop\\\" \"\n+\t\t\t  \"at any time.\\n\"));\n+\t}\n+\n+\tstrbuf_release(&autostash);\n+\treturn 0;\n+}\ndiff --git a/autostash.h b/autostash.h\nindex 4a8f504f12..5f4e4bd22c 100644\n--- a/autostash.h\n+++ b/autostash.h\n@@ -6,4 +6,6 @@\n /* Read one file, then strip line endings */\n int read_one(const char *path, struct strbuf *buf);\n \n+int apply_autostash(const char *path);\n+\n #endif\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 9fd7de6b2f..661928d427 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -682,51 +682,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)\n \treturn 0;\n }\n \n-static int apply_autostash(struct rebase_options *opts)\n-{\n-\tconst char *path = state_dir_path(\"autostash\", opts);\n-\tstruct strbuf autostash = STRBUF_INIT;\n-\tstruct child_process stash_apply = CHILD_PROCESS_INIT;\n-\n-\tif (!file_exists(path))\n-\t\treturn 0;\n-\n-\tif (read_one(path, &autostash))\n-\t\treturn error(_(\"Could not read '%s'\"), path);\n-\t/* Ensure that the hash is not mistaken for a number */\n-\tstrbuf_addstr(&autostash, \"^0\");\n-\targv_array_pushl(&stash_apply.args,\n-\t\t\t \"stash\", \"apply\", autostash.buf, NULL);\n-\tstash_apply.git_cmd = 1;\n-\tstash_apply.no_stderr = stash_apply.no_stdout =\n-\t\tstash_apply.no_stdin = 1;\n-\tif (!run_command(&stash_apply))\n-\t\tprintf(_(\"Applied autostash.\\n\"));\n-\telse {\n-\t\tstruct argv_array args = ARGV_ARRAY_INIT;\n-\t\tint res = 0;\n-\n-\t\targv_array_pushl(&args,\n-\t\t\t\t \"stash\", \"store\", \"-m\", \"autostash\", \"-q\",\n-\t\t\t\t autostash.buf, NULL);\n-\t\tif (run_command_v_opt(args.argv, RUN_GIT_CMD))\n-\t\t\tres = error(_(\"Cannot store %s\"), autostash.buf);\n-\t\targv_array_clear(&args);\n-\t\tstrbuf_release(&autostash);\n-\t\tif (res)\n-\t\t\treturn res;\n-\n-\t\tfprintf(stderr,\n-\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n-\t\t\t  \"Your changes are safe in the stash.\\n\"\n-\t\t\t  \"You can run \\\"git stash pop\\\" or \\\"git stash drop\\\" \"\n-\t\t\t  \"at any time.\\n\"));\n-\t}\n-\n-\tstrbuf_release(&autostash);\n-\treturn 0;\n-}\n-\n static int finish_rebase(struct rebase_options *opts)\n {\n \tstruct strbuf dir = STRBUF_INIT;\n@@ -734,7 +689,7 @@ static int finish_rebase(struct rebase_options *opts)\n \tint ret = 0;\n \n \tdelete_ref(NULL, \"REBASE_HEAD\", NULL, REF_NO_DEREF);\n-\tapply_autostash(opts);\n+\tapply_autostash(state_dir_path(\"autostash\", opts));\n \tclose_object_store(the_repository->objects);\n \t/*\n \t * We ignore errors in 'gc --auto', since the\n@@ -1181,7 +1136,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)\n \t} else if (status == 2) {\n \t\tstruct strbuf dir = STRBUF_INIT;\n \n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(state_dir_path(\"autostash\", opts));\n \t\tstrbuf_addstr(&dir, opts->state_dir);\n \t\tremove_dir_recursively(&dir, 0);\n \t\tstrbuf_release(&dir);\n-- \n2.23.0.897.g0a19638b1e\n\n"},{"id":"384193","messageId":"5e0dcd7cbeb3481beef04f1d145474739f8bdea3.1571246693.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1571246693.git.liu.denton@gmail.com","subject":"[RFC PATCH 4/7] autostash: extract reset_head() from rebase","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-16T17:26:27Z","receivedAt":"2019-10-16T17:26:33Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Begin the process of lib-ifying the autostash code. In a future commit,\n\nThis patch is best viewed with `--color-moved` and\n`--color-moved-ws=allow-indentation-change`.\nthis will be used to implement `--autostash` in other builtins.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n autostash.c      | 137 +++++++++++++++++++++++++++++++++++++++++++++++\n autostash.h      |  12 +++++\n builtin/rebase.c | 137 -----------------------------------------------\n 3 files changed, 149 insertions(+), 137 deletions(-)\n\ndiff --git a/autostash.c b/autostash.c\nindex 62ec7a7c80..eb58e0c8a4 100644\n--- a/autostash.c\n+++ b/autostash.c\n@@ -1,9 +1,17 @@\n+#define USE_THE_INDEX_COMPATIBILITY_MACROS\n+\n #include \"git-compat-util.h\"\n #include \"autostash.h\"\n+#include \"cache-tree.h\"\n #include \"dir.h\"\n #include \"gettext.h\"\n+#include \"lockfile.h\"\n+#include \"refs.h\"\n #include \"run-command.h\"\n #include \"strbuf.h\"\n+#include \"tree-walk.h\"\n+#include \"tree.h\"\n+#include \"unpack-trees.h\"\n \n int read_one(const char *path, struct strbuf *buf)\n {\n@@ -13,6 +21,135 @@ int read_one(const char *path, struct strbuf *buf)\n \treturn 0;\n }\n \n+int reset_head(struct object_id *oid, const char *action,\n+\t       const char *switch_to_branch, unsigned flags,\n+\t       const char *reflog_orig_head, const char *reflog_head)\n+{\n+\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n+\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n+\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n+\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n+\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n+\tstruct object_id head_oid;\n+\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n+\tstruct lock_file lock = LOCK_INIT;\n+\tstruct unpack_trees_options unpack_tree_opts;\n+\tstruct tree *tree;\n+\tconst char *reflog_action;\n+\tstruct strbuf msg = STRBUF_INIT;\n+\tsize_t prefix_len;\n+\tstruct object_id *orig = NULL, oid_orig,\n+\t\t*old_orig = NULL, oid_old_orig;\n+\tint ret = 0, nr = 0;\n+\n+\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n+\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n+\n+\tif (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {\n+\t\tret = -1;\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n+\t\tret = error(_(\"could not determine HEAD revision\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!oid)\n+\t\toid = &head_oid;\n+\n+\tif (refs_only)\n+\t\tgoto reset_head_refs;\n+\n+\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n+\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n+\tunpack_tree_opts.head_idx = 1;\n+\tunpack_tree_opts.src_index = the_repository->index;\n+\tunpack_tree_opts.dst_index = the_repository->index;\n+\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n+\tunpack_tree_opts.update = 1;\n+\tunpack_tree_opts.merge = 1;\n+\tif (!detach_head)\n+\t\tunpack_tree_opts.reset = 1;\n+\n+\tif (repo_read_index_unmerged(the_repository) < 0) {\n+\t\tret = error(_(\"could not read index\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {\n+\t\tret = error(_(\"failed to find tree of %s\"),\n+\t\t\t    oid_to_hex(&head_oid));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {\n+\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n+\t\tret = -1;\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\ttree = parse_tree_indirect(oid);\n+\tprime_cache_tree(the_repository, the_repository->index, tree);\n+\n+\tif (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {\n+\t\tret = error(_(\"could not write index\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+reset_head_refs:\n+\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n+\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : \"rebase\");\n+\tprefix_len = msg.len;\n+\n+\tif (update_orig_head) {\n+\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n+\t\t\told_orig = &oid_old_orig;\n+\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n+\t\t\torig = &oid_orig;\n+\t\t\tif (!reflog_orig_head) {\n+\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n+\t\t\t\treflog_orig_head = msg.buf;\n+\t\t\t}\n+\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n+\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n+\t\t} else if (old_orig)\n+\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n+\t}\n+\n+\tif (!reflog_head) {\n+\t\tstrbuf_setlen(&msg, prefix_len);\n+\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n+\t\treflog_head = msg.buf;\n+\t}\n+\tif (!switch_to_branch)\n+\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n+\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n+\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n+\telse {\n+\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n+\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n+\t\tif (!ret)\n+\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n+\t\t\t\t\t    reflog_head);\n+\t}\n+\tif (run_hook)\n+\t\trun_hook_le(NULL, \"post-checkout\",\n+\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n+\t\t\t    oid_to_hex(oid), \"1\", NULL);\n+\n+leave_reset_head:\n+\tstrbuf_release(&msg);\n+\trollback_lock_file(&lock);\n+\twhile (nr)\n+\t\tfree((void *)desc[--nr].buffer);\n+\treturn ret;\n+}\n+\n int apply_autostash(const char *path)\n {\n \tstruct strbuf autostash = STRBUF_INIT;\ndiff --git a/autostash.h b/autostash.h\nindex 5f4e4bd22c..1406638166 100644\n--- a/autostash.h\n+++ b/autostash.h\n@@ -6,6 +6,18 @@\n /* Read one file, then strip line endings */\n int read_one(const char *path, struct strbuf *buf);\n \n+#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n+\n+#define RESET_HEAD_DETACH (1<<0)\n+#define RESET_HEAD_HARD (1<<1)\n+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n+#define RESET_HEAD_REFS_ONLY (1<<3)\n+#define RESET_ORIG_HEAD (1<<4)\n+\n+int reset_head(struct object_id *oid, const char *action,\n+\t       const char *switch_to_branch, unsigned flags,\n+\t       const char *reflog_orig_head, const char *reflog_head);\n+\n int apply_autostash(const char *path);\n \n #endif\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 661928d427..c3165896cc 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -734,143 +734,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)\n \t}\n }\n \n-#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n-\n-#define RESET_HEAD_DETACH (1<<0)\n-#define RESET_HEAD_HARD (1<<1)\n-#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n-#define RESET_HEAD_REFS_ONLY (1<<3)\n-#define RESET_ORIG_HEAD (1<<4)\n-\n-static int reset_head(struct object_id *oid, const char *action,\n-\t\t      const char *switch_to_branch, unsigned flags,\n-\t\t      const char *reflog_orig_head, const char *reflog_head)\n-{\n-\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n-\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n-\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n-\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n-\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n-\tstruct object_id head_oid;\n-\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n-\tstruct lock_file lock = LOCK_INIT;\n-\tstruct unpack_trees_options unpack_tree_opts;\n-\tstruct tree *tree;\n-\tconst char *reflog_action;\n-\tstruct strbuf msg = STRBUF_INIT;\n-\tsize_t prefix_len;\n-\tstruct object_id *orig = NULL, oid_orig,\n-\t\t*old_orig = NULL, oid_old_orig;\n-\tint ret = 0, nr = 0;\n-\n-\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n-\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n-\n-\tif (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {\n-\t\tret = -1;\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n-\t\tret = error(_(\"could not determine HEAD revision\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!oid)\n-\t\toid = &head_oid;\n-\n-\tif (refs_only)\n-\t\tgoto reset_head_refs;\n-\n-\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n-\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n-\tunpack_tree_opts.head_idx = 1;\n-\tunpack_tree_opts.src_index = the_repository->index;\n-\tunpack_tree_opts.dst_index = the_repository->index;\n-\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n-\tunpack_tree_opts.update = 1;\n-\tunpack_tree_opts.merge = 1;\n-\tif (!detach_head)\n-\t\tunpack_tree_opts.reset = 1;\n-\n-\tif (repo_read_index_unmerged(the_repository) < 0) {\n-\t\tret = error(_(\"could not read index\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {\n-\t\tret = error(_(\"failed to find tree of %s\"),\n-\t\t\t    oid_to_hex(&head_oid));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {\n-\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n-\t\tret = -1;\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\ttree = parse_tree_indirect(oid);\n-\tprime_cache_tree(the_repository, the_repository->index, tree);\n-\n-\tif (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {\n-\t\tret = error(_(\"could not write index\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-reset_head_refs:\n-\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n-\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : \"rebase\");\n-\tprefix_len = msg.len;\n-\n-\tif (update_orig_head) {\n-\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n-\t\t\told_orig = &oid_old_orig;\n-\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n-\t\t\torig = &oid_orig;\n-\t\t\tif (!reflog_orig_head) {\n-\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n-\t\t\t\treflog_orig_head = msg.buf;\n-\t\t\t}\n-\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n-\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n-\t\t} else if (old_orig)\n-\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n-\t}\n-\n-\tif (!reflog_head) {\n-\t\tstrbuf_setlen(&msg, prefix_len);\n-\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n-\t\treflog_head = msg.buf;\n-\t}\n-\tif (!switch_to_branch)\n-\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n-\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n-\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n-\telse {\n-\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n-\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n-\t\tif (!ret)\n-\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n-\t\t\t\t\t    reflog_head);\n-\t}\n-\tif (run_hook)\n-\t\trun_hook_le(NULL, \"post-checkout\",\n-\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n-\t\t\t    oid_to_hex(oid), \"1\", NULL);\n-\n-leave_reset_head:\n-\tstrbuf_release(&msg);\n-\trollback_lock_file(&lock);\n-\twhile (nr)\n-\t\tfree((void *)desc[--nr].buffer);\n-\treturn ret;\n-}\n-\n static int move_to_original_branch(struct rebase_options *opts)\n {\n \tstruct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;\n-- \n2.23.0.897.g0a19638b1e\n\n"},{"id":"384194","messageId":"0518b0b187245e807987457cd558a1ab38476c5c.1571246693.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1571246693.git.liu.denton@gmail.com","subject":"[RFC PATCH 5/7] autostash: extract perform_autostash() from rebase","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-16T17:26:30Z","receivedAt":"2019-10-16T17:26:36Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Continue the process of lib-ifying the autostash code. In a future\ncommit, this will be used to implement `--autostash` in other builtins.\n\nThis patch is best viewed with `--color-moved` and\n`--color-moved-ws=allow-indentation-change`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n autostash.c      | 46 ++++++++++++++++++++++++++++++++++++++++++++++\n autostash.h      |  1 +\n builtin/rebase.c | 44 +-------------------------------------------\n 3 files changed, 48 insertions(+), 43 deletions(-)\n\ndiff --git a/autostash.c b/autostash.c\nindex eb58e0c8a4..722cf78b12 100644\n--- a/autostash.c\n+++ b/autostash.c\n@@ -12,6 +12,7 @@\n #include \"tree-walk.h\"\n #include \"tree.h\"\n #include \"unpack-trees.h\"\n+#include \"wt-status.h\"\n \n int read_one(const char *path, struct strbuf *buf)\n {\n@@ -150,6 +151,51 @@ int reset_head(struct object_id *oid, const char *action,\n \treturn ret;\n }\n \n+void perform_autostash(const char *path)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct lock_file lock_file = LOCK_INIT;\n+\tint fd;\n+\n+\tfd = hold_locked_index(&lock_file, 0);\n+\trefresh_cache(REFRESH_QUIET);\n+\tif (0 <= fd)\n+\t\trepo_update_index_if_able(the_repository, &lock_file);\n+\trollback_lock_file(&lock_file);\n+\n+\tif (has_unstaged_changes(the_repository, 1) ||\n+\t    has_uncommitted_changes(the_repository, 1)) {\n+\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n+\t\tstruct object_id oid;\n+\n+\t\targv_array_pushl(&stash.args,\n+\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n+\t\tstash.git_cmd = 1;\n+\t\tstash.no_stdin = 1;\n+\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n+\t\t\tdie(_(\"Cannot autostash\"));\n+\t\tstrbuf_trim_trailing_newline(&buf);\n+\t\tif (get_oid(buf.buf, &oid))\n+\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n+\t\t\t    buf.buf);\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n+\n+\t\tif (safe_create_leading_directories_const(path))\n+\t\t\tdie(_(\"Could not create directory for '%s'\"),\n+\t\t\t    path);\n+\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n+\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n+\t\tif (reset_head(NULL, \"reset --hard\",\n+\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)\n+\t\t\tdie(_(\"could not reset --hard\"));\n+\n+\t\tif (discard_index(the_repository->index) < 0 ||\n+\t\t\trepo_read_index(the_repository) < 0)\n+\t\t\tdie(_(\"could not read index\"));\n+\t}\n+}\n+\n int apply_autostash(const char *path)\n {\n \tstruct strbuf autostash = STRBUF_INIT;\ndiff --git a/autostash.h b/autostash.h\nindex 1406638166..e08ccb9881 100644\n--- a/autostash.h\n+++ b/autostash.h\n@@ -18,6 +18,7 @@ int reset_head(struct object_id *oid, const char *action,\n \t       const char *switch_to_branch, unsigned flags,\n \t       const char *reflog_orig_head, const char *reflog_head);\n \n+void perform_autostash(const char *path);\n int apply_autostash(const char *path);\n \n #endif\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex c3165896cc..c4decdfb5b 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1797,49 +1797,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read index\"));\n \n \tif (options.autostash) {\n-\t\tstruct lock_file lock_file = LOCK_INIT;\n-\t\tint fd;\n-\n-\t\tfd = hold_locked_index(&lock_file, 0);\n-\t\trefresh_cache(REFRESH_QUIET);\n-\t\tif (0 <= fd)\n-\t\t\trepo_update_index_if_able(the_repository, &lock_file);\n-\t\trollback_lock_file(&lock_file);\n-\n-\t\tif (has_unstaged_changes(the_repository, 1) ||\n-\t\t    has_uncommitted_changes(the_repository, 1)) {\n-\t\t\tconst char *autostash =\n-\t\t\t\tstate_dir_path(\"autostash\", &options);\n-\t\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n-\t\t\tstruct object_id oid;\n-\n-\t\t\targv_array_pushl(&stash.args,\n-\t\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n-\t\t\tstash.git_cmd = 1;\n-\t\t\tstash.no_stdin = 1;\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n-\t\t\t\tdie(_(\"Cannot autostash\"));\n-\t\t\tstrbuf_trim_trailing_newline(&buf);\n-\t\t\tif (get_oid(buf.buf, &oid))\n-\t\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n-\t\t\t\t    buf.buf);\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n-\n-\t\t\tif (safe_create_leading_directories_const(autostash))\n-\t\t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t\t    options.state_dir);\n-\t\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n-\t\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\t\tif (reset_head(NULL, \"reset --hard\",\n-\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)\n-\t\t\t\tdie(_(\"could not reset --hard\"));\n-\n-\t\t\tif (discard_index(the_repository->index) < 0 ||\n-\t\t\t\trepo_read_index(the_repository) < 0)\n-\t\t\t\tdie(_(\"could not read index\"));\n-\t\t}\n+\t\tperform_autostash(state_dir_path(\"autostash\", &options));\n \t}\n \n \tif (require_clean_work_tree(the_repository, \"rebase\",\n-- \n2.23.0.897.g0a19638b1e\n\n"},{"id":"384195","messageId":"73607c2e00a87b05da0e374734eb5c4d7d377d84.1571246693.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1571246693.git.liu.denton@gmail.com","subject":"[RFC PATCH 6/7] autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-16T17:26:32Z","receivedAt":"2019-10-16T17:26:38Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Since autostash.c was recently introduced, we should avoid\nUSE_THE_INDEX_COMPATIBILITY_MACROS since we are trying to move away from\nthis in the rest of the codebase. Rewrite the autostash code to not need\nit and remove its definition.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n autostash.c | 8 +++-----\n 1 file changed, 3 insertions(+), 5 deletions(-)\n\ndiff --git a/autostash.c b/autostash.c\nindex 722cf78b12..0a1f00d2e5 100644\n--- a/autostash.c\n+++ b/autostash.c\n@@ -1,5 +1,3 @@\n-#define USE_THE_INDEX_COMPATIBILITY_MACROS\n-\n #include \"git-compat-util.h\"\n #include \"autostash.h\"\n #include \"cache-tree.h\"\n@@ -46,7 +44,7 @@ int reset_head(struct object_id *oid, const char *action,\n \tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n \t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n \n-\tif (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {\n+\tif (!refs_only && repo_hold_locked_index(the_repository, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n \t\tret = -1;\n \t\tgoto leave_reset_head;\n \t}\n@@ -157,8 +155,8 @@ void perform_autostash(const char *path)\n \tstruct lock_file lock_file = LOCK_INIT;\n \tint fd;\n \n-\tfd = hold_locked_index(&lock_file, 0);\n-\trefresh_cache(REFRESH_QUIET);\n+\tfd = repo_hold_locked_index(the_repository, &lock_file, 0);\n+\trefresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);\n \tif (0 <= fd)\n \t\trepo_update_index_if_able(the_repository, &lock_file);\n \trollback_lock_file(&lock_file);\n-- \n2.23.0.897.g0a19638b1e\n\n"},{"id":"384196","messageId":"f43c10e4818c91a8c6e9d5e0ce2a04710db3e300.1571246693.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1571246693.git.liu.denton@gmail.com","subject":"[RFC PATCH 7/7] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-16T17:26:34Z","receivedAt":"2019-10-16T17:26:41Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In rebase, one can pass the `--autostash` option to cause the worktree\nto be automatically stashed before continuing with the rebase. This\noption is missing in merge, however.\n\nImplement the `--autostash` option and corresponding `merge.autoStash`\noption in merge which stashes before merging and then pops after.\n\nReported-by: Alban Gruin <alban.gruin@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/merge.c | 13 +++++++++++++\n builtin/pull.c  |  9 +++++----\n t/t5520-pull.sh |  8 --------\n 3 files changed, 18 insertions(+), 12 deletions(-)\n\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 062e911441..d1a5eaad0d 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -40,6 +40,7 @@\n #include \"branch.h\"\n #include \"commit-reach.h\"\n #include \"wt-status.h\"\n+#include \"autostash.h\"\n \n #define DEFAULT_TWOHEAD (1<<0)\n #define DEFAULT_OCTOPUS (1<<1)\n@@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {\n \tNULL\n };\n \n+static GIT_PATH_FUNC(merge_autostash, \"MERGE_AUTOSTASH\")\n+\n static int show_diffstat = 1, shortlog_len = -1, squash;\n static int option_commit = -1;\n static int option_edit = -1;\n@@ -81,6 +84,7 @@ static int show_progress = -1;\n static int default_to_upstream = 1;\n static int signoff;\n static const char *sign_commit;\n+static int autostash;\n static int no_verify;\n \n static struct strategy all_strategy[] = {\n@@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {\n \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n+\tOPT_BOOL(0, \"autostash\", &autostash,\n+\t      N_(\"automatically stash/stash pop before and after\")),\n \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n@@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,\n \t\tstrbuf_addf(&reflog_message, \"%s: %s\",\n \t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n \t}\n+\tapply_autostash(merge_autostash());\n \tif (squash) {\n \t\tsquash_message(head_commit, remoteheads);\n \t} else {\n@@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t} else if (!strcmp(k, \"commit.gpgsign\")) {\n \t\tsign_commit = git_config_bool(k, v) ? \"\" : NULL;\n \t\treturn 0;\n+\t} else if (!strcmp(k, \"merge.autostash\")) {\n+\t\tautostash = git_config_bool(k, v);\n+\t\treturn 0;\n \t}\n \n \tstatus = fmt_merge_msg_config(k, v, cb);\n@@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\tfor (j = common; j; j = j->next)\n \t\t\tcommit_list_insert(j->item, &reversed);\n \n+\t\tif (autostash)\n+\t\t\tperform_autostash(merge_autostash());\n \t\thold_locked_index(&lock, LOCK_DIE_ON_ERROR);\n \t\tclean = merge_recursive(&o, head,\n \t\t\t\tremoteheads->item, reversed, &result);\n@@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \n \t\t/* Invoke 'git reset --merge' */\n \t\tret = cmd_reset(nargc, nargv, prefix);\n+\t\tapply_autostash(merge_autostash());\n \t\tgoto done;\n \t}\n \ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex d25ff13a60..ee186781ae 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -183,7 +183,7 @@ static struct option pull_options[] = {\n \t\tN_(\"verify that the named commit has a valid GPG signature\"),\n \t\tPARSE_OPT_NOARG),\n \tOPT_BOOL(0, \"autostash\", &opt_autostash,\n-\t\tN_(\"automatically stash/stash pop before and after rebase\")),\n+\t\tN_(\"automatically stash/stash pop before and after\")),\n \tOPT_PASSTHRU_ARGV('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n \t\tN_(\"merge strategy to use\"),\n \t\t0),\n@@ -671,6 +671,10 @@ static int run_merge(void)\n \targv_array_pushv(&args, opt_strategy_opts.argv);\n \tif (opt_gpg_sign)\n \t\targv_array_push(&args, opt_gpg_sign);\n+\tif (opt_autostash == 0)\n+\t\targv_array_push(&args, \"--no-autostash\");\n+\telse if (opt_autostash == 1)\n+\t\targv_array_push(&args, \"--autostash\");\n \tif (opt_allow_unrelated_histories > 0)\n \t\targv_array_push(&args, \"--allow-unrelated-histories\");\n \n@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (get_oid(\"HEAD\", &orig_head))\n \t\toidclr(&orig_head);\n \n-\tif (!opt_rebase && opt_autostash != -1)\n-\t\tdie(_(\"--[no-]autostash option is only valid with --rebase.\"));\n-\n \tautostash = config_autostash;\n \tif (opt_rebase) {\n \t\tif (opt_autostash != -1)\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex cf4cc32fd0..75f162495a 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '\n \ttest_pull_autostash_fail --rebase --no-autostash\n '\n \n-for i in --autostash --no-autostash\n-do\n-\ttest_expect_success \"pull $i (without --rebase) is illegal\" '\n-\t\ttest_must_fail git pull $i . copy 2>err &&\n-\t\ttest_i18ngrep \"only valid with --rebase\" err\n-\t'\n-done\n-\n test_expect_success 'pull.rebase' '\n \tgit reset --hard before-rebase &&\n \ttest_config pull.rebase true &&\n-- \n2.23.0.897.g0a19638b1e\n\n"},{"id":"384282","messageId":"xmqqimon6yar.fsf@gitster-ct.c.googlers.com","threadId":"52048","inReplyTo":"02a16f9bdf740841d9a4be765e72b9fa5ae5d75c.1571246693.git.liu.denton@gmail.com","subject":"Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-10-17T18:07:24Z","receivedAt":"2019-10-17T18:07:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> There are many += lists in the Makefile and, over time, they have gotten\n> slightly out of order, alphabetically. Alphabetically sort all += lists\n> to bring them back in order.\n> ...\n\nHmm.  I like the general thrust, but ...\n\n>  LIB_OBJS += combine-diff.o\n> -LIB_OBJS += commit.o\n>  LIB_OBJS += commit-graph.o\n>  LIB_OBJS += commit-reach.o\n> +LIB_OBJS += commit.o\n\n... I do not particularly see this change (there may be similar\nones) desirable.  I'd find it it be much more natural to sort\n\"commit-anything\" after \"commit\", and that is true with or without\nthe common extension \".o\" added to these entries.\n\nIn short, flipping these entries because '.' sorts later than '-' is\nmaking the result look \"less sorted\", at least to me.\n\nThanks.\n"},{"id":"384367","messageId":"49b1ed2e-b1b9-b593-2657-e59f61925e20@gmail.com","threadId":"52048","inReplyTo":"bbd46ad8d27ab547c8efa6b55c1777becbab7302.1571246693.git.liu.denton@gmail.com","subject":"Re: [RFC PATCH 2/7] autostash: extract read_one() from rebase","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-10-18T09:04:46Z","receivedAt":"2019-10-18T09:04:53Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 16/10/2019 18:26, Denton Liu wrote:\n> Begin the process of lib-ifying the autostash code. In a future commit,\n> this will be used to implement `--autostash` in other builtins.\n> \n> This patch is best viewed with `--color-moved` and\n> `--color-moved-ws=allow-indentation-change`.\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   autostash.c      | 12 ++++++++++++\n>   autostash.h      |  9 +++++++++\n>   builtin/rebase.c | 10 +---------\n>   3 files changed, 22 insertions(+), 9 deletions(-)\n>   create mode 100644 autostash.c\n>   create mode 100644 autostash.h\n> \n> diff --git a/autostash.c b/autostash.c\n> new file mode 100644\n> index 0000000000..a6898e0fda\n> --- /dev/null\n> +++ b/autostash.c\n> @@ -0,0 +1,12 @@\n> +#include \"git-compat-util.h\"\n> +#include \"autostash.h\"\n> +#include \"gettext.h\"\n> +#include \"strbuf.h\"\n> +\n> +int read_one(const char *path, struct strbuf *buf)\n> +{\n> +\tif (strbuf_read_file(buf, path, 0) < 0)\n> +\t\treturn error_errno(_(\"could not read '%s'\"), path);\n> +\tstrbuf_trim_trailing_newline(buf);\n> +\treturn 0;\n> +}\n\nThis looks like it's doing a similar job to read_oneliner() in \nsequencer.c, is it possible to make that public and use it instead? \n(There may be a difference if the file is missing but that function \nalready takes a flag so it could probably be modified easily enough.)\n\nBest Wishes\n\nPhillip\n\n\n> diff --git a/autostash.h b/autostash.h\n> new file mode 100644\n> index 0000000000..4a8f504f12\n> --- /dev/null\n> +++ b/autostash.h\n> @@ -0,0 +1,9 @@\n> +#ifndef AUTOSTASH_H\n> +#define AUTOSTASH_H\n> +\n> +#include \"strbuf.h\"\n> +\n> +/* Read one file, then strip line endings */\n> +int read_one(const char *path, struct strbuf *buf);\n> +\n> +#endif\n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index 4a20582e72..9fd7de6b2f 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -27,6 +27,7 @@\n>   #include \"branch.h\"\n>   #include \"sequencer.h\"\n>   #include \"rebase-interactive.h\"\n> +#include \"autostash.h\"\n>   \n>   static char const * const builtin_rebase_usage[] = {\n>   \tN_(\"git rebase [-i] [options] [--exec <cmd>] \"\n> @@ -561,15 +562,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o\n>   \treturn path.buf;\n>   }\n>   \n> -/* Read one file, then strip line endings */\n> -static int read_one(const char *path, struct strbuf *buf)\n> -{\n> -\tif (strbuf_read_file(buf, path, 0) < 0)\n> -\t\treturn error_errno(_(\"could not read '%s'\"), path);\n> -\tstrbuf_trim_trailing_newline(buf);\n> -\treturn 0;\n> -}\n> -\n>   /* Initialize the rebase options from the state directory. */\n>   static int read_basic_state(struct rebase_options *opts)\n>   {\n> \n"},{"id":"384368","messageId":"ab064a01-eed3-f90b-df47-1f372a063e30@gmail.com","threadId":"52048","inReplyTo":"5e0dcd7cbeb3481beef04f1d145474739f8bdea3.1571246693.git.liu.denton@gmail.com","subject":"Re: [RFC PATCH 4/7] autostash: extract reset_head() from rebase","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-10-18T09:37:01Z","receivedAt":"2019-10-18T09:37:07Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nIt's great to see this being libified, I've had it in mind to do this so \nwe can avoid forking 'git checkout' in sequencer.c\n\nOn 16/10/2019 18:26, Denton Liu wrote:\n> Begin the process of lib-ifying the autostash code. In a future commit,\n> \n> This patch is best viewed with `--color-moved` and\n> `--color-moved-ws=allow-indentation-change`.\n> this will be used to implement `--autostash` in other builtins.\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   autostash.c      | 137 +++++++++++++++++++++++++++++++++++++++++++++++\n>   autostash.h      |  12 +++++\n>   builtin/rebase.c | 137 -----------------------------------------------\n>   3 files changed, 149 insertions(+), 137 deletions(-)\n> \n> diff --git a/autostash.c b/autostash.c\n> index 62ec7a7c80..eb58e0c8a4 100644\n> --- a/autostash.c\n> +++ b/autostash.c\n> @@ -1,9 +1,17 @@\n> +#define USE_THE_INDEX_COMPATIBILITY_MACROS\n\nIt might be nicer to have a preparatory step that fixes this by adding a \n'struct repository *r' argument to the function in builtin/rebase.c \nbefore moving the function. You could also do the same for next patch \nand then move both functions together.\n\nBest Wishes\n\nPhillip\n\n> +\n>   #include \"git-compat-util.h\"\n>   #include \"autostash.h\"\n> +#include \"cache-tree.h\"\n>   #include \"dir.h\"\n>   #include \"gettext.h\"\n> +#include \"lockfile.h\"\n> +#include \"refs.h\"\n>   #include \"run-command.h\"\n>   #include \"strbuf.h\"\n> +#include \"tree-walk.h\"\n> +#include \"tree.h\"\n> +#include \"unpack-trees.h\"\n>   \n>   int read_one(const char *path, struct strbuf *buf)\n>   {\n> @@ -13,6 +21,135 @@ int read_one(const char *path, struct strbuf *buf)\n>   \treturn 0;\n>   }\n>   \n> +int reset_head(struct object_id *oid, const char *action,\n> +\t       const char *switch_to_branch, unsigned flags,\n> +\t       const char *reflog_orig_head, const char *reflog_head)\n> +{\n> +\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n> +\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n> +\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n> +\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n> +\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n> +\tstruct object_id head_oid;\n> +\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n> +\tstruct lock_file lock = LOCK_INIT;\n> +\tstruct unpack_trees_options unpack_tree_opts;\n> +\tstruct tree *tree;\n> +\tconst char *reflog_action;\n> +\tstruct strbuf msg = STRBUF_INIT;\n> +\tsize_t prefix_len;\n> +\tstruct object_id *orig = NULL, oid_orig,\n> +\t\t*old_orig = NULL, oid_old_orig;\n> +\tint ret = 0, nr = 0;\n> +\n> +\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n> +\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n> +\n> +\tif (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {\n> +\t\tret = -1;\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n> +\t\tret = error(_(\"could not determine HEAD revision\"));\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +\tif (!oid)\n> +\t\toid = &head_oid;\n> +\n> +\tif (refs_only)\n> +\t\tgoto reset_head_refs;\n> +\n> +\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n> +\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n> +\tunpack_tree_opts.head_idx = 1;\n> +\tunpack_tree_opts.src_index = the_repository->index;\n> +\tunpack_tree_opts.dst_index = the_repository->index;\n> +\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n> +\tunpack_tree_opts.update = 1;\n> +\tunpack_tree_opts.merge = 1;\n> +\tif (!detach_head)\n> +\t\tunpack_tree_opts.reset = 1;\n> +\n> +\tif (repo_read_index_unmerged(the_repository) < 0) {\n> +\t\tret = error(_(\"could not read index\"));\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +\tif (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {\n> +\t\tret = error(_(\"failed to find tree of %s\"),\n> +\t\t\t    oid_to_hex(&head_oid));\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +\tif (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {\n> +\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n> +\t\tret = -1;\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +\ttree = parse_tree_indirect(oid);\n> +\tprime_cache_tree(the_repository, the_repository->index, tree);\n> +\n> +\tif (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {\n> +\t\tret = error(_(\"could not write index\"));\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +reset_head_refs:\n> +\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n> +\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : \"rebase\");\n> +\tprefix_len = msg.len;\n> +\n> +\tif (update_orig_head) {\n> +\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n> +\t\t\told_orig = &oid_old_orig;\n> +\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n> +\t\t\torig = &oid_orig;\n> +\t\t\tif (!reflog_orig_head) {\n> +\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n> +\t\t\t\treflog_orig_head = msg.buf;\n> +\t\t\t}\n> +\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n> +\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n> +\t\t} else if (old_orig)\n> +\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n> +\t}\n> +\n> +\tif (!reflog_head) {\n> +\t\tstrbuf_setlen(&msg, prefix_len);\n> +\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n> +\t\treflog_head = msg.buf;\n> +\t}\n> +\tif (!switch_to_branch)\n> +\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n> +\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n> +\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n> +\telse {\n> +\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n> +\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n> +\t\tif (!ret)\n> +\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n> +\t\t\t\t\t    reflog_head);\n> +\t}\n> +\tif (run_hook)\n> +\t\trun_hook_le(NULL, \"post-checkout\",\n> +\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n> +\t\t\t    oid_to_hex(oid), \"1\", NULL);\n> +\n> +leave_reset_head:\n> +\tstrbuf_release(&msg);\n> +\trollback_lock_file(&lock);\n> +\twhile (nr)\n> +\t\tfree((void *)desc[--nr].buffer);\n> +\treturn ret;\n> +}\n> +\n>   int apply_autostash(const char *path)\n>   {\n>   \tstruct strbuf autostash = STRBUF_INIT;\n> diff --git a/autostash.h b/autostash.h\n> index 5f4e4bd22c..1406638166 100644\n> --- a/autostash.h\n> +++ b/autostash.h\n> @@ -6,6 +6,18 @@\n>   /* Read one file, then strip line endings */\n>   int read_one(const char *path, struct strbuf *buf);\n>   \n> +#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n> +\n> +#define RESET_HEAD_DETACH (1<<0)\n> +#define RESET_HEAD_HARD (1<<1)\n> +#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n> +#define RESET_HEAD_REFS_ONLY (1<<3)\n> +#define RESET_ORIG_HEAD (1<<4)\n> +\n> +int reset_head(struct object_id *oid, const char *action,\n> +\t       const char *switch_to_branch, unsigned flags,\n> +\t       const char *reflog_orig_head, const char *reflog_head);\n> +\n>   int apply_autostash(const char *path);\n>   \n>   #endif\n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index 661928d427..c3165896cc 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -734,143 +734,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)\n>   \t}\n>   }\n>   \n> -#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n> -\n> -#define RESET_HEAD_DETACH (1<<0)\n> -#define RESET_HEAD_HARD (1<<1)\n> -#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n> -#define RESET_HEAD_REFS_ONLY (1<<3)\n> -#define RESET_ORIG_HEAD (1<<4)\n> -\n> -static int reset_head(struct object_id *oid, const char *action,\n> -\t\t      const char *switch_to_branch, unsigned flags,\n> -\t\t      const char *reflog_orig_head, const char *reflog_head)\n> -{\n> -\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n> -\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n> -\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n> -\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n> -\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n> -\tstruct object_id head_oid;\n> -\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n> -\tstruct lock_file lock = LOCK_INIT;\n> -\tstruct unpack_trees_options unpack_tree_opts;\n> -\tstruct tree *tree;\n> -\tconst char *reflog_action;\n> -\tstruct strbuf msg = STRBUF_INIT;\n> -\tsize_t prefix_len;\n> -\tstruct object_id *orig = NULL, oid_orig,\n> -\t\t*old_orig = NULL, oid_old_orig;\n> -\tint ret = 0, nr = 0;\n> -\n> -\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n> -\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n> -\n> -\tif (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {\n> -\t\tret = -1;\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n> -\t\tret = error(_(\"could not determine HEAD revision\"));\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -\tif (!oid)\n> -\t\toid = &head_oid;\n> -\n> -\tif (refs_only)\n> -\t\tgoto reset_head_refs;\n> -\n> -\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n> -\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n> -\tunpack_tree_opts.head_idx = 1;\n> -\tunpack_tree_opts.src_index = the_repository->index;\n> -\tunpack_tree_opts.dst_index = the_repository->index;\n> -\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n> -\tunpack_tree_opts.update = 1;\n> -\tunpack_tree_opts.merge = 1;\n> -\tif (!detach_head)\n> -\t\tunpack_tree_opts.reset = 1;\n> -\n> -\tif (repo_read_index_unmerged(the_repository) < 0) {\n> -\t\tret = error(_(\"could not read index\"));\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -\tif (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {\n> -\t\tret = error(_(\"failed to find tree of %s\"),\n> -\t\t\t    oid_to_hex(&head_oid));\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -\tif (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {\n> -\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n> -\t\tret = -1;\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -\ttree = parse_tree_indirect(oid);\n> -\tprime_cache_tree(the_repository, the_repository->index, tree);\n> -\n> -\tif (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {\n> -\t\tret = error(_(\"could not write index\"));\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -reset_head_refs:\n> -\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n> -\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : \"rebase\");\n> -\tprefix_len = msg.len;\n> -\n> -\tif (update_orig_head) {\n> -\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n> -\t\t\told_orig = &oid_old_orig;\n> -\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n> -\t\t\torig = &oid_orig;\n> -\t\t\tif (!reflog_orig_head) {\n> -\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n> -\t\t\t\treflog_orig_head = msg.buf;\n> -\t\t\t}\n> -\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n> -\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n> -\t\t} else if (old_orig)\n> -\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n> -\t}\n> -\n> -\tif (!reflog_head) {\n> -\t\tstrbuf_setlen(&msg, prefix_len);\n> -\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n> -\t\treflog_head = msg.buf;\n> -\t}\n> -\tif (!switch_to_branch)\n> -\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n> -\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n> -\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n> -\telse {\n> -\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n> -\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n> -\t\tif (!ret)\n> -\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n> -\t\t\t\t\t    reflog_head);\n> -\t}\n> -\tif (run_hook)\n> -\t\trun_hook_le(NULL, \"post-checkout\",\n> -\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n> -\t\t\t    oid_to_hex(oid), \"1\", NULL);\n> -\n> -leave_reset_head:\n> -\tstrbuf_release(&msg);\n> -\trollback_lock_file(&lock);\n> -\twhile (nr)\n> -\t\tfree((void *)desc[--nr].buffer);\n> -\treturn ret;\n> -}\n> -\n>   static int move_to_original_branch(struct rebase_options *opts)\n>   {\n>   \tstruct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;\n> \n"},{"id":"384369","messageId":"836db3ce-2c2a-1d4f-a131-6005fc961f45@gmail.com","threadId":"52048","inReplyTo":"73607c2e00a87b05da0e374734eb5c4d7d377d84.1571246693.git.liu.denton@gmail.com","subject":"Re: [RFC PATCH 6/7] autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-10-18T09:41:43Z","receivedAt":"2019-10-18T09:41:48Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 16/10/2019 18:26, Denton Liu wrote:\n> Since autostash.c was recently introduced, we should avoid\n> USE_THE_INDEX_COMPATIBILITY_MACROS since we are trying to move away from\n> this in the rest of the codebase. Rewrite the autostash code to not need\n> it and remove its definition.\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   autostash.c | 8 +++-----\n>   1 file changed, 3 insertions(+), 5 deletions(-)\n> \n> diff --git a/autostash.c b/autostash.c\n> index 722cf78b12..0a1f00d2e5 100644\n> --- a/autostash.c\n> +++ b/autostash.c\n> @@ -1,5 +1,3 @@\n> -#define USE_THE_INDEX_COMPATIBILITY_MACROS\n> -\n>   #include \"git-compat-util.h\"\n>   #include \"autostash.h\"\n>   #include \"cache-tree.h\"\n> @@ -46,7 +44,7 @@ int reset_head(struct object_id *oid, const char *action,\n>   \tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n>   \t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n>   \n> -\tif (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {\n> +\tif (!refs_only && repo_hold_locked_index(the_repository, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n\nAs I understand it the reason for moving away from hold_locked_index() \nis that it relies on a global variable. While replacing it with an \nexplicit reference the the_repository removes the implicit dependency on \nglobal state, it does not move us away from depending on a global \nvariable. I think it would be nicer to change these functions to take a \n`struct repository*` before moving them.\n\nBest Wishes\n\nPhillip\n>   \t\tret = -1;\n>   \t\tgoto leave_reset_head;\n>   \t}\n> @@ -157,8 +155,8 @@ void perform_autostash(const char *path)\n>   \tstruct lock_file lock_file = LOCK_INIT;\n>   \tint fd;\n>   \n> -\tfd = hold_locked_index(&lock_file, 0);\n> -\trefresh_cache(REFRESH_QUIET);\n> +\tfd = repo_hold_locked_index(the_repository, &lock_file, 0);\n> +\trefresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);\n>   \tif (0 <= fd)\n>   \t\trepo_update_index_if_able(the_repository, &lock_file);\n>   \trollback_lock_file(&lock_file);\n> \n"},{"id":"384370","messageId":"4a47c27f-2c6a-0bd6-eac8-533700087af9@gmail.com","threadId":"52048","inReplyTo":"f43c10e4818c91a8c6e9d5e0ce2a04710db3e300.1571246693.git.liu.denton@gmail.com","subject":"Re: [RFC PATCH 7/7] merge: teach --autostash option","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-10-18T09:46:37Z","receivedAt":"2019-10-18T09:46:47Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 16/10/2019 18:26, Denton Liu wrote:\n> In rebase, one can pass the `--autostash` option to cause the worktree\n> to be automatically stashed before continuing with the rebase. This\n> option is missing in merge, however.\n\nIt might be helpful to say why this option is useful. I can see one \nmight want to stash before doing `git pull` in the same way as one might \nbefore a rebase but what's the motivation for doing it before a normal \nmerge?\n\n> \n> Implement the `--autostash` option and corresponding `merge.autoStash`\n> option in merge which stashes before merging and then pops after.\n> \n> Reported-by: Alban Gruin <alban.gruin@gmail.com>\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   builtin/merge.c | 13 +++++++++++++\n>   builtin/pull.c  |  9 +++++----\n>   t/t5520-pull.sh |  8 --------\n>   3 files changed, 18 insertions(+), 12 deletions(-)\n> \n> diff --git a/builtin/merge.c b/builtin/merge.c\n> index 062e911441..d1a5eaad0d 100644\n> --- a/builtin/merge.c\n> +++ b/builtin/merge.c\n> @@ -40,6 +40,7 @@\n>   #include \"branch.h\"\n>   #include \"commit-reach.h\"\n>   #include \"wt-status.h\"\n> +#include \"autostash.h\"\n>   \n>   #define DEFAULT_TWOHEAD (1<<0)\n>   #define DEFAULT_OCTOPUS (1<<1)\n> @@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {\n>   \tNULL\n>   };\n>   \n> +static GIT_PATH_FUNC(merge_autostash, \"MERGE_AUTOSTASH\")\n> +\n>   static int show_diffstat = 1, shortlog_len = -1, squash;\n>   static int option_commit = -1;\n>   static int option_edit = -1;\n> @@ -81,6 +84,7 @@ static int show_progress = -1;\n>   static int default_to_upstream = 1;\n>   static int signoff;\n>   static const char *sign_commit;\n> +static int autostash;\n>   static int no_verify;\n>   \n>   static struct strategy all_strategy[] = {\n> @@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {\n>   \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n>   \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n>   \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n> +\tOPT_BOOL(0, \"autostash\", &autostash,\n> +\t      N_(\"automatically stash/stash pop before and after\")),\n>   \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n>   \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n>   \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n> @@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,\n>   \t\tstrbuf_addf(&reflog_message, \"%s: %s\",\n>   \t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n>   \t}\n> +\tapply_autostash(merge_autostash());\n>   \tif (squash) {\n>   \t\tsquash_message(head_commit, remoteheads);\n>   \t} else {\n> @@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n>   \t} else if (!strcmp(k, \"commit.gpgsign\")) {\n>   \t\tsign_commit = git_config_bool(k, v) ? \"\" : NULL;\n>   \t\treturn 0;\n> +\t} else if (!strcmp(k, \"merge.autostash\")) {\n> +\t\tautostash = git_config_bool(k, v);\n> +\t\treturn 0;\n>   \t}\n>   \n>   \tstatus = fmt_merge_msg_config(k, v, cb);\n> @@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n>   \t\tfor (j = common; j; j = j->next)\n>   \t\t\tcommit_list_insert(j->item, &reversed);\n>   \n> +\t\tif (autostash)\n> +\t\t\tperform_autostash(merge_autostash());\n>   \t\thold_locked_index(&lock, LOCK_DIE_ON_ERROR);\n>   \t\tclean = merge_recursive(&o, head,\n>   \t\t\t\tremoteheads->item, reversed, &result);\n> @@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>   \n>   \t\t/* Invoke 'git reset --merge' */\n>   \t\tret = cmd_reset(nargc, nargv, prefix);\n> +\t\tapply_autostash(merge_autostash());\n>   \t\tgoto done;\n>   \t}\n>   \n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index d25ff13a60..ee186781ae 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -183,7 +183,7 @@ static struct option pull_options[] = {\n>   \t\tN_(\"verify that the named commit has a valid GPG signature\"),\n>   \t\tPARSE_OPT_NOARG),\n>   \tOPT_BOOL(0, \"autostash\", &opt_autostash,\n> -\t\tN_(\"automatically stash/stash pop before and after rebase\")),\n> +\t\tN_(\"automatically stash/stash pop before and after\")),\n\nI've not looked closely at the code in this patch but noticed this. I \nthink it would read better if it said\n\n     automatically stash/stash pop before and after pulling\n\nBest Wishes\n\nPhillip\n\n>   \tOPT_PASSTHRU_ARGV('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n>   \t\tN_(\"merge strategy to use\"),\n>   \t\t0),\n> @@ -671,6 +671,10 @@ static int run_merge(void)\n>   \targv_array_pushv(&args, opt_strategy_opts.argv);\n>   \tif (opt_gpg_sign)\n>   \t\targv_array_push(&args, opt_gpg_sign);\n> +\tif (opt_autostash == 0)\n> +\t\targv_array_push(&args, \"--no-autostash\");\n> +\telse if (opt_autostash == 1)\n> +\t\targv_array_push(&args, \"--autostash\");\n>   \tif (opt_allow_unrelated_histories > 0)\n>   \t\targv_array_push(&args, \"--allow-unrelated-histories\");\n>   \n> @@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n>   \tif (get_oid(\"HEAD\", &orig_head))\n>   \t\toidclr(&orig_head);\n>   \n> -\tif (!opt_rebase && opt_autostash != -1)\n> -\t\tdie(_(\"--[no-]autostash option is only valid with --rebase.\"));\n> -\n>   \tautostash = config_autostash;\n>   \tif (opt_rebase) {\n>   \t\tif (opt_autostash != -1)\n> diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\n> index cf4cc32fd0..75f162495a 100755\n> --- a/t/t5520-pull.sh\n> +++ b/t/t5520-pull.sh\n> @@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '\n>   \ttest_pull_autostash_fail --rebase --no-autostash\n>   '\n>   \n> -for i in --autostash --no-autostash\n> -do\n> -\ttest_expect_success \"pull $i (without --rebase) is illegal\" '\n> -\t\ttest_must_fail git pull $i . copy 2>err &&\n> -\t\ttest_i18ngrep \"only valid with --rebase\" err\n> -\t'\n> -done\n> -\n>   test_expect_success 'pull.rebase' '\n>   \tgit reset --hard before-rebase &&\n>   \ttest_config pull.rebase true &&\n> \n"},{"id":"384547","messageId":"nycvar.QRO.7.76.6.1910212043200.46@tvgsbejvaqbjf.bet","threadId":"52048","inReplyTo":"xmqqimon6yar.fsf@gitster-ct.c.googlers.com","subject":"Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-10-21T18:44:40Z","receivedAt":"2019-10-21T18:45:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 18 Oct 2019, Junio C Hamano wrote:\n\n> Denton Liu <liu.denton@gmail.com> writes:\n>\n> > There are many += lists in the Makefile and, over time, they have gotten\n> > slightly out of order, alphabetically. Alphabetically sort all += lists\n> > to bring them back in order.\n> > ...\n>\n> Hmm.  I like the general thrust, but ...\n>\n> >  LIB_OBJS += combine-diff.o\n> > -LIB_OBJS += commit.o\n> >  LIB_OBJS += commit-graph.o\n> >  LIB_OBJS += commit-reach.o\n> > +LIB_OBJS += commit.o\n>\n> ... I do not particularly see this change (there may be similar\n> ones) desirable.  I'd find it it be much more natural to sort\n> \"commit-anything\" after \"commit\", and that is true with or without\n> the common extension \".o\" added to these entries.\n>\n> In short, flipping these entries because '.' sorts later than '-' is\n> making the result look \"less sorted\", at least to me.\n\nThe problem with this argument is that it disagrees with ASCII, as `-`\nhas code 0x2d while `.` has code 0x2e, i.e. it is lexicographically\n_larger_.\n\nSo Denton's patch does the correct thing.\n\nCiao,\nDscho\n"},{"id":"384548","messageId":"nycvar.QRO.7.76.6.1910212045490.46@tvgsbejvaqbjf.bet","threadId":"52048","inReplyTo":"49b1ed2e-b1b9-b593-2657-e59f61925e20@gmail.com","subject":"Re: [RFC PATCH 2/7] autostash: extract read_one() from rebase","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-10-21T18:46:14Z","receivedAt":"2019-10-21T18:46:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 18 Oct 2019, Phillip Wood wrote:\n\n> Hi Denton\n>\n> On 16/10/2019 18:26, Denton Liu wrote:\n> > Begin the process of lib-ifying the autostash code. In a future commit,\n> > this will be used to implement `--autostash` in other builtins.\n> >\n> > This patch is best viewed with `--color-moved` and\n> > `--color-moved-ws=allow-indentation-change`.\n> >\n> > Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> > ---\n> >   autostash.c      | 12 ++++++++++++\n> >   autostash.h      |  9 +++++++++\n> >   builtin/rebase.c | 10 +---------\n> >   3 files changed, 22 insertions(+), 9 deletions(-)\n> >   create mode 100644 autostash.c\n> >   create mode 100644 autostash.h\n> >\n> > diff --git a/autostash.c b/autostash.c\n> > new file mode 100644\n> > index 0000000000..a6898e0fda\n> > --- /dev/null\n> > +++ b/autostash.c\n> > @@ -0,0 +1,12 @@\n> > +#include \"git-compat-util.h\"\n> > +#include \"autostash.h\"\n> > +#include \"gettext.h\"\n> > +#include \"strbuf.h\"\n> > +\n> > +int read_one(const char *path, struct strbuf *buf)\n> > +{\n> > +\tif (strbuf_read_file(buf, path, 0) < 0)\n> > +\t\treturn error_errno(_(\"could not read '%s'\"), path);\n> > +\tstrbuf_trim_trailing_newline(buf);\n> > +\treturn 0;\n> > +}\n>\n> This looks like it's doing a similar job to read_oneliner() in sequencer.c, is\n> it possible to make that public and use it instead? (There may be a difference\n> if the file is missing but that function already takes a flag so it could\n> probably be modified easily enough.)\n\nOh, I would _love_ to see those two functions reconciled.\n\nThanks,\nDscho\n\n>\n> Best Wishes\n>\n> Phillip\n>\n>\n> > diff --git a/autostash.h b/autostash.h\n> > new file mode 100644\n> > index 0000000000..4a8f504f12\n> > --- /dev/null\n> > +++ b/autostash.h\n> > @@ -0,0 +1,9 @@\n> > +#ifndef AUTOSTASH_H\n> > +#define AUTOSTASH_H\n> > +\n> > +#include \"strbuf.h\"\n> > +\n> > +/* Read one file, then strip line endings */\n> > +int read_one(const char *path, struct strbuf *buf);\n> > +\n> > +#endif\n> > diff --git a/builtin/rebase.c b/builtin/rebase.c\n> > index 4a20582e72..9fd7de6b2f 100644\n> > --- a/builtin/rebase.c\n> > +++ b/builtin/rebase.c\n> > @@ -27,6 +27,7 @@\n> >   #include \"branch.h\"\n> >   #include \"sequencer.h\"\n> >   #include \"rebase-interactive.h\"\n> > +#include \"autostash.h\"\n> >\n> >   static char const * const builtin_rebase_usage[] = {\n> >   \tN_(\"git rebase [-i] [options] [--exec <cmd>] \"\n> > @@ -561,15 +562,6 @@ static const char *state_dir_path(const char *filename,\n> > struct rebase_options *o\n> >   \treturn path.buf;\n> >   }\n> >\n> > -/* Read one file, then strip line endings */\n> > -static int read_one(const char *path, struct strbuf *buf)\n> > -{\n> > -\tif (strbuf_read_file(buf, path, 0) < 0)\n> > -\t\treturn error_errno(_(\"could not read '%s'\"), path);\n> > -\tstrbuf_trim_trailing_newline(buf);\n> > -\treturn 0;\n> > -}\n> > -\n> >   /* Initialize the rebase options from the state directory. */\n> >   static int read_basic_state(struct rebase_options *opts)\n> >   {\n> >\n>\n"},{"id":"384550","messageId":"20191021185352.GA69085@generichostname","threadId":"52048","inReplyTo":"nycvar.QRO.7.76.6.1910212043200.46@tvgsbejvaqbjf.bet","subject":"Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-10-21T18:53:52Z","receivedAt":"2019-10-21T18:53:57Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Johannes,\n\nOn Mon, Oct 21, 2019 at 08:44:40PM +0200, Johannes Schindelin wrote:\n> Hi Junio,\n> \n> On Fri, 18 Oct 2019, Junio C Hamano wrote:\n> \n> > Denton Liu <liu.denton@gmail.com> writes:\n> >\n> > > There are many += lists in the Makefile and, over time, they have gotten\n> > > slightly out of order, alphabetically. Alphabetically sort all += lists\n> > > to bring them back in order.\n> > > ...\n> >\n> > Hmm.  I like the general thrust, but ...\n> >\n> > >  LIB_OBJS += combine-diff.o\n> > > -LIB_OBJS += commit.o\n> > >  LIB_OBJS += commit-graph.o\n> > >  LIB_OBJS += commit-reach.o\n> > > +LIB_OBJS += commit.o\n> >\n> > ... I do not particularly see this change (there may be similar\n> > ones) desirable.  I'd find it it be much more natural to sort\n> > \"commit-anything\" after \"commit\", and that is true with or without\n> > the common extension \".o\" added to these entries.\n> >\n> > In short, flipping these entries because '.' sorts later than '-' is\n> > making the result look \"less sorted\", at least to me.\n> \n> The problem with this argument is that it disagrees with ASCII, as `-`\n> has code 0x2d while `.` has code 0x2e, i.e. it is lexicographically\n> _larger_.\n> \n> So Denton's patch does the correct thing.\n\nI actually agree with Junio on this one. Without the prefixes, \"commit\"\nwould go before \"commit-graph\" so I think it would make more sense to\norder with the prefixes removed instead of taking the naive ordering by\njust sorting each block.\n\nThanks,\n\nDenton\n\n> \n> Ciao,\n> Dscho\n"},{"id":"384551","messageId":"nycvar.QRO.7.76.6.1910212055280.46@tvgsbejvaqbjf.bet","threadId":"52048","inReplyTo":"ab064a01-eed3-f90b-df47-1f372a063e30@gmail.com","subject":"Re: [RFC PATCH 4/7] autostash: extract reset_head() from rebase","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-10-21T18:56:10Z","receivedAt":"2019-10-21T18:56:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\n[sorry, Phillip, my reply-all fu deserts me today, apparently.]\n\n\nOn Fri, 18 Oct 2019, Phillip Wood wrote:\n\n> Hi Denton\n>\n> It's great to see this being libified, I've had it in mind to do this so we\n> can avoid forking 'git checkout' in sequencer.c\n>\n> On 16/10/2019 18:26, Denton Liu wrote:\n> > Begin the process of lib-ifying the autostash code. In a future commit,\n> >\n> > This patch is best viewed with `--color-moved` and\n> > `--color-moved-ws=allow-indentation-change`.\n> > this will be used to implement `--autostash` in other builtins.\n> >\n> > Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> > ---\n> >   autostash.c      | 137 +++++++++++++++++++++++++++++++++++++++++++++++\n> >   autostash.h      |  12 +++++\n> >   builtin/rebase.c | 137 -----------------------------------------------\n> >   3 files changed, 149 insertions(+), 137 deletions(-)\n> >\n> > diff --git a/autostash.c b/autostash.c\n> > index 62ec7a7c80..eb58e0c8a4 100644\n> > --- a/autostash.c\n> > +++ b/autostash.c\n> > @@ -1,9 +1,17 @@\n> > +#define USE_THE_INDEX_COMPATIBILITY_MACROS\n>\n> It might be nicer to have a preparatory step that fixes this by adding a\n> 'struct repository *r' argument to the function in builtin/rebase.c before\n> moving the function. You could also do the same for next patch and then move\n> both functions together.\n\nIn addition to that, I think that `reset_head()`\n\n- should live in its own file, not be hidden in `autostash.c`,\n\n- its default reflog action should _not_ be \"rebase\".\n\n- ideally be made the working horse of `builtin/reset.c`,\n\n- in addition to that `struct repository *r`, it should probably accept\n  a `struct index_state *index` and a `const char *worktree_directory`,\n  but that can easily come in the future, as needed.\n\nThanks,\nDscho\n\n\n>\n> Best Wishes\n>\n> Phillip\n>\n> > +\n> >   #include \"git-compat-util.h\"\n> >   #include \"autostash.h\"\n> > +#include \"cache-tree.h\"\n> >   #include \"dir.h\"\n> >   #include \"gettext.h\"\n> > +#include \"lockfile.h\"\n> > +#include \"refs.h\"\n> >   #include \"run-command.h\"\n> >   #include \"strbuf.h\"\n> > +#include \"tree-walk.h\"\n> > +#include \"tree.h\"\n> > +#include \"unpack-trees.h\"\n> >\n> >   int read_one(const char *path, struct strbuf *buf)\n> >   {\n> > @@ -13,6 +21,135 @@ int read_one(const char *path, struct strbuf *buf)\n> >   \treturn 0;\n> >   }\n> >\n> > +int reset_head(struct object_id *oid, const char *action,\n> > +\t       const char *switch_to_branch, unsigned flags,\n> > +\t       const char *reflog_orig_head, const char *reflog_head)\n> > +{\n> > +\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n> > +\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n> > +\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n> > +\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n> > +\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n> > +\tstruct object_id head_oid;\n> > +\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n> > +\tstruct lock_file lock = LOCK_INIT;\n> > +\tstruct unpack_trees_options unpack_tree_opts;\n> > +\tstruct tree *tree;\n> > +\tconst char *reflog_action;\n> > +\tstruct strbuf msg = STRBUF_INIT;\n> > +\tsize_t prefix_len;\n> > +\tstruct object_id *orig = NULL, oid_orig,\n> > +\t\t*old_orig = NULL, oid_old_orig;\n> > +\tint ret = 0, nr = 0;\n> > +\n> > +\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n> > +\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n> > +\n> > +\tif (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)\n> > {\n> > +\t\tret = -1;\n> > +\t\tgoto leave_reset_head;\n> > +\t}\n> > +\n> > +\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n> > +\t\tret = error(_(\"could not determine HEAD revision\"));\n> > +\t\tgoto leave_reset_head;\n> > +\t}\n> > +\n> > +\tif (!oid)\n> > +\t\toid = &head_oid;\n> > +\n> > +\tif (refs_only)\n> > +\t\tgoto reset_head_refs;\n> > +\n> > +\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n> > +\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n> > +\tunpack_tree_opts.head_idx = 1;\n> > +\tunpack_tree_opts.src_index = the_repository->index;\n> > +\tunpack_tree_opts.dst_index = the_repository->index;\n> > +\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n> > +\tunpack_tree_opts.update = 1;\n> > +\tunpack_tree_opts.merge = 1;\n> > +\tif (!detach_head)\n> > +\t\tunpack_tree_opts.reset = 1;\n> > +\n> > +\tif (repo_read_index_unmerged(the_repository) < 0) {\n> > +\t\tret = error(_(\"could not read index\"));\n> > +\t\tgoto leave_reset_head;\n> > +\t}\n> > +\n> > +\tif (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++],\n> > &head_oid)) {\n> > +\t\tret = error(_(\"failed to find tree of %s\"),\n> > +\t\t\t    oid_to_hex(&head_oid));\n> > +\t\tgoto leave_reset_head;\n> > +\t}\n> > +\n> > +\tif (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {\n> > +\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n> > +\t\tgoto leave_reset_head;\n> > +\t}\n> > +\n> > +\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n> > +\t\tret = -1;\n> > +\t\tgoto leave_reset_head;\n> > +\t}\n> > +\n> > +\ttree = parse_tree_indirect(oid);\n> > +\tprime_cache_tree(the_repository, the_repository->index, tree);\n> > +\n> > +\tif (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0)\n> > {\n> > +\t\tret = error(_(\"could not write index\"));\n> > +\t\tgoto leave_reset_head;\n> > +\t}\n> > +\n> > +reset_head_refs:\n> > +\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n> > +\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : \"rebase\");\n> > +\tprefix_len = msg.len;\n> > +\n> > +\tif (update_orig_head) {\n> > +\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n> > +\t\t\told_orig = &oid_old_orig;\n> > +\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n> > +\t\t\torig = &oid_orig;\n> > +\t\t\tif (!reflog_orig_head) {\n> > +\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n> > +\t\t\t\treflog_orig_head = msg.buf;\n> > +\t\t\t}\n> > +\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n> > +\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n> > +\t\t} else if (old_orig)\n> > +\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n> > +\t}\n> > +\n> > +\tif (!reflog_head) {\n> > +\t\tstrbuf_setlen(&msg, prefix_len);\n> > +\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n> > +\t\treflog_head = msg.buf;\n> > +\t}\n> > +\tif (!switch_to_branch)\n> > +\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n> > +\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n> > +\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n> > +\telse {\n> > +\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n> > +\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n> > +\t\tif (!ret)\n> > +\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n> > +\t\t\t\t\t    reflog_head);\n> > +\t}\n> > +\tif (run_hook)\n> > +\t\trun_hook_le(NULL, \"post-checkout\",\n> > +\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n> > +\t\t\t    oid_to_hex(oid), \"1\", NULL);\n> > +\n> > +leave_reset_head:\n> > +\tstrbuf_release(&msg);\n> > +\trollback_lock_file(&lock);\n> > +\twhile (nr)\n> > +\t\tfree((void *)desc[--nr].buffer);\n> > +\treturn ret;\n> > +}\n> > +\n> >   int apply_autostash(const char *path)\n> >   {\n> >   \tstruct strbuf autostash = STRBUF_INIT;\n> > diff --git a/autostash.h b/autostash.h\n> > index 5f4e4bd22c..1406638166 100644\n> > --- a/autostash.h\n> > +++ b/autostash.h\n> > @@ -6,6 +6,18 @@\n> >   /* Read one file, then strip line endings */\n> >   int read_one(const char *path, struct strbuf *buf);\n> >\n> > +#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n> > +\n> > +#define RESET_HEAD_DETACH (1<<0)\n> > +#define RESET_HEAD_HARD (1<<1)\n> > +#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n> > +#define RESET_HEAD_REFS_ONLY (1<<3)\n> > +#define RESET_ORIG_HEAD (1<<4)\n> > +\n> > +int reset_head(struct object_id *oid, const char *action,\n> > +\t       const char *switch_to_branch, unsigned flags,\n> > +\t       const char *reflog_orig_head, const char *reflog_head);\n> > +\n> >   int apply_autostash(const char *path);\n> >\n> >   #endif\n> > diff --git a/builtin/rebase.c b/builtin/rebase.c\n> > index 661928d427..c3165896cc 100644\n> > --- a/builtin/rebase.c\n> > +++ b/builtin/rebase.c\n> > @@ -734,143 +734,6 @@ static void add_var(struct strbuf *buf, const char\n> > *name, const char *value)\n> >   \t}\n> >   }\n> >\n> > -#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n> > -\n> > -#define RESET_HEAD_DETACH (1<<0)\n> > -#define RESET_HEAD_HARD (1<<1)\n> > -#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n> > -#define RESET_HEAD_REFS_ONLY (1<<3)\n> > -#define RESET_ORIG_HEAD (1<<4)\n> > -\n> > -static int reset_head(struct object_id *oid, const char *action,\n> > -\t\t      const char *switch_to_branch, unsigned flags,\n> > -\t\t      const char *reflog_orig_head, const char *reflog_head)\n> > -{\n> > -\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n> > -\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n> > -\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n> > -\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n> > -\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n> > -\tstruct object_id head_oid;\n> > -\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n> > -\tstruct lock_file lock = LOCK_INIT;\n> > -\tstruct unpack_trees_options unpack_tree_opts;\n> > -\tstruct tree *tree;\n> > -\tconst char *reflog_action;\n> > -\tstruct strbuf msg = STRBUF_INIT;\n> > -\tsize_t prefix_len;\n> > -\tstruct object_id *orig = NULL, oid_orig,\n> > -\t\t*old_orig = NULL, oid_old_orig;\n> > -\tint ret = 0, nr = 0;\n> > -\n> > -\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n> > -\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n> > -\n> > -\tif (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0)\n> > {\n> > -\t\tret = -1;\n> > -\t\tgoto leave_reset_head;\n> > -\t}\n> > -\n> > -\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n> > -\t\tret = error(_(\"could not determine HEAD revision\"));\n> > -\t\tgoto leave_reset_head;\n> > -\t}\n> > -\n> > -\tif (!oid)\n> > -\t\toid = &head_oid;\n> > -\n> > -\tif (refs_only)\n> > -\t\tgoto reset_head_refs;\n> > -\n> > -\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n> > -\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n> > -\tunpack_tree_opts.head_idx = 1;\n> > -\tunpack_tree_opts.src_index = the_repository->index;\n> > -\tunpack_tree_opts.dst_index = the_repository->index;\n> > -\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n> > -\tunpack_tree_opts.update = 1;\n> > -\tunpack_tree_opts.merge = 1;\n> > -\tif (!detach_head)\n> > -\t\tunpack_tree_opts.reset = 1;\n> > -\n> > -\tif (repo_read_index_unmerged(the_repository) < 0) {\n> > -\t\tret = error(_(\"could not read index\"));\n> > -\t\tgoto leave_reset_head;\n> > -\t}\n> > -\n> > -\tif (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++],\n> > &head_oid)) {\n> > -\t\tret = error(_(\"failed to find tree of %s\"),\n> > -\t\t\t    oid_to_hex(&head_oid));\n> > -\t\tgoto leave_reset_head;\n> > -\t}\n> > -\n> > -\tif (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {\n> > -\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n> > -\t\tgoto leave_reset_head;\n> > -\t}\n> > -\n> > -\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n> > -\t\tret = -1;\n> > -\t\tgoto leave_reset_head;\n> > -\t}\n> > -\n> > -\ttree = parse_tree_indirect(oid);\n> > -\tprime_cache_tree(the_repository, the_repository->index, tree);\n> > -\n> > -\tif (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0)\n> > {\n> > -\t\tret = error(_(\"could not write index\"));\n> > -\t\tgoto leave_reset_head;\n> > -\t}\n> > -\n> > -reset_head_refs:\n> > -\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n> > -\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : \"rebase\");\n> > -\tprefix_len = msg.len;\n> > -\n> > -\tif (update_orig_head) {\n> > -\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n> > -\t\t\told_orig = &oid_old_orig;\n> > -\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n> > -\t\t\torig = &oid_orig;\n> > -\t\t\tif (!reflog_orig_head) {\n> > -\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n> > -\t\t\t\treflog_orig_head = msg.buf;\n> > -\t\t\t}\n> > -\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n> > -\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n> > -\t\t} else if (old_orig)\n> > -\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n> > -\t}\n> > -\n> > -\tif (!reflog_head) {\n> > -\t\tstrbuf_setlen(&msg, prefix_len);\n> > -\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n> > -\t\treflog_head = msg.buf;\n> > -\t}\n> > -\tif (!switch_to_branch)\n> > -\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n> > -\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n> > -\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n> > -\telse {\n> > -\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n> > -\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n> > -\t\tif (!ret)\n> > -\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n> > -\t\t\t\t\t    reflog_head);\n> > -\t}\n> > -\tif (run_hook)\n> > -\t\trun_hook_le(NULL, \"post-checkout\",\n> > -\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n> > -\t\t\t    oid_to_hex(oid), \"1\", NULL);\n> > -\n> > -leave_reset_head:\n> > -\tstrbuf_release(&msg);\n> > -\trollback_lock_file(&lock);\n> > -\twhile (nr)\n> > -\t\tfree((void *)desc[--nr].buffer);\n> > -\treturn ret;\n> > -}\n> > -\n> >   static int move_to_original_branch(struct rebase_options *opts)\n> >   {\n> >    struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;\n> >\n>\n"},{"id":"384552","messageId":"nycvar.QRO.7.76.6.1910212057460.46@tvgsbejvaqbjf.bet","threadId":"52048","inReplyTo":"0518b0b187245e807987457cd558a1ab38476c5c.1571246693.git.liu.denton@gmail.com","subject":"Re: [RFC PATCH 5/7] autostash: extract perform_autostash() from rebase","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-10-21T19:00:54Z","receivedAt":"2019-10-21T19:01:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Denton,\n\nOn Wed, 16 Oct 2019, Denton Liu wrote:\n\n> Continue the process of lib-ifying the autostash code. In a future\n> commit, this will be used to implement `--autostash` in other builtins.\n>\n> This patch is best viewed with `--color-moved` and\n> `--color-moved-ws=allow-indentation-change`.\n>\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>  autostash.c      | 46 ++++++++++++++++++++++++++++++++++++++++++++++\n>  autostash.h      |  1 +\n>  builtin/rebase.c | 44 +-------------------------------------------\n>  3 files changed, 48 insertions(+), 43 deletions(-)\n>\n> diff --git a/autostash.c b/autostash.c\n> index eb58e0c8a4..722cf78b12 100644\n> --- a/autostash.c\n> +++ b/autostash.c\n> @@ -12,6 +12,7 @@\n>  #include \"tree-walk.h\"\n>  #include \"tree.h\"\n>  #include \"unpack-trees.h\"\n> +#include \"wt-status.h\"\n>\n>  int read_one(const char *path, struct strbuf *buf)\n>  {\n> @@ -150,6 +151,51 @@ int reset_head(struct object_id *oid, const char *action,\n>  \treturn ret;\n>  }\n>\n> +void perform_autostash(const char *path)\n\nMaybe we can find a better name than \"perform_autostash\"? It is not\nclear whether this is the \"saving\" or \"applying\" part of the autostash,\nI think.\n\nMaybe `save_autostash()`? And maybe instead of `path`, the parameter\ncould be `save_hash_to_path` or something similar?\n\nNow that I think of it, I forgot to mention in a reply to an earlier\npatch in this series that `reset_head()` might be too generic a name to\nbe a global function...\n\nCiao,\nDscho\n\n> +{\n> +\tstruct strbuf buf = STRBUF_INIT;\n> +\tstruct lock_file lock_file = LOCK_INIT;\n> +\tint fd;\n> +\n> +\tfd = hold_locked_index(&lock_file, 0);\n> +\trefresh_cache(REFRESH_QUIET);\n> +\tif (0 <= fd)\n> +\t\trepo_update_index_if_able(the_repository, &lock_file);\n> +\trollback_lock_file(&lock_file);\n> +\n> +\tif (has_unstaged_changes(the_repository, 1) ||\n> +\t    has_uncommitted_changes(the_repository, 1)) {\n> +\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n> +\t\tstruct object_id oid;\n> +\n> +\t\targv_array_pushl(&stash.args,\n> +\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n> +\t\tstash.git_cmd = 1;\n> +\t\tstash.no_stdin = 1;\n> +\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n> +\t\t\tdie(_(\"Cannot autostash\"));\n> +\t\tstrbuf_trim_trailing_newline(&buf);\n> +\t\tif (get_oid(buf.buf, &oid))\n> +\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n> +\t\t\t    buf.buf);\n> +\t\tstrbuf_reset(&buf);\n> +\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n> +\n> +\t\tif (safe_create_leading_directories_const(path))\n> +\t\t\tdie(_(\"Could not create directory for '%s'\"),\n> +\t\t\t    path);\n> +\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n> +\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n> +\t\tif (reset_head(NULL, \"reset --hard\",\n> +\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)\n> +\t\t\tdie(_(\"could not reset --hard\"));\n> +\n> +\t\tif (discard_index(the_repository->index) < 0 ||\n> +\t\t\trepo_read_index(the_repository) < 0)\n> +\t\t\tdie(_(\"could not read index\"));\n> +\t}\n> +}\n> +\n>  int apply_autostash(const char *path)\n>  {\n>  \tstruct strbuf autostash = STRBUF_INIT;\n> diff --git a/autostash.h b/autostash.h\n> index 1406638166..e08ccb9881 100644\n> --- a/autostash.h\n> +++ b/autostash.h\n> @@ -18,6 +18,7 @@ int reset_head(struct object_id *oid, const char *action,\n>  \t       const char *switch_to_branch, unsigned flags,\n>  \t       const char *reflog_orig_head, const char *reflog_head);\n>\n> +void perform_autostash(const char *path);\n>  int apply_autostash(const char *path);\n>\n>  #endif\n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index c3165896cc..c4decdfb5b 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -1797,49 +1797,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>  \t\tdie(_(\"could not read index\"));\n>\n>  \tif (options.autostash) {\n> -\t\tstruct lock_file lock_file = LOCK_INIT;\n> -\t\tint fd;\n> -\n> -\t\tfd = hold_locked_index(&lock_file, 0);\n> -\t\trefresh_cache(REFRESH_QUIET);\n> -\t\tif (0 <= fd)\n> -\t\t\trepo_update_index_if_able(the_repository, &lock_file);\n> -\t\trollback_lock_file(&lock_file);\n> -\n> -\t\tif (has_unstaged_changes(the_repository, 1) ||\n> -\t\t    has_uncommitted_changes(the_repository, 1)) {\n> -\t\t\tconst char *autostash =\n> -\t\t\t\tstate_dir_path(\"autostash\", &options);\n> -\t\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n> -\t\t\tstruct object_id oid;\n> -\n> -\t\t\targv_array_pushl(&stash.args,\n> -\t\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n> -\t\t\tstash.git_cmd = 1;\n> -\t\t\tstash.no_stdin = 1;\n> -\t\t\tstrbuf_reset(&buf);\n> -\t\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n> -\t\t\t\tdie(_(\"Cannot autostash\"));\n> -\t\t\tstrbuf_trim_trailing_newline(&buf);\n> -\t\t\tif (get_oid(buf.buf, &oid))\n> -\t\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n> -\t\t\t\t    buf.buf);\n> -\t\t\tstrbuf_reset(&buf);\n> -\t\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n> -\n> -\t\t\tif (safe_create_leading_directories_const(autostash))\n> -\t\t\t\tdie(_(\"Could not create directory for '%s'\"),\n> -\t\t\t\t    options.state_dir);\n> -\t\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n> -\t\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n> -\t\t\tif (reset_head(NULL, \"reset --hard\",\n> -\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)\n> -\t\t\t\tdie(_(\"could not reset --hard\"));\n> -\n> -\t\t\tif (discard_index(the_repository->index) < 0 ||\n> -\t\t\t\trepo_read_index(the_repository) < 0)\n> -\t\t\t\tdie(_(\"could not read index\"));\n> -\t\t}\n> +\t\tperform_autostash(state_dir_path(\"autostash\", &options));\n>  \t}\n>\n>  \tif (require_clean_work_tree(the_repository, \"rebase\",\n> --\n> 2.23.0.897.g0a19638b1e\n>\n>\n"},{"id":"384554","messageId":"nycvar.QRO.7.76.6.1910212103080.46@tvgsbejvaqbjf.bet","threadId":"52048","inReplyTo":"f43c10e4818c91a8c6e9d5e0ce2a04710db3e300.1571246693.git.liu.denton@gmail.com","subject":"Re: [RFC PATCH 7/7] merge: teach --autostash option","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-10-21T19:10:09Z","receivedAt":"2019-10-21T19:10:34Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Denton,\n\nOn Wed, 16 Oct 2019, Denton Liu wrote:\n\n> In rebase, one can pass the `--autostash` option to cause the worktree\n> to be automatically stashed before continuing with the rebase. This\n> option is missing in merge, however.\n>\n> Implement the `--autostash` option and corresponding `merge.autoStash`\n> option in merge which stashes before merging and then pops after.\n>\n> Reported-by: Alban Gruin <alban.gruin@gmail.com>\n\nMaybe \"Suggested-by\" would be more accurate, it is not like this feature\nrequest was a bug report...\n\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>  builtin/merge.c | 13 +++++++++++++\n>  builtin/pull.c  |  9 +++++----\n>  t/t5520-pull.sh |  8 --------\n>  3 files changed, 18 insertions(+), 12 deletions(-)\n>\n> diff --git a/builtin/merge.c b/builtin/merge.c\n> index 062e911441..d1a5eaad0d 100644\n> --- a/builtin/merge.c\n> +++ b/builtin/merge.c\n> @@ -40,6 +40,7 @@\n>  #include \"branch.h\"\n>  #include \"commit-reach.h\"\n>  #include \"wt-status.h\"\n> +#include \"autostash.h\"\n>\n>  #define DEFAULT_TWOHEAD (1<<0)\n>  #define DEFAULT_OCTOPUS (1<<1)\n> @@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {\n>  \tNULL\n>  };\n>\n> +static GIT_PATH_FUNC(merge_autostash, \"MERGE_AUTOSTASH\")\n> +\n>  static int show_diffstat = 1, shortlog_len = -1, squash;\n>  static int option_commit = -1;\n>  static int option_edit = -1;\n> @@ -81,6 +84,7 @@ static int show_progress = -1;\n>  static int default_to_upstream = 1;\n>  static int signoff;\n>  static const char *sign_commit;\n> +static int autostash;\n>  static int no_verify;\n>\n>  static struct strategy all_strategy[] = {\n> @@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {\n>  \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n>  \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n>  \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n> +\tOPT_BOOL(0, \"autostash\", &autostash,\n> +\t      N_(\"automatically stash/stash pop before and after\")),\n>  \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n>  \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n>  \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n> @@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,\n>  \t\tstrbuf_addf(&reflog_message, \"%s: %s\",\n>  \t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n>  \t}\n> +\tapply_autostash(merge_autostash());\n\nShould this not be guarded by `if (autostash)`?\n\n>  \tif (squash) {\n>  \t\tsquash_message(head_commit, remoteheads);\n>  \t} else {\n> @@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n>  \t} else if (!strcmp(k, \"commit.gpgsign\")) {\n>  \t\tsign_commit = git_config_bool(k, v) ? \"\" : NULL;\n>  \t\treturn 0;\n> +\t} else if (!strcmp(k, \"merge.autostash\")) {\n> +\t\tautostash = git_config_bool(k, v);\n> +\t\treturn 0;\n>  \t}\n>\n>  \tstatus = fmt_merge_msg_config(k, v, cb);\n> @@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n>  \t\tfor (j = common; j; j = j->next)\n>  \t\t\tcommit_list_insert(j->item, &reversed);\n>\n> +\t\tif (autostash)\n> +\t\t\tperform_autostash(merge_autostash());\n>  \t\thold_locked_index(&lock, LOCK_DIE_ON_ERROR);\n>  \t\tclean = merge_recursive(&o, head,\n>  \t\t\t\tremoteheads->item, reversed, &result);\n> @@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>\n>  \t\t/* Invoke 'git reset --merge' */\n>  \t\tret = cmd_reset(nargc, nargv, prefix);\n> +\t\tapply_autostash(merge_autostash());\n\nAgain, this should be guarded by `if (autostash)`, methinks.\n\n>  \t\tgoto done;\n>  \t}\n>\n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index d25ff13a60..ee186781ae 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -183,7 +183,7 @@ static struct option pull_options[] = {\n>  \t\tN_(\"verify that the named commit has a valid GPG signature\"),\n>  \t\tPARSE_OPT_NOARG),\n>  \tOPT_BOOL(0, \"autostash\", &opt_autostash,\n> -\t\tN_(\"automatically stash/stash pop before and after rebase\")),\n> +\t\tN_(\"automatically stash/stash pop before and after\")),\n\nMakes sense; this is now shared between the rebase and the merge modes.\n\n>  \tOPT_PASSTHRU_ARGV('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n>  \t\tN_(\"merge strategy to use\"),\n>  \t\t0),\n> @@ -671,6 +671,10 @@ static int run_merge(void)\n>  \targv_array_pushv(&args, opt_strategy_opts.argv);\n>  \tif (opt_gpg_sign)\n>  \t\targv_array_push(&args, opt_gpg_sign);\n> +\tif (opt_autostash == 0)\n> +\t\targv_array_push(&args, \"--no-autostash\");\n> +\telse if (opt_autostash == 1)\n> +\t\targv_array_push(&args, \"--autostash\");\n\nOr shorter:\n\n\targv_array_pushf(&args, \"%s-autostash\", opt_autostash ? \"-\" : \"--no\");\n\nAh, but that would mishandle `-1`. I bet I will be puzzled by this\nagain. Maybe it would make sense to mention in a code comment that it\ncan be `-1` in which case we leave it to `rebase` to use the config\nsettings to determine whether or not to autostash.\n\n>  \tif (opt_allow_unrelated_histories > 0)\n>  \t\targv_array_push(&args, \"--allow-unrelated-histories\");\n>\n> @@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n>  \tif (get_oid(\"HEAD\", &orig_head))\n>  \t\toidclr(&orig_head);\n>\n> -\tif (!opt_rebase && opt_autostash != -1)\n> -\t\tdie(_(\"--[no-]autostash option is only valid with --rebase.\"));\n> -\n>  \tautostash = config_autostash;\n>  \tif (opt_rebase) {\n>  \t\tif (opt_autostash != -1)\n> diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\n> index cf4cc32fd0..75f162495a 100755\n> --- a/t/t5520-pull.sh\n> +++ b/t/t5520-pull.sh\n> @@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '\n>  \ttest_pull_autostash_fail --rebase --no-autostash\n>  '\n>\n> -for i in --autostash --no-autostash\n> -do\n> -\ttest_expect_success \"pull $i (without --rebase) is illegal\" '\n> -\t\ttest_must_fail git pull $i . copy 2>err &&\n> -\t\ttest_i18ngrep \"only valid with --rebase\" err\n> -\t'\n> -done\n> -\n\nNice!\nDscho\n\n>  test_expect_success 'pull.rebase' '\n>  \tgit reset --hard before-rebase &&\n>  \ttest_config pull.rebase true &&\n> --\n> 2.23.0.897.g0a19638b1e\n>\n>\n"},{"id":"384557","messageId":"xmqqblu9lw00.fsf@gitster-ct.c.googlers.com","threadId":"52048","inReplyTo":"nycvar.QRO.7.76.6.1910212043200.46@tvgsbejvaqbjf.bet","subject":"Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-10-21T19:49:19Z","receivedAt":"2019-10-21T19:49:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> ... I do not particularly see this change (there may be similar\n>> ones) desirable.  I'd find it it be much more natural to sort\n>> \"commit-anything\" after \"commit\", and that is true with or without\n>> the common extension \".o\" added to these entries.\n>>\n>> In short, flipping these entries because '.' sorts later than '-' is\n>> making the result look \"less sorted\", at least to me.\n>\n> The problem with this argument is that it disagrees with ASCII, as `-`\n> has code 0x2d while `.` has code 0x2e, i.e. it is lexicographically\n> _larger_.\n\nI am saying that sorting these in ASCII order did not produce result\nthat is easy to the eyes.\n\nYou are saying that Denton's patch sorted these lines in ASCII order.\n\nI agree with you that it did correctly sort them in ASCII order.\n\nThat does not make the patch right ;-)\n\n"},{"id":"384558","messageId":"20191021195413.GB3959@sigill.intra.peff.net","threadId":"52048","inReplyTo":"xmqqblu9lw00.fsf@gitster-ct.c.googlers.com","subject":"Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2019-10-21T19:54:13Z","receivedAt":"2019-10-21T19:54:15Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Oct 22, 2019 at 04:49:19AM +0900, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> >> ... I do not particularly see this change (there may be similar\n> >> ones) desirable.  I'd find it it be much more natural to sort\n> >> \"commit-anything\" after \"commit\", and that is true with or without\n> >> the common extension \".o\" added to these entries.\n> >>\n> >> In short, flipping these entries because '.' sorts later than '-' is\n> >> making the result look \"less sorted\", at least to me.\n> >\n> > The problem with this argument is that it disagrees with ASCII, as `-`\n> > has code 0x2d while `.` has code 0x2e, i.e. it is lexicographically\n> > _larger_.\n> \n> I am saying that sorting these in ASCII order did not produce result\n> that is easy to the eyes.\n> \n> You are saying that Denton's patch sorted these lines in ASCII order.\n> \n> I agree with you that it did correctly sort them in ASCII order.\n> \n> That does not make the patch right ;-)\n\nWhat's the purpose of sorting them, though? I thought it was less for\naesthetics and more to to keep lines deterministic (to avoid two\nbranches adding the same line in different places, thus causing\nweirdness when the two are merged). In that case, I think we care less\nabout the exact order and more that anybody can easily reproduce the\nsame sort (by running \"10:!sort\" or whatever you weird emacs-types would\ntype).\n\n-Peff\n"},{"id":"384596","messageId":"xmqq7e4xko8v.fsf@gitster-ct.c.googlers.com","threadId":"52048","inReplyTo":"20191021195413.GB3959@sigill.intra.peff.net","subject":"Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-10-22T11:34:24Z","receivedAt":"2019-10-22T11:34:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>> ...\n>> I agree with you that it did correctly sort them in ASCII order.\n>\n> What's the purpose of sorting them, though? I thought it was less for\n> aesthetics and more to to keep lines deterministic (to avoid two\n> branches adding the same line in different places, thus causing\n> weirdness when the two are merged). In that case, I think we care less\n> about the exact order and more that anybody can easily reproduce the\n> same sort (by running \"10:!sort\" or whatever you weird emacs-types would\n> type).\n\nIn the ideal world, \"sort\" would have a handy option we can tell it\nto reshuffle the ASCII table in such a way that all punctuations\ncome before alphanumeric, making sure \"/\" and \".\" are the first two\nletters in the alphabet, and everybody can use it to sort the lines\nreproducibly and also readably.  But I do not know of such a widely\nused implementation of \"sort\", so...\n\nIf we had known better, we would have used such a custom sort order\nto sort the index entries, making sure that slash sorts before any\nother byte ;-)\n"},{"id":"384644","messageId":"nycvar.QRO.7.76.6.1910230132260.46@tvgsbejvaqbjf.bet","threadId":"52048","inReplyTo":"20191021185352.GA69085@generichostname","subject":"Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-10-22T23:33:37Z","receivedAt":"2019-10-22T23:33:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 21 Oct 2019, Denton Liu wrote:\n\n> Hi Johannes,\n>\n> On Mon, Oct 21, 2019 at 08:44:40PM +0200, Johannes Schindelin wrote:\n> > Hi Junio,\n> >\n> > On Fri, 18 Oct 2019, Junio C Hamano wrote:\n> >\n> > > Denton Liu <liu.denton@gmail.com> writes:\n> > >\n> > > > There are many += lists in the Makefile and, over time, they have gotten\n> > > > slightly out of order, alphabetically. Alphabetically sort all += lists\n> > > > to bring them back in order.\n> > > > ...\n> > >\n> > > Hmm.  I like the general thrust, but ...\n> > >\n> > > >  LIB_OBJS += combine-diff.o\n> > > > -LIB_OBJS += commit.o\n> > > >  LIB_OBJS += commit-graph.o\n> > > >  LIB_OBJS += commit-reach.o\n> > > > +LIB_OBJS += commit.o\n> > >\n> > > ... I do not particularly see this change (there may be similar\n> > > ones) desirable.  I'd find it it be much more natural to sort\n> > > \"commit-anything\" after \"commit\", and that is true with or without\n> > > the common extension \".o\" added to these entries.\n> > >\n> > > In short, flipping these entries because '.' sorts later than '-' is\n> > > making the result look \"less sorted\", at least to me.\n> >\n> > The problem with this argument is that it disagrees with ASCII, as `-`\n> > has code 0x2d while `.` has code 0x2e, i.e. it is lexicographically\n> > _larger_.\n> >\n> > So Denton's patch does the correct thing.\n>\n> I actually agree with Junio on this one. Without the prefixes, \"commit\"\n> would go before \"commit-graph\" so I think it would make more sense to\n> order with the prefixes removed instead of taking the naive ordering by\n> just sorting each block.\n\nThat will make it harder on other contributors like me, who prefer to\nmark the lines in `vim` and then call `:sort` on them, and then not care\nabout it any further.\n\nAny decision that makes automating tedious tasks harder puts more burden\non human beings. I don't like that.\n\nCiao,\nDscho\n\n>\n> Thanks,\n>\n> Denton\n>\n> >\n> > Ciao,\n> > Dscho\n>\n"},{"id":"388842","messageId":"20191224095831.GA2743221@generichostname","threadId":"52048","inReplyTo":"4a47c27f-2c6a-0bd6-eac8-533700087af9@gmail.com","subject":"Re: [RFC PATCH 7/7] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T09:58:31Z","receivedAt":"2019-12-24T09:58:38Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Phillip,\n\nOn Fri, Oct 18, 2019 at 10:46:37AM +0100, Phillip Wood wrote:\n> Hi Denton\n> \n> On 16/10/2019 18:26, Denton Liu wrote:\n> > In rebase, one can pass the `--autostash` option to cause the worktree\n> > to be automatically stashed before continuing with the rebase. This\n> > option is missing in merge, however.\n> \n> It might be helpful to say why this option is useful. I can see one might\n> want to stash before doing `git pull` in the same way as one might before a\n> rebase but what's the motivation for doing it before a normal merge?\n\nWill do.\n\n> \n> > \n> > Implement the `--autostash` option and corresponding `merge.autoStash`\n> > option in merge which stashes before merging and then pops after.\n> > \n> > Reported-by: Alban Gruin <alban.gruin@gmail.com>\n> > Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> > ---\n> >   builtin/merge.c | 13 +++++++++++++\n> >   builtin/pull.c  |  9 +++++----\n> >   t/t5520-pull.sh |  8 --------\n> >   3 files changed, 18 insertions(+), 12 deletions(-)\n> > \n> > diff --git a/builtin/merge.c b/builtin/merge.c\n> > index 062e911441..d1a5eaad0d 100644\n> > --- a/builtin/merge.c\n> > +++ b/builtin/merge.c\n> > @@ -40,6 +40,7 @@\n> >   #include \"branch.h\"\n> >   #include \"commit-reach.h\"\n> >   #include \"wt-status.h\"\n> > +#include \"autostash.h\"\n> >   #define DEFAULT_TWOHEAD (1<<0)\n> >   #define DEFAULT_OCTOPUS (1<<1)\n> > @@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {\n> >   \tNULL\n> >   };\n> > +static GIT_PATH_FUNC(merge_autostash, \"MERGE_AUTOSTASH\")\n> > +\n> >   static int show_diffstat = 1, shortlog_len = -1, squash;\n> >   static int option_commit = -1;\n> >   static int option_edit = -1;\n> > @@ -81,6 +84,7 @@ static int show_progress = -1;\n> >   static int default_to_upstream = 1;\n> >   static int signoff;\n> >   static const char *sign_commit;\n> > +static int autostash;\n> >   static int no_verify;\n> >   static struct strategy all_strategy[] = {\n> > @@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {\n> >   \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n> >   \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n> >   \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n> > +\tOPT_BOOL(0, \"autostash\", &autostash,\n> > +\t      N_(\"automatically stash/stash pop before and after\")),\n> >   \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n> >   \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n> >   \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n> > @@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,\n> >   \t\tstrbuf_addf(&reflog_message, \"%s: %s\",\n> >   \t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n> >   \t}\n> > +\tapply_autostash(merge_autostash());\n> >   \tif (squash) {\n> >   \t\tsquash_message(head_commit, remoteheads);\n> >   \t} else {\n> > @@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n> >   \t} else if (!strcmp(k, \"commit.gpgsign\")) {\n> >   \t\tsign_commit = git_config_bool(k, v) ? \"\" : NULL;\n> >   \t\treturn 0;\n> > +\t} else if (!strcmp(k, \"merge.autostash\")) {\n> > +\t\tautostash = git_config_bool(k, v);\n> > +\t\treturn 0;\n> >   \t}\n> >   \tstatus = fmt_merge_msg_config(k, v, cb);\n> > @@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n> >   \t\tfor (j = common; j; j = j->next)\n> >   \t\t\tcommit_list_insert(j->item, &reversed);\n> > +\t\tif (autostash)\n> > +\t\t\tperform_autostash(merge_autostash());\n> >   \t\thold_locked_index(&lock, LOCK_DIE_ON_ERROR);\n> >   \t\tclean = merge_recursive(&o, head,\n> >   \t\t\t\tremoteheads->item, reversed, &result);\n> > @@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n> >   \t\t/* Invoke 'git reset --merge' */\n> >   \t\tret = cmd_reset(nargc, nargv, prefix);\n> > +\t\tapply_autostash(merge_autostash());\n> >   \t\tgoto done;\n> >   \t}\n> > diff --git a/builtin/pull.c b/builtin/pull.c\n> > index d25ff13a60..ee186781ae 100644\n> > --- a/builtin/pull.c\n> > +++ b/builtin/pull.c\n> > @@ -183,7 +183,7 @@ static struct option pull_options[] = {\n> >   \t\tN_(\"verify that the named commit has a valid GPG signature\"),\n> >   \t\tPARSE_OPT_NOARG),\n> >   \tOPT_BOOL(0, \"autostash\", &opt_autostash,\n> > -\t\tN_(\"automatically stash/stash pop before and after rebase\")),\n> > +\t\tN_(\"automatically stash/stash pop before and after\")),\n> \n> I've not looked closely at the code in this patch but noticed this. I think\n> it would read better if it said\n> \n>     automatically stash/stash pop before and after pulling\n\nSince this is an `argh` argument, it says that we should \"keep it\nhomogenous across the repository\". Since rebase (which I copied it\nfrom), merge and pull will be sharing the same argh string, I think that\nwe should leave it as is.\n\nThanks,\n\nDenton\n\n> \n> Best Wishes\n> \n> Phillip\n> \n> >   \tOPT_PASSTHRU_ARGV('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n> >   \t\tN_(\"merge strategy to use\"),\n> >   \t\t0),\n> > @@ -671,6 +671,10 @@ static int run_merge(void)\n> >   \targv_array_pushv(&args, opt_strategy_opts.argv);\n> >   \tif (opt_gpg_sign)\n> >   \t\targv_array_push(&args, opt_gpg_sign);\n> > +\tif (opt_autostash == 0)\n> > +\t\targv_array_push(&args, \"--no-autostash\");\n> > +\telse if (opt_autostash == 1)\n> > +\t\targv_array_push(&args, \"--autostash\");\n> >   \tif (opt_allow_unrelated_histories > 0)\n> >   \t\targv_array_push(&args, \"--allow-unrelated-histories\");\n> > @@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n> >   \tif (get_oid(\"HEAD\", &orig_head))\n> >   \t\toidclr(&orig_head);\n> > -\tif (!opt_rebase && opt_autostash != -1)\n> > -\t\tdie(_(\"--[no-]autostash option is only valid with --rebase.\"));\n> > -\n> >   \tautostash = config_autostash;\n> >   \tif (opt_rebase) {\n> >   \t\tif (opt_autostash != -1)\n> > diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\n> > index cf4cc32fd0..75f162495a 100755\n> > --- a/t/t5520-pull.sh\n> > +++ b/t/t5520-pull.sh\n> > @@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '\n> >   \ttest_pull_autostash_fail --rebase --no-autostash\n> >   '\n> > -for i in --autostash --no-autostash\n> > -do\n> > -\ttest_expect_success \"pull $i (without --rebase) is illegal\" '\n> > -\t\ttest_must_fail git pull $i . copy 2>err &&\n> > -\t\ttest_i18ngrep \"only valid with --rebase\" err\n> > -\t'\n> > -done\n> > -\n> >   test_expect_success 'pull.rebase' '\n> >   \tgit reset --hard before-rebase &&\n> >   \ttest_config pull.rebase true &&\n> > \n"},{"id":"388843","messageId":"20191224100507.GB2743221@generichostname","threadId":"52048","inReplyTo":"nycvar.QRO.7.76.6.1910212103080.46@tvgsbejvaqbjf.bet","subject":"Re: [RFC PATCH 7/7] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T10:05:07Z","receivedAt":"2019-12-24T10:05:13Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Dscho,\n\nOn Mon, Oct 21, 2019 at 09:10:09PM +0200, Johannes Schindelin wrote:\n> Hi Denton,\n> \n> On Wed, 16 Oct 2019, Denton Liu wrote:\n> \n> > In rebase, one can pass the `--autostash` option to cause the worktree\n> > to be automatically stashed before continuing with the rebase. This\n> > option is missing in merge, however.\n> >\n> > Implement the `--autostash` option and corresponding `merge.autoStash`\n> > option in merge which stashes before merging and then pops after.\n> >\n> > Reported-by: Alban Gruin <alban.gruin@gmail.com>\n> \n> Maybe \"Suggested-by\" would be more accurate, it is not like this feature\n> request was a bug report...\n\nMakes sense.\n\n> \n> > Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> > ---\n> >  builtin/merge.c | 13 +++++++++++++\n> >  builtin/pull.c  |  9 +++++----\n> >  t/t5520-pull.sh |  8 --------\n> >  3 files changed, 18 insertions(+), 12 deletions(-)\n> >\n> > diff --git a/builtin/merge.c b/builtin/merge.c\n> > index 062e911441..d1a5eaad0d 100644\n> > --- a/builtin/merge.c\n> > +++ b/builtin/merge.c\n> > @@ -40,6 +40,7 @@\n> >  #include \"branch.h\"\n> >  #include \"commit-reach.h\"\n> >  #include \"wt-status.h\"\n> > +#include \"autostash.h\"\n> >\n> >  #define DEFAULT_TWOHEAD (1<<0)\n> >  #define DEFAULT_OCTOPUS (1<<1)\n> > @@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {\n> >  \tNULL\n> >  };\n> >\n> > +static GIT_PATH_FUNC(merge_autostash, \"MERGE_AUTOSTASH\")\n> > +\n> >  static int show_diffstat = 1, shortlog_len = -1, squash;\n> >  static int option_commit = -1;\n> >  static int option_edit = -1;\n> > @@ -81,6 +84,7 @@ static int show_progress = -1;\n> >  static int default_to_upstream = 1;\n> >  static int signoff;\n> >  static const char *sign_commit;\n> > +static int autostash;\n> >  static int no_verify;\n> >\n> >  static struct strategy all_strategy[] = {\n> > @@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {\n> >  \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n> >  \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n> >  \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n> > +\tOPT_BOOL(0, \"autostash\", &autostash,\n> > +\t      N_(\"automatically stash/stash pop before and after\")),\n> >  \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n> >  \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n> >  \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n> > @@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,\n> >  \t\tstrbuf_addf(&reflog_message, \"%s: %s\",\n> >  \t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n> >  \t}\n> > +\tapply_autostash(merge_autostash());\n> \n> Should this not be guarded by `if (autostash)`?\n\nThe apply_autostash() checks if an autostash file exists and then\napplies it if it does. As a result, we should be unconditionally calling\napply_autostash().\n\nI guess that an odd edge-case that might come up is that a user may\nrun into a merge conflict, resolve the conflict, create a\n.git/MERGE_AUTOSTASH file and then, after they commit, the autostash\nfile will be applied. Currently, rebase behaves the same way so I think\nit's safe to leave the behaviour as is. I don't think this is a common\nuse-case that will come up anyway ;)\n\n> \n> >  \tif (squash) {\n> >  \t\tsquash_message(head_commit, remoteheads);\n> >  \t} else {\n> > @@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n> >  \t} else if (!strcmp(k, \"commit.gpgsign\")) {\n> >  \t\tsign_commit = git_config_bool(k, v) ? \"\" : NULL;\n> >  \t\treturn 0;\n> > +\t} else if (!strcmp(k, \"merge.autostash\")) {\n> > +\t\tautostash = git_config_bool(k, v);\n> > +\t\treturn 0;\n> >  \t}\n> >\n> >  \tstatus = fmt_merge_msg_config(k, v, cb);\n> > @@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n> >  \t\tfor (j = common; j; j = j->next)\n> >  \t\t\tcommit_list_insert(j->item, &reversed);\n> >\n> > +\t\tif (autostash)\n> > +\t\t\tperform_autostash(merge_autostash());\n> >  \t\thold_locked_index(&lock, LOCK_DIE_ON_ERROR);\n> >  \t\tclean = merge_recursive(&o, head,\n> >  \t\t\t\tremoteheads->item, reversed, &result);\n> > @@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n> >\n> >  \t\t/* Invoke 'git reset --merge' */\n> >  \t\tret = cmd_reset(nargc, nargv, prefix);\n> > +\t\tapply_autostash(merge_autostash());\n> \n> Again, this should be guarded by `if (autostash)`, methinks.\n> \n> >  \t\tgoto done;\n> >  \t}\n> >\n> > diff --git a/builtin/pull.c b/builtin/pull.c\n> > index d25ff13a60..ee186781ae 100644\n> > --- a/builtin/pull.c\n> > +++ b/builtin/pull.c\n> > @@ -183,7 +183,7 @@ static struct option pull_options[] = {\n> >  \t\tN_(\"verify that the named commit has a valid GPG signature\"),\n> >  \t\tPARSE_OPT_NOARG),\n> >  \tOPT_BOOL(0, \"autostash\", &opt_autostash,\n> > -\t\tN_(\"automatically stash/stash pop before and after rebase\")),\n> > +\t\tN_(\"automatically stash/stash pop before and after\")),\n> \n> Makes sense; this is now shared between the rebase and the merge modes.\n> \n> >  \tOPT_PASSTHRU_ARGV('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n> >  \t\tN_(\"merge strategy to use\"),\n> >  \t\t0),\n> > @@ -671,6 +671,10 @@ static int run_merge(void)\n> >  \targv_array_pushv(&args, opt_strategy_opts.argv);\n> >  \tif (opt_gpg_sign)\n> >  \t\targv_array_push(&args, opt_gpg_sign);\n> > +\tif (opt_autostash == 0)\n> > +\t\targv_array_push(&args, \"--no-autostash\");\n> > +\telse if (opt_autostash == 1)\n> > +\t\targv_array_push(&args, \"--autostash\");\n> \n> Or shorter:\n> \n> \targv_array_pushf(&args, \"%s-autostash\", opt_autostash ? \"-\" : \"--no\");\n> \n> Ah, but that would mishandle `-1`. I bet I will be puzzled by this\n> again. Maybe it would make sense to mention in a code comment that it\n> can be `-1` in which case we leave it to `rebase` to use the config\n> settings to determine whether or not to autostash.\n\nI copied this over from the rebase case. I'll add a comment there as\nwell.\n\n> \n> >  \tif (opt_allow_unrelated_histories > 0)\n> >  \t\targv_array_push(&args, \"--allow-unrelated-histories\");\n> >\n> > @@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n> >  \tif (get_oid(\"HEAD\", &orig_head))\n> >  \t\toidclr(&orig_head);\n> >\n> > -\tif (!opt_rebase && opt_autostash != -1)\n> > -\t\tdie(_(\"--[no-]autostash option is only valid with --rebase.\"));\n> > -\n> >  \tautostash = config_autostash;\n> >  \tif (opt_rebase) {\n> >  \t\tif (opt_autostash != -1)\n> > diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\n> > index cf4cc32fd0..75f162495a 100755\n> > --- a/t/t5520-pull.sh\n> > +++ b/t/t5520-pull.sh\n> > @@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '\n> >  \ttest_pull_autostash_fail --rebase --no-autostash\n> >  '\n> >\n> > -for i in --autostash --no-autostash\n> > -do\n> > -\ttest_expect_success \"pull $i (without --rebase) is illegal\" '\n> > -\t\ttest_must_fail git pull $i . copy 2>err &&\n> > -\t\ttest_i18ngrep \"only valid with --rebase\" err\n> > -\t'\n> > -done\n> > -\n> \n> Nice!\n> Dscho\n\nThanks,\n\nDenton\n\n> \n> >  test_expect_success 'pull.rebase' '\n> >  \tgit reset --hard before-rebase &&\n> >  \ttest_config pull.rebase true &&\n> > --\n> > 2.23.0.897.g0a19638b1e\n> >\n> >\n"},{"id":"388844","messageId":"20191224101214.GA2744494@generichostname","threadId":"52048","inReplyTo":"20191224100507.GB2743221@generichostname","subject":"Re: [RFC PATCH 7/7] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T10:12:14Z","receivedAt":"2019-12-24T10:12:19Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"On Tue, Dec 24, 2019 at 05:05:07AM -0500, Denton Liu wrote:\n> > >  \tOPT_PASSTHRU_ARGV('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n> > >  \t\tN_(\"merge strategy to use\"),\n> > >  \t\t0),\n> > > @@ -671,6 +671,10 @@ static int run_merge(void)\n> > >  \targv_array_pushv(&args, opt_strategy_opts.argv);\n> > >  \tif (opt_gpg_sign)\n> > >  \t\targv_array_push(&args, opt_gpg_sign);\n> > > +\tif (opt_autostash == 0)\n> > > +\t\targv_array_push(&args, \"--no-autostash\");\n> > > +\telse if (opt_autostash == 1)\n> > > +\t\targv_array_push(&args, \"--autostash\");\n> > \n> > Or shorter:\n> > \n> > \targv_array_pushf(&args, \"%s-autostash\", opt_autostash ? \"-\" : \"--no\");\n> > \n> > Ah, but that would mishandle `-1`. I bet I will be puzzled by this\n> > again. Maybe it would make sense to mention in a code comment that it\n> > can be `-1` in which case we leave it to `rebase` to use the config\n> > settings to determine whether or not to autostash.\n> \n> I copied this over from the rebase case. I'll add a comment there as\n> well.\n\nActually, on another thought, this happens in a couple of places in\nbuiltin/pull.c. I'm not sure that it's worth the noise of commenting it\nevery place where this happens. I dunno. I'll send a re-roll out and if\nsomeone else brings it up again, I'll add the comments in.\n"},{"id":"388846","messageId":"cover.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1571246693.git.liu.denton@gmail.com","subject":"[PATCH v2 00/17] merge: learn --autostash","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:04:57Z","receivedAt":"2019-12-24T11:05:51Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Alban reported[1] that he expected merge to have an --autostash option,\njust like rebase. Since there's not really any reason why merge can't\nhave it, let's implement it in this patchset.\n\nThe majority of this patchset is spent refactoring. This is so that the\nactual implementation in merge is done as simply as possible.\n\nChanges since v1:\n\n* Completely changed how the refactoring was done\n\n* More tests and documentation\n\n[1]: https://github.com/gitgitgadget/git/issues/394\n\nDenton Liu (17):\n  Makefile: alphabetically sort += lists\n  t7600: use test_write_lines()\n  sequencer: use file strbuf for read_oneliner()\n  sequencer: configurably warn on non-existent files\n  sequencer: make read_oneliner() extern\n  rebase: use read_oneliner()\n  sequencer: make apply_rebase() accept a path\n  rebase: use apply_autostash() from sequencer.c\n  rebase: generify reset_head()\n  reset: extract reset_head() from rebase\n  rebase: extract create_autostash()\n  rebase: generify create_autostash()\n  sequencer: extract perform_autostash() from rebase\n  sequencer: unlink autostash in apply_autostash()\n  merge: teach --autostash option\n  t5520: make test_pull_autostash() accept expect_parent_num\n  pull: pass --autostash to merge\n\n Documentation/config/merge.txt  |  10 ++\n Documentation/git-pull.txt      |   9 -\n Documentation/merge-options.txt |   8 +\n Makefile                        |  76 ++++----\n branch.c                        |   1 +\n builtin/commit.c                |   2 +\n builtin/merge.c                 |  17 ++\n builtin/pull.c                  |   9 +-\n builtin/rebase.c                | 295 ++++----------------------------\n path.c                          |   1 +\n path.h                          |   4 +-\n reset.c                         | 140 +++++++++++++++\n reset.h                         |  20 +++\n sequencer.c                     | 128 +++++++++-----\n sequencer.h                     |  15 ++\n t/t3033-merge-toplevel.sh       |  22 +++\n t/t5520-pull.sh                 |  57 ++++--\n t/t7600-merge.sh                |  87 ++++++++--\n 18 files changed, 522 insertions(+), 379 deletions(-)\n create mode 100644 reset.c\n create mode 100644 reset.h\n\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388847","messageId":"de79e27c17af27395401e0c2280d90a7d8bdef14.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 03/17] sequencer: use file strbuf for read_oneliner()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:00Z","receivedAt":"2019-12-24T11:05:51Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the original read_oneliner logic, we duplicated the logic for\nstrbuf_trim_trailing_newline() with one exception: instead of preventing\nbuffer accesses below index 0, it would prevent buffer accesses below\nindex `orig_len`. Although this is correct, it isn't worth having the\nduplicated logic around.\n\nAdd a second strbuf to which files are read and run\nstrbuf_trim_trailing_newline() directly on this strbuf then concatenate\nthis strbuf with the argument strbuf at the end of the function. The\nfunction's external behaviour is unchanged.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 24 +++++++++++++-----------\n 1 file changed, 13 insertions(+), 11 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 763ccbbc45..5ba8b4383f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -429,26 +429,28 @@ static int write_message(const void *buf, size_t len, const char *filename,\n static int read_oneliner(struct strbuf *buf,\n \tconst char *path, int skip_if_empty)\n {\n-\tint orig_len = buf->len;\n+\tint ret = 0;\n+\tstruct strbuf file_buf = STRBUF_INIT;\n \n \tif (!file_exists(path))\n \t\treturn 0;\n \n-\tif (strbuf_read_file(buf, path, 0) < 0) {\n+\tif (strbuf_read_file(&file_buf, path, 0) < 0) {\n \t\twarning_errno(_(\"could not read '%s'\"), path);\n-\t\treturn 0;\n+\t\tgoto done;\n \t}\n \n-\tif (buf->len > orig_len && buf->buf[buf->len - 1] == '\\n') {\n-\t\tif (--buf->len > orig_len && buf->buf[buf->len - 1] == '\\r')\n-\t\t\t--buf->len;\n-\t\tbuf->buf[buf->len] = '\\0';\n-\t}\n+\tstrbuf_trim_trailing_newline(&file_buf);\n \n-\tif (skip_if_empty && buf->len == orig_len)\n-\t\treturn 0;\n+\tif (skip_if_empty && !file_buf.len)\n+\t\tgoto done;\n \n-\treturn 1;\n+\tstrbuf_addbuf(buf, &file_buf);\n+\tret = 1;\n+\n+done:\n+\tstrbuf_release(&file_buf);\n+\treturn ret;\n }\n \n static struct tree *empty_tree(struct repository *r)\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388848","messageId":"2e0020b3e3d58d4be4fe9fabfb079d17b30cc994.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 02/17] t7600: use test_write_lines()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:04:59Z","receivedAt":"2019-12-24T11:05:51Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In t7600, we were rewriting `printf '%s\\n' ...` to create files from\nparameters, one per line. However, we already have a function that wraps\nthis for us: test_write_lines(). Rewrite these instances to use that\nfunction instead of open coding it.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7600-merge.sh | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 132608879a..4fa0ef8e3b 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -29,15 +29,15 @@ Testing basic merge operations/option parsing.\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-gpg.sh\n \n-printf '%s\\n' 1 2 3 4 5 6 7 8 9 >file\n-printf '%s\\n' '1 X' 2 3 4 5 6 7 8 9 >file.1\n-printf '%s\\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5\n-printf '%s\\n' 1 2 3 4 5 6 7 8 '9 X' >file.9\n-printf '%s\\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n-printf '%s\\n' '1 X' 2 3 4 5 6 7 8 9 >result.1\n-printf '%s\\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n-printf '%s\\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n-printf '%s\\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n+test_write_lines 1 2 3 4 5 6 7 8 9 >file\n+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1\n+test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5\n+test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9\n+test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1\n+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n+test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n \n create_merge_msgs () {\n \techo \"Merge tag 'c2'\" >msg.1-5 &&\n@@ -81,7 +81,7 @@ verify_head () {\n }\n \n verify_parents () {\n-\tprintf '%s\\n' \"$@\" >parents.expected &&\n+\ttest_write_lines \"$@\" >parents.expected &&\n \t>parents.actual &&\n \ti=1 &&\n \twhile test $i -le $#\n@@ -95,7 +95,7 @@ verify_parents () {\n }\n \n verify_mergeheads () {\n-\tprintf '%s\\n' \"$@\" >mergehead.expected &&\n+\ttest_write_lines \"$@\" >mergehead.expected &&\n \twhile read sha1 rest\n \tdo\n \t\tgit rev-parse $sha1\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388849","messageId":"31055cdac4a5bfc48cf6f18179f4e9bce9a04c8c.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 01/17] Makefile: alphabetically sort += lists","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:04:58Z","receivedAt":"2019-12-24T11:05:51Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"There are many += lists in the Makefile and, over time, they have gotten\nslightly out of order, alphabetically. Alphabetically sort all += lists\nto bring them back in order.\n\nNote that if we omit file prefixes, the lists aren't sorted in strictly\nalphabetical order (e.g. archive.o comes after archive-zip.o instead of\nbefore archive-tar.o). This is intentional because the purpose of\nmaintaining the sorted list is to ensure line insertions are\ndeterministic. As a result, we want to ensure that anybody can easily\nreproduce the sort order and that is very simple when we don't have to\ntreat the prefix specially.\n\nThis patch is best viewed with `--color-moved`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Makefile | 75 ++++++++++++++++++++++++++++----------------------------\n 1 file changed, 38 insertions(+), 37 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 42a061d3fb..de149947f6 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -604,12 +604,12 @@ unexport CDPATH\n SCRIPT_SH += git-bisect.sh\n SCRIPT_SH += git-difftool--helper.sh\n SCRIPT_SH += git-filter-branch.sh\n+SCRIPT_SH += git-legacy-stash.sh\n SCRIPT_SH += git-merge-octopus.sh\n SCRIPT_SH += git-merge-one-file.sh\n SCRIPT_SH += git-merge-resolve.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-quiltimport.sh\n-SCRIPT_SH += git-legacy-stash.sh\n SCRIPT_SH += git-request-pull.sh\n SCRIPT_SH += git-submodule.sh\n SCRIPT_SH += git-web--browse.sh\n@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh\n SCRIPT_LIB += git-mergetool--lib\n SCRIPT_LIB += git-parse-remote\n SCRIPT_LIB += git-rebase--preserve-merges\n-SCRIPT_LIB += git-sh-setup\n SCRIPT_LIB += git-sh-i18n\n+SCRIPT_LIB += git-sh-setup\n \n SCRIPT_PERL += git-add--interactive.perl\n SCRIPT_PERL += git-archimport.perl\n@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o\n PROGRAM_OBJS += fast-import.o\n PROGRAM_OBJS += http-backend.o\n PROGRAM_OBJS += imap-send.o\n+PROGRAM_OBJS += remote-testsvn.o\n PROGRAM_OBJS += sh-i18n--envsubst.o\n PROGRAM_OBJS += shell.o\n-PROGRAM_OBJS += remote-testsvn.o\n \n # Binary suffix, set to .exe for Windows builds\n X =\n@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o\n TEST_BUILTINS_OBJS += test-example-decorate.o\n TEST_BUILTINS_OBJS += test-genrandom.o\n TEST_BUILTINS_OBJS += test-genzeros.o\n+TEST_BUILTINS_OBJS += test-hash-speed.o\n TEST_BUILTINS_OBJS += test-hash.o\n TEST_BUILTINS_OBJS += test-hashmap.o\n-TEST_BUILTINS_OBJS += test-hash-speed.o\n TEST_BUILTINS_OBJS += test-index-version.o\n TEST_BUILTINS_OBJS += test-json-writer.o\n TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o\n@@ -736,8 +736,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o\n TEST_BUILTINS_OBJS += test-run-command.o\n TEST_BUILTINS_OBJS += test-scrap-cache-tree.o\n TEST_BUILTINS_OBJS += test-serve-v2.o\n-TEST_BUILTINS_OBJS += test-sha1.o\n TEST_BUILTINS_OBJS += test-sha1-array.o\n+TEST_BUILTINS_OBJS += test-sha1.o\n TEST_BUILTINS_OBJS += test-sha256.o\n TEST_BUILTINS_OBJS += test-sigchain.o\n TEST_BUILTINS_OBJS += test-strcmp-offset.o\n@@ -747,10 +747,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o\n TEST_BUILTINS_OBJS += test-subprocess.o\n TEST_BUILTINS_OBJS += test-trace2.o\n TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n-TEST_BUILTINS_OBJS += test-xml-encode.o\n TEST_BUILTINS_OBJS += test-wildmatch.o\n TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n TEST_BUILTINS_OBJS += test-write-cache.o\n+TEST_BUILTINS_OBJS += test-xml-encode.o\n \n # Do not add more tests here unless they have extra dependencies. Add\n # them in TEST_BUILTINS_OBJS above.\n@@ -787,10 +787,10 @@ OTHER_PROGRAMS = git$X\n \n # what test wrappers are needed and 'install' will install, in bindir\n BINDIR_PROGRAMS_NEED_X += git\n-BINDIR_PROGRAMS_NEED_X += git-upload-pack\n BINDIR_PROGRAMS_NEED_X += git-receive-pack\n-BINDIR_PROGRAMS_NEED_X += git-upload-archive\n BINDIR_PROGRAMS_NEED_X += git-shell\n+BINDIR_PROGRAMS_NEED_X += git-upload-archive\n+BINDIR_PROGRAMS_NEED_X += git-upload-pack\n \n BINDIR_PROGRAMS_NO_X += git-cvsserver\n \n@@ -829,9 +829,9 @@ LIB_OBJS += advice.o\n LIB_OBJS += alias.o\n LIB_OBJS += alloc.o\n LIB_OBJS += apply.o\n-LIB_OBJS += archive.o\n LIB_OBJS += archive-tar.o\n LIB_OBJS += archive-zip.o\n+LIB_OBJS += archive.o\n LIB_OBJS += argv-array.o\n LIB_OBJS += attr.o\n LIB_OBJS += base85.o\n@@ -847,9 +847,9 @@ LIB_OBJS += checkout.o\n LIB_OBJS += color.o\n LIB_OBJS += column.o\n LIB_OBJS += combine-diff.o\n-LIB_OBJS += commit.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n+LIB_OBJS += commit.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\n LIB_OBJS += config.o\n@@ -863,17 +863,17 @@ LIB_OBJS += ctype.o\n LIB_OBJS += date.o\n LIB_OBJS += decorate.o\n LIB_OBJS += delta-islands.o\n+LIB_OBJS += diff-delta.o\n+LIB_OBJS += diff-lib.o\n+LIB_OBJS += diff-no-index.o\n+LIB_OBJS += diff.o\n LIB_OBJS += diffcore-break.o\n LIB_OBJS += diffcore-delta.o\n LIB_OBJS += diffcore-order.o\n LIB_OBJS += diffcore-pickaxe.o\n LIB_OBJS += diffcore-rename.o\n-LIB_OBJS += diff-delta.o\n-LIB_OBJS += diff-lib.o\n-LIB_OBJS += diff-no-index.o\n-LIB_OBJS += diff.o\n-LIB_OBJS += dir.o\n LIB_OBJS += dir-iterator.o\n+LIB_OBJS += dir.o\n LIB_OBJS += editor.o\n LIB_OBJS += entry.o\n LIB_OBJS += environment.o\n@@ -891,7 +891,6 @@ LIB_OBJS += gpg-interface.o\n LIB_OBJS += graph.o\n LIB_OBJS += grep.o\n LIB_OBJS += hashmap.o\n-LIB_OBJS += linear-assignment.o\n LIB_OBJS += help.o\n LIB_OBJS += hex.o\n LIB_OBJS += ident.o\n@@ -901,9 +900,10 @@ LIB_OBJS += kwset.o\n LIB_OBJS += levenshtein.o\n LIB_OBJS += line-log.o\n LIB_OBJS += line-range.o\n-LIB_OBJS += list-objects.o\n-LIB_OBJS += list-objects-filter.o\n+LIB_OBJS += linear-assignment.o\n LIB_OBJS += list-objects-filter-options.o\n+LIB_OBJS += list-objects-filter.o\n+LIB_OBJS += list-objects.o\n LIB_OBJS += ll-merge.o\n LIB_OBJS += lockfile.o\n LIB_OBJS += log-tree.o\n@@ -912,31 +912,31 @@ LIB_OBJS += mailinfo.o\n LIB_OBJS += mailmap.o\n LIB_OBJS += match-trees.o\n LIB_OBJS += mem-pool.o\n-LIB_OBJS += merge.o\n LIB_OBJS += merge-blobs.o\n LIB_OBJS += merge-recursive.o\n+LIB_OBJS += merge.o\n LIB_OBJS += mergesort.o\n LIB_OBJS += midx.o\n LIB_OBJS += name-hash.o\n LIB_OBJS += negotiator/default.o\n LIB_OBJS += negotiator/skipping.o\n-LIB_OBJS += notes.o\n LIB_OBJS += notes-cache.o\n LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\n+LIB_OBJS += notes.o\n LIB_OBJS += object.o\n LIB_OBJS += oidmap.o\n LIB_OBJS += oidset.o\n-LIB_OBJS += packfile.o\n-LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-bitmap-write.o\n+LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-objects.o\n LIB_OBJS += pack-revindex.o\n LIB_OBJS += pack-write.o\n+LIB_OBJS += packfile.o\n LIB_OBJS += pager.o\n-LIB_OBJS += parse-options.o\n LIB_OBJS += parse-options-cb.o\n+LIB_OBJS += parse-options.o\n LIB_OBJS += patch-delta.o\n LIB_OBJS += patch-ids.o\n LIB_OBJS += path.o\n@@ -954,6 +954,7 @@ LIB_OBJS += range-diff.o\n LIB_OBJS += reachable.o\n LIB_OBJS += read-cache.o\n LIB_OBJS += rebase-interactive.o\n+LIB_OBJS += ref-filter.o\n LIB_OBJS += reflog-walk.o\n LIB_OBJS += refs.o\n LIB_OBJS += refs/files-backend.o\n@@ -961,7 +962,6 @@ LIB_OBJS += refs/iterator.o\n LIB_OBJS += refs/packed-backend.o\n LIB_OBJS += refs/ref-cache.o\n LIB_OBJS += refspec.o\n-LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace-object.o\n LIB_OBJS += repo-settings.o\n@@ -976,8 +976,8 @@ LIB_OBJS += serve.o\n LIB_OBJS += server-info.o\n LIB_OBJS += setup.o\n LIB_OBJS += sha1-array.o\n-LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1-file.o\n+LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1-name.o\n LIB_OBJS += shallow.o\n LIB_OBJS += sideband.o\n@@ -987,9 +987,9 @@ LIB_OBJS += stable-qsort.o\n LIB_OBJS += strbuf.o\n LIB_OBJS += streaming.o\n LIB_OBJS += string-list.o\n-LIB_OBJS += submodule.o\n-LIB_OBJS += submodule-config.o\n LIB_OBJS += sub-process.o\n+LIB_OBJS += submodule-config.o\n+LIB_OBJS += submodule.o\n LIB_OBJS += symlinks.o\n LIB_OBJS += tag.o\n LIB_OBJS += tempfile.o\n@@ -1008,11 +1008,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o\n LIB_OBJS += trace2/tr2_tgt_perf.o\n LIB_OBJS += trace2/tr2_tls.o\n LIB_OBJS += trailer.o\n-LIB_OBJS += transport.o\n LIB_OBJS += transport-helper.o\n+LIB_OBJS += transport.o\n LIB_OBJS += tree-diff.o\n-LIB_OBJS += tree.o\n LIB_OBJS += tree-walk.o\n+LIB_OBJS += tree.o\n LIB_OBJS += unpack-trees.o\n LIB_OBJS += upload-pack.o\n LIB_OBJS += url.o\n@@ -1052,9 +1052,9 @@ BUILTIN_OBJS += builtin/checkout.o\n BUILTIN_OBJS += builtin/clean.o\n BUILTIN_OBJS += builtin/clone.o\n BUILTIN_OBJS += builtin/column.o\n+BUILTIN_OBJS += builtin/commit-graph.o\n BUILTIN_OBJS += builtin/commit-tree.o\n BUILTIN_OBJS += builtin/commit.o\n-BUILTIN_OBJS += builtin/commit-graph.o\n BUILTIN_OBJS += builtin/config.o\n BUILTIN_OBJS += builtin/count-objects.o\n BUILTIN_OBJS += builtin/credential.o\n@@ -1085,13 +1085,13 @@ BUILTIN_OBJS += builtin/ls-remote.o\n BUILTIN_OBJS += builtin/ls-tree.o\n BUILTIN_OBJS += builtin/mailinfo.o\n BUILTIN_OBJS += builtin/mailsplit.o\n-BUILTIN_OBJS += builtin/merge.o\n BUILTIN_OBJS += builtin/merge-base.o\n BUILTIN_OBJS += builtin/merge-file.o\n BUILTIN_OBJS += builtin/merge-index.o\n BUILTIN_OBJS += builtin/merge-ours.o\n BUILTIN_OBJS += builtin/merge-recursive.o\n BUILTIN_OBJS += builtin/merge-tree.o\n+BUILTIN_OBJS += builtin/merge.o\n BUILTIN_OBJS += builtin/mktag.o\n BUILTIN_OBJS += builtin/mktree.o\n BUILTIN_OBJS += builtin/multi-pack-index.o\n@@ -1111,9 +1111,9 @@ BUILTIN_OBJS += builtin/read-tree.o\n BUILTIN_OBJS += builtin/rebase.o\n BUILTIN_OBJS += builtin/receive-pack.o\n BUILTIN_OBJS += builtin/reflog.o\n-BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/remote-ext.o\n BUILTIN_OBJS += builtin/remote-fd.o\n+BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/rerere.o\n@@ -2328,16 +2328,16 @@ reconfigure config.mak.autogen: config.status\n endif\n \n XDIFF_OBJS += xdiff/xdiffi.o\n-XDIFF_OBJS += xdiff/xprepare.o\n-XDIFF_OBJS += xdiff/xutils.o\n XDIFF_OBJS += xdiff/xemit.o\n+XDIFF_OBJS += xdiff/xhistogram.o\n XDIFF_OBJS += xdiff/xmerge.o\n XDIFF_OBJS += xdiff/xpatience.o\n-XDIFF_OBJS += xdiff/xhistogram.o\n+XDIFF_OBJS += xdiff/xprepare.o\n+XDIFF_OBJS += xdiff/xutils.o\n \n+VCSSVN_OBJS += vcs-svn/fast_export.o\n VCSSVN_OBJS += vcs-svn/line_buffer.o\n VCSSVN_OBJS += vcs-svn/sliding_window.o\n-VCSSVN_OBJS += vcs-svn/fast_export.o\n VCSSVN_OBJS += vcs-svn/svndiff.o\n VCSSVN_OBJS += vcs-svn/svndump.o\n \n@@ -3146,9 +3146,10 @@ endif\n #\n ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS)\n ALL_COMMANDS += git\n+ALL_COMMANDS += git-citool\n+ALL_COMMANDS += git-gui\n ALL_COMMANDS += gitk\n ALL_COMMANDS += gitweb\n-ALL_COMMANDS += git-gui git-citool\n \n .PHONY: check-docs\n check-docs::\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388850","messageId":"06c5bd48d767ff5dce45bebb58505888745af861.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 06/17] rebase: use read_oneliner()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:03Z","receivedAt":"2019-12-24T11:05:56Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Since in sequencer.c, read_one() basically duplicates the functionality\nof read_oneliner(), reduce code duplication by replacing read_one() with\nread_oneliner().\n\nThis was done with the following Coccinelle script:\n\n\t@@\n\texpression a, b;\n\t@@\n\t- read_one(a, b)\n\t+ !read_oneliner(b, a, 0, 1)\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 29 +++++++++--------------------\n 1 file changed, 9 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex ddf33bc9d4..68bedccb95 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -582,15 +582,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o\n \treturn path.buf;\n }\n \n-/* Read one file, then strip line endings */\n-static int read_one(const char *path, struct strbuf *buf)\n-{\n-\tif (strbuf_read_file(buf, path, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'\"), path);\n-\tstrbuf_trim_trailing_newline(buf);\n-\treturn 0;\n-}\n-\n /* Initialize the rebase options from the state directory. */\n static int read_basic_state(struct rebase_options *opts)\n {\n@@ -598,8 +589,8 @@ static int read_basic_state(struct rebase_options *opts)\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct object_id oid;\n \n-\tif (read_one(state_dir_path(\"head-name\", opts), &head_name) ||\n-\t    read_one(state_dir_path(\"onto\", opts), &buf))\n+\tif (!read_oneliner(&head_name, state_dir_path(\"head-name\", opts), 0, 1) ||\n+\t    !read_oneliner(&buf, state_dir_path(\"onto\", opts), 0, 1))\n \t\treturn -1;\n \topts->head_name = starts_with(head_name.buf, \"refs/\") ?\n \t\txstrdup(head_name.buf) : NULL;\n@@ -615,9 +606,9 @@ static int read_basic_state(struct rebase_options *opts)\n \t */\n \tstrbuf_reset(&buf);\n \tif (file_exists(state_dir_path(\"orig-head\", opts))) {\n-\t\tif (read_one(state_dir_path(\"orig-head\", opts), &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"orig-head\", opts), 0, 1))\n \t\t\treturn -1;\n-\t} else if (read_one(state_dir_path(\"head\", opts), &buf))\n+\t} else if (!read_oneliner(&buf, state_dir_path(\"head\", opts), 0, 1))\n \t\treturn -1;\n \tif (get_oid(buf.buf, &opts->orig_head))\n \t\treturn error(_(\"invalid orig-head: '%s'\"), buf.buf);\n@@ -637,8 +628,7 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tif (file_exists(state_dir_path(\"allow_rerere_autoupdate\", opts))) {\n \t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"allow_rerere_autoupdate\", opts),\n-\t\t\t    &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"allow_rerere_autoupdate\", opts), 0, 1))\n \t\t\treturn -1;\n \t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n \t\t\topts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;\n@@ -651,8 +641,7 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tif (file_exists(state_dir_path(\"gpg_sign_opt\", opts))) {\n \t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"gpg_sign_opt\", opts),\n-\t\t\t    &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"gpg_sign_opt\", opts), 0, 1))\n \t\t\treturn -1;\n \t\tfree(opts->gpg_sign_opt);\n \t\topts->gpg_sign_opt = xstrdup(buf.buf);\n@@ -660,7 +649,7 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tif (file_exists(state_dir_path(\"strategy\", opts))) {\n \t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"strategy\", opts), &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy\", opts), 0, 1))\n \t\t\treturn -1;\n \t\tfree(opts->strategy);\n \t\topts->strategy = xstrdup(buf.buf);\n@@ -668,7 +657,7 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tif (file_exists(state_dir_path(\"strategy_opts\", opts))) {\n \t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"strategy_opts\", opts), &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy_opts\", opts), 0, 1))\n \t\t\treturn -1;\n \t\tfree(opts->strategy_opts);\n \t\topts->strategy_opts = xstrdup(buf.buf);\n@@ -720,7 +709,7 @@ static int apply_autostash(struct rebase_options *opts)\n \tif (!file_exists(path))\n \t\treturn 0;\n \n-\tif (read_one(path, &autostash))\n+\tif (!read_oneliner(&autostash, path, 0, 1))\n \t\treturn error(_(\"Could not read '%s'\"), path);\n \t/* Ensure that the hash is not mistaken for a number */\n \tstrbuf_addstr(&autostash, \"^0\");\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388851","messageId":"fd547aab491b24a47f683d717c6b77255ac16901.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 04/17] sequencer: configurably warn on non-existent files","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:01Z","receivedAt":"2019-12-24T11:05:56Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we plan on externing read_oneliner(). Future users of\nread_oneliner() will want the ability to output warnings in the event\nthat the `path` doesn't exist. Introduce the `warn_nonexistence`\nparameter which, if true, would issue a warning when a file doesn't\nexist by skipping the `!file_exists()` check and letting\n`strbuf_read_file()` handle that case.\n\nMechanically replace uses of read_oneliner() in sequencer.c with the\nfollowing spatch so that current behaviour is maintained:\n\n\t@@\n\texpression a, b, c;\n\t@@\n\t  read_oneliner(a, b, c\n\t+ , 0\n\t  )\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 32 ++++++++++++++++----------------\n 1 file changed, 16 insertions(+), 16 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 5ba8b4383f..103cea1460 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -426,13 +426,13 @@ static int write_message(const void *buf, size_t len, const char *filename,\n  *\n  * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n  */\n-static int read_oneliner(struct strbuf *buf,\n-\tconst char *path, int skip_if_empty)\n+static int read_oneliner(struct strbuf *buf, const char *path,\n+\t\t\t int skip_if_empty, int warn_nonexistence)\n {\n \tint ret = 0;\n \tstruct strbuf file_buf = STRBUF_INIT;\n \n-\tif (!file_exists(path))\n+\tif (!warn_nonexistence && !file_exists(path))\n \t\treturn 0;\n \n \tif (strbuf_read_file(&file_buf, path, 0) < 0) {\n@@ -2558,10 +2558,10 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)\n static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)\n {\n \tstrbuf_reset(buf);\n-\tif (!read_oneliner(buf, rebase_path_strategy(), 0))\n+\tif (!read_oneliner(buf, rebase_path_strategy(), 0, 0))\n \t\treturn;\n \topts->strategy = strbuf_detach(buf, NULL);\n-\tif (!read_oneliner(buf, rebase_path_strategy_opts(), 0))\n+\tif (!read_oneliner(buf, rebase_path_strategy_opts(), 0, 0))\n \t\treturn;\n \n \tparse_strategy_opts(opts, buf->buf);\n@@ -2572,7 +2572,7 @@ static int read_populate_opts(struct replay_opts *opts)\n \tif (is_rebase_i(opts)) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {\n+\t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1, 0)) {\n \t\t\tif (!starts_with(buf.buf, \"-S\"))\n \t\t\t\tstrbuf_reset(&buf);\n \t\t\telse {\n@@ -2582,7 +2582,7 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t\tstrbuf_reset(&buf);\n \t\t}\n \n-\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {\n+\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1, 0)) {\n \t\t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n \t\t\t\topts->allow_rerere_auto = RERERE_AUTOUPDATE;\n \t\t\telse if (!strcmp(buf.buf, \"--no-rerere-autoupdate\"))\n@@ -2618,7 +2618,7 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\tstrbuf_release(&buf);\n \n \t\tif (read_oneliner(&opts->current_fixups,\n-\t\t\t\t  rebase_path_current_fixups(), 1)) {\n+\t\t\t\t  rebase_path_current_fixups(), 1, 0)) {\n \t\t\tconst char *p = opts->current_fixups.buf;\n \t\t\topts->current_fixup_count = 1;\n \t\t\twhile ((p = strchr(p, '\\n'))) {\n@@ -2627,7 +2627,7 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t\t}\n \t\t}\n \n-\t\tif (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {\n+\t\tif (read_oneliner(&buf, rebase_path_squash_onto(), 0, 0)) {\n \t\t\tif (get_oid_hex(buf.buf, &opts->squash_onto) < 0)\n \t\t\t\treturn error(_(\"unusable squash-onto\"));\n \t\t\topts->have_squash_onto = 1;\n@@ -3759,7 +3759,7 @@ static int apply_autostash(struct replay_opts *opts)\n \tstruct child_process child = CHILD_PROCESS_INIT;\n \tint ret = 0;\n \n-\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {\n+\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1, 0)) {\n \t\tstrbuf_release(&stash_sha1);\n \t\treturn 0;\n \t}\n@@ -4093,7 +4093,7 @@ static int pick_commits(struct repository *r,\n \t\tif (todo_list->current < todo_list->nr)\n \t\t\treturn 0;\n \n-\t\tif (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&\n+\t\tif (read_oneliner(&head_ref, rebase_path_head_name(), 0, 0) &&\n \t\t\t\tstarts_with(head_ref.buf, \"refs/\")) {\n \t\t\tconst char *msg;\n \t\t\tstruct object_id head, orig;\n@@ -4106,13 +4106,13 @@ static int pick_commits(struct repository *r,\n \t\t\t\tstrbuf_release(&buf);\n \t\t\t\treturn res;\n \t\t\t}\n-\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||\n+\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0, 0) ||\n \t\t\t\t\tget_oid_hex(buf.buf, &orig)) {\n \t\t\t\tres = error(_(\"could not read orig-head\"));\n \t\t\t\tgoto cleanup_head_ref;\n \t\t\t}\n \t\t\tstrbuf_reset(&buf);\n-\t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0)) {\n+\t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0, 0)) {\n \t\t\t\tres = error(_(\"could not read 'onto'\"));\n \t\t\t\tgoto cleanup_head_ref;\n \t\t\t}\n@@ -4145,7 +4145,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\tDIFF_FORMAT_DIFFSTAT;\n \t\t\tlog_tree_opt.disable_stdin = 1;\n \n-\t\t\tif (read_oneliner(&buf, rebase_path_orig_head(), 0) &&\n+\t\t\tif (read_oneliner(&buf, rebase_path_orig_head(), 0, 0) &&\n \t\t\t    !get_oid(buf.buf, &orig) &&\n \t\t\t    !get_oid(\"HEAD\", &head)) {\n \t\t\t\tdiff_tree_oid(&orig, &head, \"\",\n@@ -4230,7 +4230,7 @@ static int commit_staged_changes(struct repository *r,\n \n \t\tif (get_oid(\"HEAD\", &head))\n \t\t\treturn error(_(\"cannot amend non-existing commit\"));\n-\t\tif (!read_oneliner(&rev, rebase_path_amend(), 0))\n+\t\tif (!read_oneliner(&rev, rebase_path_amend(), 0, 0))\n \t\t\treturn error(_(\"invalid file: '%s'\"), rebase_path_amend());\n \t\tif (get_oid_hex(rev.buf, &to_amend))\n \t\t\treturn error(_(\"invalid contents: '%s'\"),\n@@ -4391,7 +4391,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tstruct object_id oid;\n \n-\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&\n+\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(), 1, 0) &&\n \t\t    !get_oid_committish(buf.buf, &oid))\n \t\t\trecord_in_rewritten(&oid, peek_command(&todo_list, 0));\n \t\tstrbuf_release(&buf);\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388854","messageId":"12c37d11babf2df496066d113328242584e405f0.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 07/17] sequencer: make apply_rebase() accept a path","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:04Z","receivedAt":"2019-12-24T11:05:57Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In order to make apply_rebase() more generic for future extraction, make\nit accept a `path` argument so that the location from where to read the\nreference to the autostash commit can be customized. Remove the `opts`\nargument since it was unused before anyway.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 14 +++++++-------\n 1 file changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 9ef9424f88..e1b7cb461c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3744,13 +3744,13 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n-static int apply_autostash(struct replay_opts *opts)\n+static int apply_autostash(const char *path)\n {\n \tstruct strbuf stash_sha1 = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\n \tint ret = 0;\n \n-\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1, 0)) {\n+\tif (!read_oneliner(&stash_sha1, path, 1, 0)) {\n \t\tstrbuf_release(&stash_sha1);\n \t\treturn 0;\n \t}\n@@ -3856,7 +3856,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,\n \t\treturn error(_(\"%s: not a valid OID\"), orig_head);\n \n \tif (run_git_checkout(r, opts, oid_to_hex(onto), action)) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \t\treturn error(_(\"could not detach HEAD\"));\n \t}\n@@ -4172,7 +4172,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\trun_command(&hook);\n \t\t\t}\n \t\t}\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \n \t\tif (!opts->quiet) {\n \t\t\tif (!opts->verbose)\n@@ -5213,7 +5213,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\ttodo_list_add_exec_commands(todo_list, commands);\n \n \tif (count_commands(todo_list) == 0) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \n \t\treturn error(_(\"nothing to do\"));\n@@ -5224,12 +5224,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (res == -1)\n \t\treturn -1;\n \telse if (res == -2) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \n \t\treturn -1;\n \t} else if (res == -3) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \t\ttodo_list_release(&new_todo);\n \n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388852","messageId":"893a2f9825f8a9640b747048f1c7de37c8c12cce.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 08/17] rebase: use apply_autostash() from sequencer.c","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:05Z","receivedAt":"2019-12-24T11:05:58Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"The apply_autostash() function in builtin/rebase.c is similar enough to\nthe apply_autostash() function in sequencer.c that they are almost\ninterchangeable. Make the sequencer.c version extern and use it in\nrebase.\n\nThe rebase version was introduced in 6defce2b02 (builtin rebase: support\n`--autostash` option, 2018-09-04) as part of the shell to C conversion.\nIt opted to duplicate the function because, at the time, there was\nanother in-progress project converting interactive rebase from shell to\nC as well and they did not want to clash with them by refactoring\nsequencer.c version of apply_autostash(). Since both efforts are long\ndone, we can freely combine them together now.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 49 ++----------------------------------------------\n sequencer.c      |  2 +-\n sequencer.h      |  2 ++\n 3 files changed, 5 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 68bedccb95..07674a16d8 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -700,51 +700,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)\n \treturn 0;\n }\n \n-static int apply_autostash(struct rebase_options *opts)\n-{\n-\tconst char *path = state_dir_path(\"autostash\", opts);\n-\tstruct strbuf autostash = STRBUF_INIT;\n-\tstruct child_process stash_apply = CHILD_PROCESS_INIT;\n-\n-\tif (!file_exists(path))\n-\t\treturn 0;\n-\n-\tif (!read_oneliner(&autostash, path, 0, 1))\n-\t\treturn error(_(\"Could not read '%s'\"), path);\n-\t/* Ensure that the hash is not mistaken for a number */\n-\tstrbuf_addstr(&autostash, \"^0\");\n-\targv_array_pushl(&stash_apply.args,\n-\t\t\t \"stash\", \"apply\", autostash.buf, NULL);\n-\tstash_apply.git_cmd = 1;\n-\tstash_apply.no_stderr = stash_apply.no_stdout =\n-\t\tstash_apply.no_stdin = 1;\n-\tif (!run_command(&stash_apply))\n-\t\tprintf(_(\"Applied autostash.\\n\"));\n-\telse {\n-\t\tstruct argv_array args = ARGV_ARRAY_INIT;\n-\t\tint res = 0;\n-\n-\t\targv_array_pushl(&args,\n-\t\t\t\t \"stash\", \"store\", \"-m\", \"autostash\", \"-q\",\n-\t\t\t\t autostash.buf, NULL);\n-\t\tif (run_command_v_opt(args.argv, RUN_GIT_CMD))\n-\t\t\tres = error(_(\"Cannot store %s\"), autostash.buf);\n-\t\targv_array_clear(&args);\n-\t\tstrbuf_release(&autostash);\n-\t\tif (res)\n-\t\t\treturn res;\n-\n-\t\tfprintf(stderr,\n-\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n-\t\t\t  \"Your changes are safe in the stash.\\n\"\n-\t\t\t  \"You can run \\\"git stash pop\\\" or \\\"git stash drop\\\" \"\n-\t\t\t  \"at any time.\\n\"));\n-\t}\n-\n-\tstrbuf_release(&autostash);\n-\treturn 0;\n-}\n-\n static int finish_rebase(struct rebase_options *opts)\n {\n \tstruct strbuf dir = STRBUF_INIT;\n@@ -752,7 +707,7 @@ static int finish_rebase(struct rebase_options *opts)\n \tint ret = 0;\n \n \tdelete_ref(NULL, \"REBASE_HEAD\", NULL, REF_NO_DEREF);\n-\tapply_autostash(opts);\n+\tapply_autostash(state_dir_path(\"autostash\", opts));\n \tclose_object_store(the_repository->objects);\n \t/*\n \t * We ignore errors in 'gc --auto', since the\n@@ -1206,7 +1161,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)\n \t} else if (status == 2) {\n \t\tstruct strbuf dir = STRBUF_INIT;\n \n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(state_dir_path(\"autostash\", opts));\n \t\tstrbuf_addstr(&dir, opts->state_dir);\n \t\tremove_dir_recursively(&dir, 0);\n \t\tstrbuf_release(&dir);\ndiff --git a/sequencer.c b/sequencer.c\nindex e1b7cb461c..98e08dbbe0 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3744,7 +3744,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n-static int apply_autostash(const char *path)\n+int apply_autostash(const char *path)\n {\n \tstruct strbuf stash_sha1 = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\ndiff --git a/sequencer.h b/sequencer.h\nindex 56cea80dd8..ecef2c144c 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -195,6 +195,8 @@ void commit_post_rewrite(struct repository *r,\n int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,\n \t\t\t\t const char *commit);\n \n+int apply_autostash(const char *path);\n+\n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n void print_commit_summary(struct repository *repo,\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388853","messageId":"819fb2203ba59df1fc446167d5648c6aae46ad17.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 05/17] sequencer: make read_oneliner() extern","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:02Z","receivedAt":"2019-12-24T11:05:59Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"The function read_oneliner() is a generally useful util function.\nInstead of hiding it as a static function within sequencer.c, extern it\nso that it can be reused by others.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 13 ++-----------\n sequencer.h | 11 +++++++++++\n 2 files changed, 13 insertions(+), 11 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 103cea1460..9ef9424f88 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -417,17 +417,8 @@ static int write_message(const void *buf, size_t len, const char *filename,\n \treturn 0;\n }\n \n-/*\n- * Reads a file that was presumably written by a shell script, i.e. with an\n- * end-of-line marker that needs to be stripped.\n- *\n- * Note that only the last end-of-line marker is stripped, consistent with the\n- * behavior of \"$(cat path)\" in a shell script.\n- *\n- * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n- */\n-static int read_oneliner(struct strbuf *buf, const char *path,\n-\t\t\t int skip_if_empty, int warn_nonexistence)\n+int read_oneliner(struct strbuf *buf, const char *path,\n+\t\t  int skip_if_empty, int warn_nonexistence)\n {\n \tint ret = 0;\n \tstruct strbuf file_buf = STRBUF_INIT;\ndiff --git a/sequencer.h b/sequencer.h\nindex e9a0e03ea2..56cea80dd8 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -202,6 +202,17 @@ void print_commit_summary(struct repository *repo,\n \t\t\t  const struct object_id *oid,\n \t\t\t  unsigned int flags);\n \n+/*\n+ * Reads a file that was presumably written by a shell script, i.e. with an\n+ * end-of-line marker that needs to be stripped.\n+ *\n+ * Note that only the last end-of-line marker is stripped, consistent with the\n+ * behavior of \"$(cat path)\" in a shell script.\n+ *\n+ * Returns 1 if the file was read, 0 if it could not be read.\n+ */\n+int read_oneliner(struct strbuf *buf, const char *path,\n+\t\t  int skip_if_empty, int warn_nonexistence);\n int read_author_script(const char *path, char **name, char **email, char **date,\n \t\t       int allow_missing);\n void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388855","messageId":"981969d23024e1b49c9efbcffe4f05a8c36bcd54.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 10/17] reset: extract reset_head() from rebase","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:07Z","receivedAt":"2019-12-24T11:06:02Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Continue the process of lib-ifying the autostash code. In a future\ncommit, this will be used to implement `--autostash` in other builtins.\n\nThis patch is best viewed with `--color-moved`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Makefile         |   1 +\n builtin/rebase.c | 139 +---------------------------------------------\n reset.c          | 140 +++++++++++++++++++++++++++++++++++++++++++++++\n reset.h          |  20 +++++++\n 4 files changed, 162 insertions(+), 138 deletions(-)\n create mode 100644 reset.c\n create mode 100644 reset.h\n\ndiff --git a/Makefile b/Makefile\nindex de149947f6..8d3aa4153e 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -967,6 +967,7 @@ LIB_OBJS += replace-object.o\n LIB_OBJS += repo-settings.o\n LIB_OBJS += repository.o\n LIB_OBJS += rerere.o\n+LIB_OBJS += reset.o\n LIB_OBJS += resolve-undo.o\n LIB_OBJS += revision.o\n LIB_OBJS += run-command.o\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 4e3cb0852d..a6d7527f4c 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -27,6 +27,7 @@\n #include \"branch.h\"\n #include \"sequencer.h\"\n #include \"rebase-interactive.h\"\n+#include \"reset.h\"\n \n #define DEFAULT_REFLOG_ACTION \"rebase\"\n \n@@ -754,144 +755,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)\n \t}\n }\n \n-#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n-\n-#define RESET_HEAD_DETACH (1<<0)\n-#define RESET_HEAD_HARD (1<<1)\n-#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n-#define RESET_HEAD_REFS_ONLY (1<<3)\n-#define RESET_ORIG_HEAD (1<<4)\n-\n-static int reset_head(struct repository *r, struct object_id *oid, const char *action,\n-\t\t      const char *switch_to_branch, unsigned flags,\n-\t\t      const char *reflog_orig_head, const char *reflog_head,\n-\t\t      const char *default_reflog_action)\n-{\n-\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n-\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n-\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n-\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n-\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n-\tstruct object_id head_oid;\n-\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n-\tstruct lock_file lock = LOCK_INIT;\n-\tstruct unpack_trees_options unpack_tree_opts;\n-\tstruct tree *tree;\n-\tconst char *reflog_action;\n-\tstruct strbuf msg = STRBUF_INIT;\n-\tsize_t prefix_len;\n-\tstruct object_id *orig = NULL, oid_orig,\n-\t\t*old_orig = NULL, oid_old_orig;\n-\tint ret = 0, nr = 0;\n-\n-\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n-\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n-\n-\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n-\t\tret = -1;\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n-\t\tret = error(_(\"could not determine HEAD revision\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!oid)\n-\t\toid = &head_oid;\n-\n-\tif (refs_only)\n-\t\tgoto reset_head_refs;\n-\n-\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n-\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n-\tunpack_tree_opts.head_idx = 1;\n-\tunpack_tree_opts.src_index = r->index;\n-\tunpack_tree_opts.dst_index = r->index;\n-\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n-\tunpack_tree_opts.update = 1;\n-\tunpack_tree_opts.merge = 1;\n-\tif (!detach_head)\n-\t\tunpack_tree_opts.reset = 1;\n-\n-\tif (repo_read_index_unmerged(r) < 0) {\n-\t\tret = error(_(\"could not read index\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n-\t\tret = error(_(\"failed to find tree of %s\"),\n-\t\t\t    oid_to_hex(&head_oid));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n-\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n-\t\tret = -1;\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\ttree = parse_tree_indirect(oid);\n-\tprime_cache_tree(r, r->index, tree);\n-\n-\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n-\t\tret = error(_(\"could not write index\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-reset_head_refs:\n-\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n-\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n-\tprefix_len = msg.len;\n-\n-\tif (update_orig_head) {\n-\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n-\t\t\told_orig = &oid_old_orig;\n-\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n-\t\t\torig = &oid_orig;\n-\t\t\tif (!reflog_orig_head) {\n-\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n-\t\t\t\treflog_orig_head = msg.buf;\n-\t\t\t}\n-\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n-\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n-\t\t} else if (old_orig)\n-\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n-\t}\n-\n-\tif (!reflog_head) {\n-\t\tstrbuf_setlen(&msg, prefix_len);\n-\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n-\t\treflog_head = msg.buf;\n-\t}\n-\tif (!switch_to_branch)\n-\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n-\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n-\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n-\telse {\n-\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n-\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n-\t\tif (!ret)\n-\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n-\t\t\t\t\t    reflog_head);\n-\t}\n-\tif (run_hook)\n-\t\trun_hook_le(NULL, \"post-checkout\",\n-\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n-\t\t\t    oid_to_hex(oid), \"1\", NULL);\n-\n-leave_reset_head:\n-\tstrbuf_release(&msg);\n-\trollback_lock_file(&lock);\n-\twhile (nr)\n-\t\tfree((void *)desc[--nr].buffer);\n-\treturn ret;\n-}\n-\n static int move_to_original_branch(struct rebase_options *opts)\n {\n \tstruct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;\ndiff --git a/reset.c b/reset.c\nnew file mode 100644\nindex 0000000000..79b683bffa\n--- /dev/null\n+++ b/reset.c\n@@ -0,0 +1,140 @@\n+#include \"git-compat-util.h\"\n+#include \"cache-tree.h\"\n+#include \"lockfile.h\"\n+#include \"refs.h\"\n+#include \"reset.h\"\n+#include \"run-command.h\"\n+#include \"tree-walk.h\"\n+#include \"tree.h\"\n+#include \"unpack-trees.h\"\n+\n+int reset_head(struct repository *r, struct object_id *oid, const char *action,\n+\t       const char *switch_to_branch, unsigned flags,\n+\t       const char *reflog_orig_head, const char *reflog_head,\n+\t       const char *default_reflog_action)\n+{\n+\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n+\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n+\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n+\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n+\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n+\tstruct object_id head_oid;\n+\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n+\tstruct lock_file lock = LOCK_INIT;\n+\tstruct unpack_trees_options unpack_tree_opts;\n+\tstruct tree *tree;\n+\tconst char *reflog_action;\n+\tstruct strbuf msg = STRBUF_INIT;\n+\tsize_t prefix_len;\n+\tstruct object_id *orig = NULL, oid_orig,\n+\t\t*old_orig = NULL, oid_old_orig;\n+\tint ret = 0, nr = 0;\n+\n+\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n+\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n+\n+\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n+\t\tret = -1;\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n+\t\tret = error(_(\"could not determine HEAD revision\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!oid)\n+\t\toid = &head_oid;\n+\n+\tif (refs_only)\n+\t\tgoto reset_head_refs;\n+\n+\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n+\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n+\tunpack_tree_opts.head_idx = 1;\n+\tunpack_tree_opts.src_index = r->index;\n+\tunpack_tree_opts.dst_index = r->index;\n+\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n+\tunpack_tree_opts.update = 1;\n+\tunpack_tree_opts.merge = 1;\n+\tif (!detach_head)\n+\t\tunpack_tree_opts.reset = 1;\n+\n+\tif (repo_read_index_unmerged(r) < 0) {\n+\t\tret = error(_(\"could not read index\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n+\t\tret = error(_(\"failed to find tree of %s\"),\n+\t\t\t    oid_to_hex(&head_oid));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n+\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n+\t\tret = -1;\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\ttree = parse_tree_indirect(oid);\n+\tprime_cache_tree(r, r->index, tree);\n+\n+\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n+\t\tret = error(_(\"could not write index\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+reset_head_refs:\n+\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n+\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n+\tprefix_len = msg.len;\n+\n+\tif (update_orig_head) {\n+\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n+\t\t\told_orig = &oid_old_orig;\n+\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n+\t\t\torig = &oid_orig;\n+\t\t\tif (!reflog_orig_head) {\n+\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n+\t\t\t\treflog_orig_head = msg.buf;\n+\t\t\t}\n+\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n+\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n+\t\t} else if (old_orig)\n+\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n+\t}\n+\n+\tif (!reflog_head) {\n+\t\tstrbuf_setlen(&msg, prefix_len);\n+\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n+\t\treflog_head = msg.buf;\n+\t}\n+\tif (!switch_to_branch)\n+\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n+\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n+\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n+\telse {\n+\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n+\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n+\t\tif (!ret)\n+\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n+\t\t\t\t\t    reflog_head);\n+\t}\n+\tif (run_hook)\n+\t\trun_hook_le(NULL, \"post-checkout\",\n+\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n+\t\t\t    oid_to_hex(oid), \"1\", NULL);\n+\n+leave_reset_head:\n+\tstrbuf_release(&msg);\n+\trollback_lock_file(&lock);\n+\twhile (nr)\n+\t\tfree((void *)desc[--nr].buffer);\n+\treturn ret;\n+\n+}\ndiff --git a/reset.h b/reset.h\nnew file mode 100644\nindex 0000000000..12f83c78e2\n--- /dev/null\n+++ b/reset.h\n@@ -0,0 +1,20 @@\n+#ifndef RESET_H\n+#define RESET_H\n+\n+#include \"hash.h\"\n+#include \"repository.h\"\n+\n+#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n+\n+#define RESET_HEAD_DETACH (1<<0)\n+#define RESET_HEAD_HARD (1<<1)\n+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n+#define RESET_HEAD_REFS_ONLY (1<<3)\n+#define RESET_ORIG_HEAD (1<<4)\n+\n+int reset_head(struct repository *r, struct object_id *oid, const char *action,\n+\t       const char *switch_to_branch, unsigned flags,\n+\t       const char *reflog_orig_head, const char *reflog_head,\n+\t       const char *default_reflog_action);\n+\n+#endif\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388856","messageId":"bba7586cba65f40ea016d892479e61f9c9d430dd.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 12/17] rebase: generify create_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:09Z","receivedAt":"2019-12-24T11:06:03Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we plan on lib-ifying create_autostash() so we need it to\nbe more generic. Make it more generic by making it accept a\n`struct repository` argument instead of implicitly using the non-repo\nfunctions and `the_repository`. Also, make it accept a `path` argument\nso that we no longer rely have to rely on `struct rebase_options`.\nFinally, make it accept a `default_reflog_action` argument so we no\nlonger have to rely on `DEFAULT_REFLOG_ACTION`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 32 ++++++++++++++++----------------\n 1 file changed, 16 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex b60be5c875..fea4ae754d 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1232,22 +1232,21 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n-static void create_autostash(struct rebase_options *options)\n+static void create_autostash(struct repository *r, const char *path,\n+\t\t\t     const char *default_reflog_action)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct lock_file lock_file = LOCK_INIT;\n \tint fd;\n \n-\tfd = hold_locked_index(&lock_file, 0);\n-\trefresh_cache(REFRESH_QUIET);\n+\tfd = repo_hold_locked_index(r, &lock_file, 0);\n+\trefresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);\n \tif (0 <= fd)\n-\t\trepo_update_index_if_able(the_repository, &lock_file);\n+\t\trepo_update_index_if_able(r, &lock_file);\n \trollback_lock_file(&lock_file);\n \n-\tif (has_unstaged_changes(the_repository, 1) ||\n-\t    has_uncommitted_changes(the_repository, 1)) {\n-\t\tconst char *autostash =\n-\t\t\tstate_dir_path(\"autostash\", options);\n+\tif (has_unstaged_changes(r, 1) ||\n+\t    has_uncommitted_changes(r, 1)) {\n \t\tstruct child_process stash = CHILD_PROCESS_INIT;\n \t\tstruct object_id oid;\n \n@@ -1265,18 +1264,18 @@ static void create_autostash(struct rebase_options *options)\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n \n-\t\tif (safe_create_leading_directories_const(autostash))\n+\t\tif (safe_create_leading_directories_const(path))\n \t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t    options->state_dir);\n-\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n+\t\t\t    path);\n+\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n \t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n+\t\tif (reset_head(r, NULL, \"reset --hard\",\n \t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n-\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n+\t\t\t       default_reflog_action) < 0)\n \t\t\tdie(_(\"could not reset --hard\"));\n \n-\t\tif (discard_index(the_repository->index) < 0 ||\n-\t\t\trepo_read_index(the_repository) < 0)\n+\t\tif (discard_index(r->index) < 0 ||\n+\t\t\trepo_read_index(r) < 0)\n \t\t\tdie(_(\"could not read index\"));\n \t}\n \tstrbuf_release(&buf);\n@@ -1881,7 +1880,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read index\"));\n \n \tif (options.autostash) {\n-\t\tcreate_autostash(&options);\n+\t\tcreate_autostash(the_repository, state_dir_path(\"autostash\", &options),\n+\t\t\t\t DEFAULT_REFLOG_ACTION);\n \t}\n \n \tif (require_clean_work_tree(the_repository, \"rebase\",\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388857","messageId":"2ff189692f82dc34391715ce68e85938190b06c5.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 13/17] sequencer: extract perform_autostash() from rebase","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:10Z","receivedAt":"2019-12-24T11:06:04Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Lib-ify the autostash code by extracting perform_autostash() from rebase\ninto sequencer. In a future commit, this will be used to implement\n`--autostash` in other builtins.\n\nThis patch is best viewed with `--color-moved`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 49 -----------------------------------------------\n sequencer.c      | 50 ++++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |  2 ++\n 3 files changed, 52 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex fea4ae754d..666d3f0181 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1232,55 +1232,6 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n-static void create_autostash(struct repository *r, const char *path,\n-\t\t\t     const char *default_reflog_action)\n-{\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct lock_file lock_file = LOCK_INIT;\n-\tint fd;\n-\n-\tfd = repo_hold_locked_index(r, &lock_file, 0);\n-\trefresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);\n-\tif (0 <= fd)\n-\t\trepo_update_index_if_able(r, &lock_file);\n-\trollback_lock_file(&lock_file);\n-\n-\tif (has_unstaged_changes(r, 1) ||\n-\t    has_uncommitted_changes(r, 1)) {\n-\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n-\t\tstruct object_id oid;\n-\n-\t\targv_array_pushl(&stash.args,\n-\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n-\t\tstash.git_cmd = 1;\n-\t\tstash.no_stdin = 1;\n-\t\tstrbuf_reset(&buf);\n-\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n-\t\t\tdie(_(\"Cannot autostash\"));\n-\t\tstrbuf_trim_trailing_newline(&buf);\n-\t\tif (get_oid(buf.buf, &oid))\n-\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n-\t\t\t    buf.buf);\n-\t\tstrbuf_reset(&buf);\n-\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n-\n-\t\tif (safe_create_leading_directories_const(path))\n-\t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t    path);\n-\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n-\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\tif (reset_head(r, NULL, \"reset --hard\",\n-\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n-\t\t\t       default_reflog_action) < 0)\n-\t\t\tdie(_(\"could not reset --hard\"));\n-\n-\t\tif (discard_index(r->index) < 0 ||\n-\t\t\trepo_read_index(r) < 0)\n-\t\t\tdie(_(\"could not read index\"));\n-\t}\n-\tstrbuf_release(&buf);\n-}\n-\n int cmd_rebase(int argc, const char **argv, const char *prefix)\n {\n \tstruct rebase_options options = REBASE_OPTIONS_INIT;\ndiff --git a/sequencer.c b/sequencer.c\nindex 98e08dbbe0..ba04ee2de8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -32,6 +32,7 @@\n #include \"alias.h\"\n #include \"commit-reach.h\"\n #include \"rebase-interactive.h\"\n+#include \"reset.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -3744,6 +3745,55 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n+void create_autostash(struct repository *r, const char *path,\n+\t\t      const char *default_reflog_action)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct lock_file lock_file = LOCK_INIT;\n+\tint fd;\n+\n+\tfd = repo_hold_locked_index(r, &lock_file, 0);\n+\trefresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);\n+\tif (0 <= fd)\n+\t\trepo_update_index_if_able(r, &lock_file);\n+\trollback_lock_file(&lock_file);\n+\n+\tif (has_unstaged_changes(r, 1) ||\n+\t    has_uncommitted_changes(r, 1)) {\n+\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n+\t\tstruct object_id oid;\n+\n+\t\targv_array_pushl(&stash.args,\n+\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n+\t\tstash.git_cmd = 1;\n+\t\tstash.no_stdin = 1;\n+\t\tstrbuf_reset(&buf);\n+\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n+\t\t\tdie(_(\"Cannot autostash\"));\n+\t\tstrbuf_trim_trailing_newline(&buf);\n+\t\tif (get_oid(buf.buf, &oid))\n+\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n+\t\t\t    buf.buf);\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n+\n+\t\tif (safe_create_leading_directories_const(path))\n+\t\t\tdie(_(\"Could not create directory for '%s'\"),\n+\t\t\t    path);\n+\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n+\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n+\t\tif (reset_head(r, NULL, \"reset --hard\",\n+\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n+\t\t\t       default_reflog_action) < 0)\n+\t\t\tdie(_(\"could not reset --hard\"));\n+\n+\t\tif (discard_index(r->index) < 0 ||\n+\t\t\trepo_read_index(r) < 0)\n+\t\t\tdie(_(\"could not read index\"));\n+\t}\n+\tstrbuf_release(&buf);\n+}\n+\n int apply_autostash(const char *path)\n {\n \tstruct strbuf stash_sha1 = STRBUF_INIT;\ndiff --git a/sequencer.h b/sequencer.h\nindex ecef2c144c..8beb1472f7 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -195,6 +195,8 @@ void commit_post_rewrite(struct repository *r,\n int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,\n \t\t\t\t const char *commit);\n \n+void create_autostash(struct repository *r, const char *path,\n+\t\t      const char *default_reflog_action);\n int apply_autostash(const char *path);\n \n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388858","messageId":"8e3c0c0aecf0bcee68d88fddc772f1af4a0d66fb.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 09/17] rebase: generify reset_head()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:06Z","receivedAt":"2019-12-24T11:06:05Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we plan on lib-ifying reset_head() so we need it to\nbe more generic. Make it more generic by making it accept a\n`struct repository` argument instead of implicitly using the non-repo\nfunctions. Also, make it accept a `const char *default_reflog_action`\nargument so that the default action of \"rebase\" isn't hardcoded in.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 63 +++++++++++++++++++++++++++---------------------\n 1 file changed, 36 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 07674a16d8..4e3cb0852d 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -28,6 +28,8 @@\n #include \"sequencer.h\"\n #include \"rebase-interactive.h\"\n \n+#define DEFAULT_REFLOG_ACTION \"rebase\"\n+\n static char const * const builtin_rebase_usage[] = {\n \tN_(\"git rebase [-i] [options] [--exec <cmd>] \"\n \t\t\"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]\"),\n@@ -760,9 +762,10 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)\n #define RESET_HEAD_REFS_ONLY (1<<3)\n #define RESET_ORIG_HEAD (1<<4)\n \n-static int reset_head(struct object_id *oid, const char *action,\n+static int reset_head(struct repository *r, struct object_id *oid, const char *action,\n \t\t      const char *switch_to_branch, unsigned flags,\n-\t\t      const char *reflog_orig_head, const char *reflog_head)\n+\t\t      const char *reflog_orig_head, const char *reflog_head,\n+\t\t      const char *default_reflog_action)\n {\n \tunsigned detach_head = flags & RESET_HEAD_DETACH;\n \tunsigned reset_hard = flags & RESET_HEAD_HARD;\n@@ -784,7 +787,7 @@ static int reset_head(struct object_id *oid, const char *action,\n \tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n \t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n \n-\tif (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {\n+\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n \t\tret = -1;\n \t\tgoto leave_reset_head;\n \t}\n@@ -803,26 +806,26 @@ static int reset_head(struct object_id *oid, const char *action,\n \tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n \tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n \tunpack_tree_opts.head_idx = 1;\n-\tunpack_tree_opts.src_index = the_repository->index;\n-\tunpack_tree_opts.dst_index = the_repository->index;\n+\tunpack_tree_opts.src_index = r->index;\n+\tunpack_tree_opts.dst_index = r->index;\n \tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n \tunpack_tree_opts.update = 1;\n \tunpack_tree_opts.merge = 1;\n \tif (!detach_head)\n \t\tunpack_tree_opts.reset = 1;\n \n-\tif (repo_read_index_unmerged(the_repository) < 0) {\n+\tif (repo_read_index_unmerged(r) < 0) {\n \t\tret = error(_(\"could not read index\"));\n \t\tgoto leave_reset_head;\n \t}\n \n-\tif (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {\n+\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n \t\tret = error(_(\"failed to find tree of %s\"),\n \t\t\t    oid_to_hex(&head_oid));\n \t\tgoto leave_reset_head;\n \t}\n \n-\tif (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {\n+\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n \t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n \t\tgoto leave_reset_head;\n \t}\n@@ -833,16 +836,16 @@ static int reset_head(struct object_id *oid, const char *action,\n \t}\n \n \ttree = parse_tree_indirect(oid);\n-\tprime_cache_tree(the_repository, the_repository->index, tree);\n+\tprime_cache_tree(r, r->index, tree);\n \n-\tif (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {\n+\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n \t\tret = error(_(\"could not write index\"));\n \t\tgoto leave_reset_head;\n \t}\n \n reset_head_refs:\n \treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n-\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : \"rebase\");\n+\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n \tprefix_len = msg.len;\n \n \tif (update_orig_head) {\n@@ -904,8 +907,10 @@ static int move_to_original_branch(struct rebase_options *opts)\n \t\t    opts->head_name, oid_to_hex(&opts->onto->object.oid));\n \tstrbuf_addf(&head_reflog, \"rebase finished: returning to %s\",\n \t\t    opts->head_name);\n-\tret = reset_head(NULL, \"\", opts->head_name, RESET_HEAD_REFS_ONLY,\n-\t\t\t orig_head_reflog.buf, head_reflog.buf);\n+\tret = reset_head(the_repository, NULL, \"\", opts->head_name,\n+\t\t\t RESET_HEAD_REFS_ONLY,\n+\t\t\t orig_head_reflog.buf, head_reflog.buf,\n+\t\t\t DEFAULT_REFLOG_ACTION);\n \n \tstrbuf_release(&orig_head_reflog);\n \tstrbuf_release(&head_reflog);\n@@ -999,8 +1004,9 @@ static int run_am(struct rebase_options *opts)\n \t\tfree(rebased_patches);\n \t\targv_array_clear(&am.args);\n \n-\t\treset_head(&opts->orig_head, \"checkout\", opts->head_name, 0,\n-\t\t\t   \"HEAD\", NULL);\n+\t\treset_head(the_repository, &opts->orig_head, \"checkout\",\n+\t\t\t   opts->head_name, 0,\n+\t\t\t   \"HEAD\", NULL, DEFAULT_REFLOG_ACTION);\n \t\terror(_(\"\\ngit encountered an error while preparing the \"\n \t\t\t\"patches to replay\\n\"\n \t\t\t\"these revisions:\\n\"\n@@ -1609,8 +1615,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\trerere_clear(the_repository, &merge_rr);\n \t\tstring_list_clear(&merge_rr, 1);\n \n-\t\tif (reset_head(NULL, \"reset\", NULL, RESET_HEAD_HARD,\n-\t\t\t       NULL, NULL) < 0)\n+\t\tif (reset_head(the_repository, NULL, \"reset\", NULL, RESET_HEAD_HARD,\n+\t\t\t       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)\n \t\t\tdie(_(\"could not discard worktree changes\"));\n \t\tremove_branch_state(the_repository, 0);\n \t\tif (read_basic_state(&options))\n@@ -1627,9 +1633,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \t\tif (read_basic_state(&options))\n \t\t\texit(1);\n-\t\tif (reset_head(&options.orig_head, \"reset\",\n+\t\tif (reset_head(the_repository, &options.orig_head, \"reset\",\n \t\t\t       options.head_name, RESET_HEAD_HARD,\n-\t\t\t       NULL, NULL) < 0)\n+\t\t\t       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)\n \t\t\tdie(_(\"could not move back to %s\"),\n \t\t\t    oid_to_hex(&options.orig_head));\n \t\tremove_branch_state(the_repository, 0);\n@@ -1998,8 +2004,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\t    options.state_dir);\n \t\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n \t\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\t\tif (reset_head(NULL, \"reset --hard\",\n-\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)\n+\t\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n+\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n+\t\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n \t\t\t\tdie(_(\"could not reset --hard\"));\n \n \t\t\tif (discard_index(the_repository->index) < 0 ||\n@@ -2045,10 +2052,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\tstrbuf_addf(&buf, \"%s: checkout %s\",\n \t\t\t\t\t    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),\n \t\t\t\t\t    options.switch_to);\n-\t\t\t\tif (reset_head(&oid, \"checkout\",\n+\t\t\t\tif (reset_head(the_repository, &oid, \"checkout\",\n \t\t\t\t\t       options.head_name,\n \t\t\t\t\t       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,\n-\t\t\t\t\t       NULL, buf.buf) < 0) {\n+\t\t\t\t\t       NULL, buf.buf,\n+\t\t\t\t\t       DEFAULT_REFLOG_ACTION) < 0) {\n \t\t\t\t\tret = !!error(_(\"could not switch to \"\n \t\t\t\t\t\t\t\"%s\"),\n \t\t\t\t\t\t      options.switch_to);\n@@ -2120,10 +2128,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tstrbuf_addf(&msg, \"%s: checkout %s\",\n \t\t    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);\n-\tif (reset_head(&options.onto->object.oid, \"checkout\", NULL,\n+\tif (reset_head(the_repository, &options.onto->object.oid, \"checkout\", NULL,\n \t\t       RESET_HEAD_DETACH | RESET_ORIG_HEAD |\n \t\t       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,\n-\t\t       NULL, msg.buf))\n+\t\t       NULL, msg.buf, DEFAULT_REFLOG_ACTION))\n \t\tdie(_(\"Could not detach HEAD\"));\n \tstrbuf_release(&msg);\n \n@@ -2138,8 +2146,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addf(&msg, \"rebase finished: %s onto %s\",\n \t\t\toptions.head_name ? options.head_name : \"detached HEAD\",\n \t\t\toid_to_hex(&options.onto->object.oid));\n-\t\treset_head(NULL, \"Fast-forwarded\", options.head_name,\n-\t\t\t   RESET_HEAD_REFS_ONLY, \"HEAD\", msg.buf);\n+\t\treset_head(the_repository, NULL, \"Fast-forwarded\", options.head_name,\n+\t\t\t   RESET_HEAD_REFS_ONLY, \"HEAD\", msg.buf,\n+\t\t\t   DEFAULT_REFLOG_ACTION);\n \t\tstrbuf_release(&msg);\n \t\tret = !!finish_rebase(&options);\n \t\tgoto cleanup;\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388859","messageId":"1b5bbe66dc4a6034f465ce565eef4d34f8db8124.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 11/17] rebase: extract create_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:08Z","receivedAt":"2019-12-24T11:06:07Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future commit, we will lib-ify this code. In preparation for\nthis, extract the code into the create_autostash() function so that it\ncan be cleaned up before it is finally lib-ified.\n\nThis patch is best viewed with `--color-moved` and\n`--color-moved-ws=allow-indentation-change`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 94 +++++++++++++++++++++++++-----------------------\n 1 file changed, 50 insertions(+), 44 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex a6d7527f4c..b60be5c875 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1232,6 +1232,55 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n+static void create_autostash(struct rebase_options *options)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct lock_file lock_file = LOCK_INIT;\n+\tint fd;\n+\n+\tfd = hold_locked_index(&lock_file, 0);\n+\trefresh_cache(REFRESH_QUIET);\n+\tif (0 <= fd)\n+\t\trepo_update_index_if_able(the_repository, &lock_file);\n+\trollback_lock_file(&lock_file);\n+\n+\tif (has_unstaged_changes(the_repository, 1) ||\n+\t    has_uncommitted_changes(the_repository, 1)) {\n+\t\tconst char *autostash =\n+\t\t\tstate_dir_path(\"autostash\", options);\n+\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n+\t\tstruct object_id oid;\n+\n+\t\targv_array_pushl(&stash.args,\n+\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n+\t\tstash.git_cmd = 1;\n+\t\tstash.no_stdin = 1;\n+\t\tstrbuf_reset(&buf);\n+\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n+\t\t\tdie(_(\"Cannot autostash\"));\n+\t\tstrbuf_trim_trailing_newline(&buf);\n+\t\tif (get_oid(buf.buf, &oid))\n+\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n+\t\t\t    buf.buf);\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n+\n+\t\tif (safe_create_leading_directories_const(autostash))\n+\t\t\tdie(_(\"Could not create directory for '%s'\"),\n+\t\t\t    options->state_dir);\n+\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n+\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n+\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n+\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n+\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n+\t\t\tdie(_(\"could not reset --hard\"));\n+\n+\t\tif (discard_index(the_repository->index) < 0 ||\n+\t\t\trepo_read_index(the_repository) < 0)\n+\t\t\tdie(_(\"could not read index\"));\n+\t}\n+\tstrbuf_release(&buf);\n+}\n \n int cmd_rebase(int argc, const char **argv, const char *prefix)\n {\n@@ -1832,50 +1881,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read index\"));\n \n \tif (options.autostash) {\n-\t\tstruct lock_file lock_file = LOCK_INIT;\n-\t\tint fd;\n-\n-\t\tfd = hold_locked_index(&lock_file, 0);\n-\t\trefresh_cache(REFRESH_QUIET);\n-\t\tif (0 <= fd)\n-\t\t\trepo_update_index_if_able(the_repository, &lock_file);\n-\t\trollback_lock_file(&lock_file);\n-\n-\t\tif (has_unstaged_changes(the_repository, 1) ||\n-\t\t    has_uncommitted_changes(the_repository, 1)) {\n-\t\t\tconst char *autostash =\n-\t\t\t\tstate_dir_path(\"autostash\", &options);\n-\t\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n-\t\t\tstruct object_id oid;\n-\n-\t\t\targv_array_pushl(&stash.args,\n-\t\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n-\t\t\tstash.git_cmd = 1;\n-\t\t\tstash.no_stdin = 1;\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n-\t\t\t\tdie(_(\"Cannot autostash\"));\n-\t\t\tstrbuf_trim_trailing_newline(&buf);\n-\t\t\tif (get_oid(buf.buf, &oid))\n-\t\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n-\t\t\t\t    buf.buf);\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n-\n-\t\t\tif (safe_create_leading_directories_const(autostash))\n-\t\t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t\t    options.state_dir);\n-\t\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n-\t\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n-\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n-\t\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n-\t\t\t\tdie(_(\"could not reset --hard\"));\n-\n-\t\t\tif (discard_index(the_repository->index) < 0 ||\n-\t\t\t\trepo_read_index(the_repository) < 0)\n-\t\t\t\tdie(_(\"could not read index\"));\n-\t\t}\n+\t\tcreate_autostash(&options);\n \t}\n \n \tif (require_clean_work_tree(the_repository, \"rebase\",\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388860","messageId":"6ebae1d7588acbba5dd11d6a0d6dcdb78e266b17.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 17/17] pull: pass --autostash to merge","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:14Z","receivedAt":"2019-12-24T11:06:09Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Before, `--autostash` only worked with `git pull --rebase`. However, in\nthe last patch, merge learned `--autostash` as well so there's no reason\nwhy we should have this restriction anymore. Teach pull to pass\n`--autostash` to merge, just like it did for rebase.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-pull.txt      |  9 -------\n Documentation/merge-options.txt |  4 +--\n builtin/pull.c                  |  9 ++++---\n t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------\n 4 files changed, 42 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt\nindex dfb901f8b8..ba3772de9f 100644\n--- a/Documentation/git-pull.txt\n+++ b/Documentation/git-pull.txt\n@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.\n --no-rebase::\n \tOverride earlier --rebase.\n \n---autostash::\n---no-autostash::\n-\tBefore starting rebase, stash local modifications away (see\n-\tlinkgit:git-stash[1]) if needed, and apply the stash entry when\n-\tdone. `--no-autostash` is useful to override the `rebase.autoStash`\n-\tconfiguration variable (see linkgit:git-config[1]).\n-+\n-This option is only valid when \"--rebase\" is used.\n-\n Options related to fetching\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n \ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 34493eb58b..ae56cca826 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -155,6 +155,8 @@ ifndef::git-pull[]\n \tNote that not all merge strategies may support progress\n \treporting.\n \n+endif::git-pull[]\n+\n --autostash::\n --no-autostash::\n \tAutomatically create a temporary stash entry before the operation\n@@ -163,8 +165,6 @@ ifndef::git-pull[]\n \twith care: the final stash application after a successful\n \trebase might result in non-trivial conflicts.\n \n-endif::git-pull[]\n-\n --allow-unrelated-histories::\n \tBy default, `git merge` command refuses to merge histories\n \tthat do not share a common ancestor.  This option can be\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex d25ff13a60..ee186781ae 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -183,7 +183,7 @@ static struct option pull_options[] = {\n \t\tN_(\"verify that the named commit has a valid GPG signature\"),\n \t\tPARSE_OPT_NOARG),\n \tOPT_BOOL(0, \"autostash\", &opt_autostash,\n-\t\tN_(\"automatically stash/stash pop before and after rebase\")),\n+\t\tN_(\"automatically stash/stash pop before and after\")),\n \tOPT_PASSTHRU_ARGV('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n \t\tN_(\"merge strategy to use\"),\n \t\t0),\n@@ -671,6 +671,10 @@ static int run_merge(void)\n \targv_array_pushv(&args, opt_strategy_opts.argv);\n \tif (opt_gpg_sign)\n \t\targv_array_push(&args, opt_gpg_sign);\n+\tif (opt_autostash == 0)\n+\t\targv_array_push(&args, \"--no-autostash\");\n+\telse if (opt_autostash == 1)\n+\t\targv_array_push(&args, \"--autostash\");\n \tif (opt_allow_unrelated_histories > 0)\n \t\targv_array_push(&args, \"--allow-unrelated-histories\");\n \n@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (get_oid(\"HEAD\", &orig_head))\n \t\toidclr(&orig_head);\n \n-\tif (!opt_rebase && opt_autostash != -1)\n-\t\tdie(_(\"--[no-]autostash option is only valid with --rebase.\"));\n-\n \tautostash = config_autostash;\n \tif (opt_rebase) {\n \t\tif (opt_autostash != -1)\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 218f469d0a..2919ad4f81 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -28,7 +28,7 @@ test_pull_autostash_fail () {\n \techo dirty >new_file &&\n \tgit add new_file &&\n \ttest_must_fail git pull \"$@\" . copy 2>err &&\n-\ttest_i18ngrep \"uncommitted changes.\" err\n+\ttest_i18ngrep \"\\(uncommitted changes.\\)\\|\\(overwritten by merge:\\)\" err\n }\n \n test_expect_success setup '\n@@ -391,13 +391,40 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '\n \ttest_pull_autostash_fail --rebase --no-autostash\n '\n \n-for i in --autostash --no-autostash\n-do\n-\ttest_expect_success \"pull $i (without --rebase) is illegal\" '\n-\t\ttest_must_fail git pull $i . copy 2>err &&\n-\t\ttest_i18ngrep \"only valid with --rebase\" err\n-\t'\n-done\n+test_expect_success 'pull succeeds with dirty working directory and merge.autostash set' '\n+\ttest_config merge.autostash true &&\n+\ttest_pull_autostash 2\n+'\n+\n+test_expect_success 'pull --autostash & merge.autostash=true' '\n+\ttest_config merge.autostash true &&\n+\ttest_pull_autostash 2 --autostash\n+'\n+\n+test_expect_success 'pull --autostash & merge.autostash=false' '\n+\ttest_config merge.autostash false &&\n+\ttest_pull_autostash 2 --autostash\n+'\n+\n+test_expect_success 'pull --autostash & merge.autostash unset' '\n+\ttest_unconfig merge.autostash &&\n+\ttest_pull_autostash 2 --autostash\n+'\n+\n+test_expect_success 'pull --no-autostash & merge.autostash=true' '\n+\ttest_config merge.autostash true &&\n+\ttest_pull_autostash_fail --no-autostash\n+'\n+\n+test_expect_success 'pull --no-autostash & merge.autostash=false' '\n+\ttest_config merge.autostash false &&\n+\ttest_pull_autostash_fail --no-autostash\n+'\n+\n+test_expect_success 'pull --no-autostash & merge.autostash unset' '\n+\ttest_unconfig merge.autostash &&\n+\ttest_pull_autostash_fail --no-autostash\n+'\n \n test_expect_success 'pull.rebase' '\n \tgit reset --hard before-rebase &&\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388861","messageId":"2b167116802c7130e4cdecd0ecd5df501fe9a6e0.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 14/17] sequencer: unlink autostash in apply_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:11Z","receivedAt":"2019-12-24T11:06:10Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Explicitly remove autostash file in apply_autostash() once it has been\napplied successfully.\n\nThis is currently a no-op because the only users of this function will unlink\nthe state (including the autostash file) after this function runs.\nHowever, in the future, we will introduce a user of the function that\ndoes not explicitly remove the state so we do it here.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex ba04ee2de8..cc0391ca35 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3834,6 +3834,7 @@ int apply_autostash(const char *path)\n \t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"));\n \t}\n \n+\tunlink(path);\n \tstrbuf_release(&stash_sha1);\n \treturn ret;\n }\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388862","messageId":"c70643ca9fb504509c68b6265e35ea2ab9df99bc.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 16/17] t5520: make test_pull_autostash() accept expect_parent_num","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:13Z","receivedAt":"2019-12-24T11:06:11Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Before, test_pull_autostash() was hardcoded to run\n`test_cmp_rev HEAD^ copy` to test that a rebase happened. However, in a\nfuture patch, we plan on testing merging as well. Make\ntest_pull_autostash() accept a parent number as an argument so that, in\nthe future, we can test if a merge happened in addition to a rebase.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t5520-pull.sh | 14 ++++++++------\n 1 file changed, 8 insertions(+), 6 deletions(-)\n\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 602d996a33..218f469d0a 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -10,11 +10,13 @@ modify () {\n }\n \n test_pull_autostash () {\n+\texpect_parent_num=\"$1\" &&\n+\tshift &&\n \tgit reset --hard before-rebase &&\n \techo dirty >new_file &&\n \tgit add new_file &&\n \tgit pull \"$@\" . copy &&\n-\ttest_cmp_rev HEAD^ copy &&\n+\ttest_cmp_rev HEAD^\"$expect_parent_num\" copy &&\n \techo dirty >expect &&\n \ttest_cmp expect new_file &&\n \techo \"modified again\" >expect &&\n@@ -356,22 +358,22 @@ test_expect_success '--rebase fails with multiple branches' '\n \n test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' '\n \ttest_config rebase.autostash true &&\n-\ttest_pull_autostash --rebase\n+\ttest_pull_autostash 1 --rebase\n '\n \n test_expect_success 'pull --rebase --autostash & rebase.autostash=true' '\n \ttest_config rebase.autostash true &&\n-\ttest_pull_autostash --rebase --autostash\n+\ttest_pull_autostash 1 --rebase --autostash\n '\n \n test_expect_success 'pull --rebase --autostash & rebase.autostash=false' '\n \ttest_config rebase.autostash false &&\n-\ttest_pull_autostash --rebase --autostash\n+\ttest_pull_autostash 1 --rebase --autostash\n '\n \n test_expect_success 'pull --rebase --autostash & rebase.autostash unset' '\n \ttest_unconfig rebase.autostash &&\n-\ttest_pull_autostash --rebase --autostash\n+\ttest_pull_autostash 1 --rebase --autostash\n '\n \n test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '\n@@ -409,7 +411,7 @@ test_expect_success 'pull.rebase' '\n \n test_expect_success 'pull --autostash & pull.rebase=true' '\n \ttest_config pull.rebase true &&\n-\ttest_pull_autostash --autostash\n+\ttest_pull_autostash 1 --autostash\n '\n \n test_expect_success 'pull --no-autostash & pull.rebase=true' '\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388863","messageId":"6e987052c0910f0131317093cb917dcf9eea127d.1577185374.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v2 15/17] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2019-12-24T11:05:12Z","receivedAt":"2019-12-24T11:06:12Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In rebase, one can pass the `--autostash` option to cause the worktree\nto be automatically stashed before continuing with the rebase. This\noption is missing in merge, however.\n\nImplement the `--autostash` option and corresponding `merge.autoStash`\noption in merge which stashes before merging and then pops after.\n\nThis option is useful when a developer has some local changes on a topic\nbranch but they realize that their work depends on another branch.\nPreviously, they had to run something like\n\n\tgit fetch ...\n\tgit stash push\n\tgit merge FETCH_HEAD\n\tgit stash pop\n\nbut now, that is reduced to\n\n\tgit fetch ...\n\tgit merge --autostash FETCH_HEAD\n\nSuggested-by: Alban Gruin <alban.gruin@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/merge.txt  | 10 +++++\n Documentation/merge-options.txt |  8 ++++\n branch.c                        |  1 +\n builtin/commit.c                |  2 +\n builtin/merge.c                 | 17 +++++++++\n path.c                          |  1 +\n path.h                          |  4 +-\n t/t3033-merge-toplevel.sh       | 22 +++++++++++\n t/t7600-merge.sh                | 65 +++++++++++++++++++++++++++++++++\n 9 files changed, 129 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex 6a313937f8..88b29127bf 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -70,6 +70,16 @@ merge.stat::\n \tWhether to print the diffstat between ORIG_HEAD and the merge result\n \tat the end of the merge.  True by default.\n \n+merge.autoStash::\n+\tWhen set to true, automatically create a temporary stash entry\n+\tbefore the operation begins, and apply it after the operation\n+\tends.  This means that you can run rebase on a dirty worktree.\n+\tHowever, use with care: the final stash application after a\n+\tsuccessful rebase might result in non-trivial conflicts.\n+\tThis option can be overridden by the `--no-autostash` and\n+\t`--autostash` options of linkgit:git-merge[1].\n+\tDefaults to false.\n+\n merge.tool::\n \tControls which merge tool is used by linkgit:git-mergetool[1].\n \tThe list below shows the valid built-in values.\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 40dc4f5e8c..34493eb58b 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -155,6 +155,14 @@ ifndef::git-pull[]\n \tNote that not all merge strategies may support progress\n \treporting.\n \n+--autostash::\n+--no-autostash::\n+\tAutomatically create a temporary stash entry before the operation\n+\tbegins, and apply it after the operation ends.  This means\n+\tthat you can run rebase on a dirty worktree.  However, use\n+\twith care: the final stash application after a successful\n+\trebase might result in non-trivial conflicts.\n+\n endif::git-pull[]\n \n --allow-unrelated-histories::\ndiff --git a/branch.c b/branch.c\nindex 579494738a..bf2536c70d 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)\n \tunlink(git_path_merge_rr(r));\n \tunlink(git_path_merge_msg(r));\n \tunlink(git_path_merge_mode(r));\n+\tapply_autostash(git_path_merge_autostash(r));\n }\n \n void remove_branch_state(struct repository *r, int verbose)\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex e48c1fd90a..ebb7f4d80e 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1679,6 +1679,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tunlink(git_path_merge_mode(the_repository));\n \tunlink(git_path_squash_msg(the_repository));\n \n+\tapply_autostash(git_path_merge_autostash(the_repository));\n+\n \tif (commit_index_files())\n \t\tdie(_(\"repository has been updated, but unable to write\\n\"\n \t\t      \"new_index file. Check that disk is not full and quota is\\n\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 062e911441..a940bd4cbf 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -81,6 +81,7 @@ static int show_progress = -1;\n static int default_to_upstream = 1;\n static int signoff;\n static const char *sign_commit;\n+static int autostash;\n static int no_verify;\n \n static struct strategy all_strategy[] = {\n@@ -285,6 +286,8 @@ static struct option builtin_merge_options[] = {\n \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n+\tOPT_BOOL(0, \"autostash\", &autostash,\n+\t      N_(\"automatically stash/stash pop before and after\")),\n \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n@@ -440,6 +443,7 @@ static void finish(struct commit *head_commit,\n \t\tstrbuf_addf(&reflog_message, \"%s: %s\",\n \t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n \t}\n+\tapply_autostash(git_path_merge_autostash(the_repository));\n \tif (squash) {\n \t\tsquash_message(head_commit, remoteheads);\n \t} else {\n@@ -631,6 +635,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t} else if (!strcmp(k, \"commit.gpgsign\")) {\n \t\tsign_commit = git_config_bool(k, v) ? \"\" : NULL;\n \t\treturn 0;\n+\t} else if (!strcmp(k, \"merge.autostash\")) {\n+\t\tautostash = git_config_bool(k, v);\n+\t\treturn 0;\n \t}\n \n \tstatus = fmt_merge_msg_config(k, v, cb);\n@@ -1288,6 +1295,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \n \t\t/* Invoke 'git reset --merge' */\n \t\tret = cmd_reset(nargc, nargv, prefix);\n+\t\tapply_autostash(git_path_merge_autostash(the_repository));\n \t\tgoto done;\n \t}\n \n@@ -1508,6 +1516,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\tgoto done;\n \t\t}\n \n+\t\tif (autostash)\n+\t\t\tcreate_autostash(the_repository,\n+\t\t\t\t\t git_path_merge_autostash(the_repository),\n+\t\t\t\t\t \"merge\");\n \t\tif (checkout_fast_forward(the_repository,\n \t\t\t\t\t  &head_commit->object.oid,\n \t\t\t\t\t  &commit->object.oid,\n@@ -1574,6 +1586,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (fast_forward == FF_ONLY)\n \t\tdie(_(\"Not possible to fast-forward, aborting.\"));\n \n+\tif (autostash)\n+\t\tcreate_autostash(the_repository,\n+\t\t\t\t git_path_merge_autostash(the_repository),\n+\t\t\t\t \"merge\");\n+\n \t/* We are going to make a new commit. */\n \tgit_committer_info(IDENT_STRICT);\n \ndiff --git a/path.c b/path.c\nindex a76eec8b96..d8c4072f1a 100644\n--- a/path.c\n+++ b/path.c\n@@ -1533,5 +1533,6 @@ REPO_GIT_PATH_FUNC(merge_msg, \"MERGE_MSG\")\n REPO_GIT_PATH_FUNC(merge_rr, \"MERGE_RR\")\n REPO_GIT_PATH_FUNC(merge_mode, \"MERGE_MODE\")\n REPO_GIT_PATH_FUNC(merge_head, \"MERGE_HEAD\")\n+REPO_GIT_PATH_FUNC(merge_autostash, \"MERGE_AUTOSTASH\")\n REPO_GIT_PATH_FUNC(fetch_head, \"FETCH_HEAD\")\n REPO_GIT_PATH_FUNC(shallow, \"shallow\")\ndiff --git a/path.h b/path.h\nindex 14d6dcad16..1f1bf8f87a 100644\n--- a/path.h\n+++ b/path.h\n@@ -177,11 +177,12 @@ struct path_cache {\n \tconst char *merge_rr;\n \tconst char *merge_mode;\n \tconst char *merge_head;\n+\tconst char *merge_autostash;\n \tconst char *fetch_head;\n \tconst char *shallow;\n };\n \n-#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n+#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n \n const char *git_path_cherry_pick_head(struct repository *r);\n const char *git_path_revert_head(struct repository *r);\n@@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);\n const char *git_path_merge_rr(struct repository *r);\n const char *git_path_merge_mode(struct repository *r);\n const char *git_path_merge_head(struct repository *r);\n+const char *git_path_merge_autostash(struct repository *r);\n const char *git_path_fetch_head(struct repository *r);\n const char *git_path_shallow(struct repository *r);\n \ndiff --git a/t/t3033-merge-toplevel.sh b/t/t3033-merge-toplevel.sh\nindex d314599428..e29c284b9b 100755\n--- a/t/t3033-merge-toplevel.sh\n+++ b/t/t3033-merge-toplevel.sh\n@@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' '\n \ttest_must_fail git merge five\n '\n \n+test_expect_success 'refuse two-project merge by default, quit before --autostash happens' '\n+\tt3033_reset &&\n+\tgit reset --hard four &&\n+\techo change >>one.t &&\n+\tgit diff >expect &&\n+\ttest_must_fail git merge --autostash five 2>err &&\n+\ttest_i18ngrep ! \"stash\" err &&\n+\tgit diff >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'two-project merge with --allow-unrelated-histories' '\n \tt3033_reset &&\n \tgit reset --hard four &&\n@@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' '\n \tgit diff --exit-code five\n '\n \n+test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' '\n+\tt3033_reset &&\n+\tgit reset --hard four &&\n+\techo change >>one.t &&\n+\tgit diff one.t >expect &&\n+\tgit merge --allow-unrelated-histories --autostash five 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit diff one.t >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 4fa0ef8e3b..e0c8a0bad4 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -30,13 +30,17 @@ Testing basic merge operations/option parsing.\n . \"$TEST_DIRECTORY\"/lib-gpg.sh\n \n test_write_lines 1 2 3 4 5 6 7 8 9 >file\n+cp file file.orig\n test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1\n+test_write_lines 1 2 '3 X' 4 5 6 7 8 9 >file.3\n test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5\n test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9\n test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1\n test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n+test_write_lines '1 X' 2 3 4 5 6 7 8 '9 X' >result.1-9\n test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n+test_write_lines '1 X' 2 '3 X' 4 '5 X' 6 7 8 '9 X' >result.1-3-5-9\n test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n \n create_merge_msgs () {\n@@ -675,6 +679,67 @@ test_expect_success 'refresh the index before merging' '\n \tgit merge c3\n '\n \n+test_expect_success 'merge with --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit merge --autostash c2 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'merge with merge.autoStash' '\n+\ttest_config merge.autoStash true &&\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit merge c2 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'fast-forward merge with --autostash' '\n+\tgit reset --hard c0 &&\n+\tgit merge-file file file.orig file.5 &&\n+\tgit merge --autostash c1 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\ttest_cmp result.1-5 file\n+'\n+\n+test_expect_success 'octopus merge with --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.3 &&\n+\tgit merge --autostash c2 c3 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5-9 merge-result &&\n+\ttest_cmp result.1-3-5-9 file\n+'\n+\n+test_expect_success 'conflicted merge with --autostash, --abort restores stash' '\n+\tgit reset --hard c3 &&\n+\tcp file.1 file &&\n+\ttest_must_fail git merge --autostash c7 &&\n+\tgit merge --abort 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\ttest_cmp file.1 file\n+'\n+\n+test_expect_success 'merge with conflicted --autostash changes' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9y &&\n+\tgit diff >expect &&\n+\ttest_when_finished \"test_might_fail git stash drop\" &&\n+\tgit merge --autostash c3 2>err &&\n+\ttest_i18ngrep \"Applying autostash resulted in conflicts.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-9 merge-result &&\n+\tgit stash show -p >actual &&\n+\ttest_cmp expect actual\n+'\n+\n cat >expected.branch <<\\EOF\n Merge branch 'c5-branch' (early part)\n EOF\n-- \n2.24.1.810.g65a2f617f4\n\n"},{"id":"388949","messageId":"xmqqwoaikehz.fsf@gitster-ct.c.googlers.com","threadId":"52048","inReplyTo":"fd547aab491b24a47f683d717c6b77255ac16901.1577185374.git.liu.denton@gmail.com","subject":"Re: [PATCH v2 04/17] sequencer: configurably warn on non-existent files","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-12-26T20:39:52Z","receivedAt":"2019-12-26T20:40:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> -static int read_oneliner(struct strbuf *buf,\n> -\tconst char *path, int skip_if_empty)\n> +static int read_oneliner(struct strbuf *buf, const char *path,\n> +\t\t\t int skip_if_empty, int warn_nonexistence)\n\nI would have to say that this results in a rather poor API in the\nend, and also forces an awkward API evolution, and we need to be\ncareful especially if we plan to make this an external API in a\nlater step in the series.\n\nThere is a topic in flight that introduces additional callsites of\nread_oneliner().  It is a selfish move that breaks codebase\nunnecessarily to unilaterally change the function signature.\n\nTwo possible ways to improve are:\n\n * keep read_oneliner() with existing function signature working for\n   other topics in flight, by renaming your extended variant and\n   making read_oneliner() a thin wrapper that calls yours with the\n   extended feature disabled (i.e. warn_nonexistence==0); or\n\n * update skip_if_empty that wastes an int parameter to pass only a\n   single bit into an unsigned that is a collection of bits, after\n   vetting that new callsites contemporary topics add always pass\n   either 0 or 1 as the parameter.  Then\n\n\t#define READ_ONELINER_SKIP_IF_EMPTY 01\n\t#define READ_ONELINER_WARN_MISSING  02\n\tstatic int read_oneliner(struct strbuf *buf, const char *path,\n   \t\t\t\t unsigned flags)\n\n   would transparently work well for those other topics.\n\nThe latter probably is more preferrable, as the end result would be\na cleaner API than the former or the one presented in this series.\n\nThanks.\n\n>  {\n>  \tint ret = 0;\n>  \tstruct strbuf file_buf = STRBUF_INIT;\n>  \n> -\tif (!file_exists(path))\n> +\tif (!warn_nonexistence && !file_exists(path))\n>  \t\treturn 0;\n>  \n>  \tif (strbuf_read_file(&file_buf, path, 0) < 0) {\n> @@ -2558,10 +2558,10 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)\n>  static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)\n>  {\n>  \tstrbuf_reset(buf);\n> -\tif (!read_oneliner(buf, rebase_path_strategy(), 0))\n> +\tif (!read_oneliner(buf, rebase_path_strategy(), 0, 0))\n>  \t\treturn;\n>  \topts->strategy = strbuf_detach(buf, NULL);\n> -\tif (!read_oneliner(buf, rebase_path_strategy_opts(), 0))\n> +\tif (!read_oneliner(buf, rebase_path_strategy_opts(), 0, 0))\n>  \t\treturn;\n>  \n>  \tparse_strategy_opts(opts, buf->buf);\n> @@ -2572,7 +2572,7 @@ static int read_populate_opts(struct replay_opts *opts)\n>  \tif (is_rebase_i(opts)) {\n>  \t\tstruct strbuf buf = STRBUF_INIT;\n>  \n> -\t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {\n> +\t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1, 0)) {\n>  \t\t\tif (!starts_with(buf.buf, \"-S\"))\n>  \t\t\t\tstrbuf_reset(&buf);\n>  \t\t\telse {\n> @@ -2582,7 +2582,7 @@ static int read_populate_opts(struct replay_opts *opts)\n>  \t\t\tstrbuf_reset(&buf);\n>  \t\t}\n>  \n> -\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {\n> +\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1, 0)) {\n>  \t\t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n>  \t\t\t\topts->allow_rerere_auto = RERERE_AUTOUPDATE;\n>  \t\t\telse if (!strcmp(buf.buf, \"--no-rerere-autoupdate\"))\n> @@ -2618,7 +2618,7 @@ static int read_populate_opts(struct replay_opts *opts)\n>  \t\tstrbuf_release(&buf);\n>  \n>  \t\tif (read_oneliner(&opts->current_fixups,\n> -\t\t\t\t  rebase_path_current_fixups(), 1)) {\n> +\t\t\t\t  rebase_path_current_fixups(), 1, 0)) {\n>  \t\t\tconst char *p = opts->current_fixups.buf;\n>  \t\t\topts->current_fixup_count = 1;\n>  \t\t\twhile ((p = strchr(p, '\\n'))) {\n> @@ -2627,7 +2627,7 @@ static int read_populate_opts(struct replay_opts *opts)\n>  \t\t\t}\n>  \t\t}\n>  \n> -\t\tif (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {\n> +\t\tif (read_oneliner(&buf, rebase_path_squash_onto(), 0, 0)) {\n>  \t\t\tif (get_oid_hex(buf.buf, &opts->squash_onto) < 0)\n>  \t\t\t\treturn error(_(\"unusable squash-onto\"));\n>  \t\t\topts->have_squash_onto = 1;\n> @@ -3759,7 +3759,7 @@ static int apply_autostash(struct replay_opts *opts)\n>  \tstruct child_process child = CHILD_PROCESS_INIT;\n>  \tint ret = 0;\n>  \n> -\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {\n> +\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1, 0)) {\n>  \t\tstrbuf_release(&stash_sha1);\n>  \t\treturn 0;\n>  \t}\n> @@ -4093,7 +4093,7 @@ static int pick_commits(struct repository *r,\n>  \t\tif (todo_list->current < todo_list->nr)\n>  \t\t\treturn 0;\n>  \n> -\t\tif (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&\n> +\t\tif (read_oneliner(&head_ref, rebase_path_head_name(), 0, 0) &&\n>  \t\t\t\tstarts_with(head_ref.buf, \"refs/\")) {\n>  \t\t\tconst char *msg;\n>  \t\t\tstruct object_id head, orig;\n> @@ -4106,13 +4106,13 @@ static int pick_commits(struct repository *r,\n>  \t\t\t\tstrbuf_release(&buf);\n>  \t\t\t\treturn res;\n>  \t\t\t}\n> -\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||\n> +\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0, 0) ||\n>  \t\t\t\t\tget_oid_hex(buf.buf, &orig)) {\n>  \t\t\t\tres = error(_(\"could not read orig-head\"));\n>  \t\t\t\tgoto cleanup_head_ref;\n>  \t\t\t}\n>  \t\t\tstrbuf_reset(&buf);\n> -\t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0)) {\n> +\t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0, 0)) {\n>  \t\t\t\tres = error(_(\"could not read 'onto'\"));\n>  \t\t\t\tgoto cleanup_head_ref;\n>  \t\t\t}\n> @@ -4145,7 +4145,7 @@ static int pick_commits(struct repository *r,\n>  \t\t\t\tDIFF_FORMAT_DIFFSTAT;\n>  \t\t\tlog_tree_opt.disable_stdin = 1;\n>  \n> -\t\t\tif (read_oneliner(&buf, rebase_path_orig_head(), 0) &&\n> +\t\t\tif (read_oneliner(&buf, rebase_path_orig_head(), 0, 0) &&\n>  \t\t\t    !get_oid(buf.buf, &orig) &&\n>  \t\t\t    !get_oid(\"HEAD\", &head)) {\n>  \t\t\t\tdiff_tree_oid(&orig, &head, \"\",\n> @@ -4230,7 +4230,7 @@ static int commit_staged_changes(struct repository *r,\n>  \n>  \t\tif (get_oid(\"HEAD\", &head))\n>  \t\t\treturn error(_(\"cannot amend non-existing commit\"));\n> -\t\tif (!read_oneliner(&rev, rebase_path_amend(), 0))\n> +\t\tif (!read_oneliner(&rev, rebase_path_amend(), 0, 0))\n>  \t\t\treturn error(_(\"invalid file: '%s'\"), rebase_path_amend());\n>  \t\tif (get_oid_hex(rev.buf, &to_amend))\n>  \t\t\treturn error(_(\"invalid contents: '%s'\"),\n> @@ -4391,7 +4391,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)\n>  \t\tstruct strbuf buf = STRBUF_INIT;\n>  \t\tstruct object_id oid;\n>  \n> -\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&\n> +\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(), 1, 0) &&\n>  \t\t    !get_oid_committish(buf.buf, &oid))\n>  \t\t\trecord_in_rewritten(&oid, peek_command(&todo_list, 0));\n>  \t\tstrbuf_release(&buf);\n"},{"id":"389105","messageId":"xmqqwoadfq8n.fsf@gitster-ct.c.googlers.com","threadId":"52048","inReplyTo":"31055cdac4a5bfc48cf6f18179f4e9bce9a04c8c.1577185374.git.liu.denton@gmail.com","subject":"Re: [PATCH v2 01/17] Makefile: alphabetically sort += lists","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-12-30T21:38:48Z","receivedAt":"2019-12-30T21:39:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> Note that if we omit file prefixes, the lists aren't sorted in strictly\n> alphabetical order (e.g. archive.o comes after archive-zip.o instead of\n> before archive-tar.o). This is intentional ...\n\nThat's fine, but then we shouldn't label it as \"alphabetically sort\".\n\n> because the purpose of\n> maintaining the sorted list is to ensure line insertions are\n> deterministic.\n\nTruly alphabetic sorting is deterministic, too, so that is *not* the\nreason why you are not being strictly alphabetic.  The true reason\nis because it is easier to apply mechanical sorting in ascii order\nthan truly sort alphabetically.  Both are equally deterministic and\nreproducible.  Alphabetic might be easier for humans to see and\nunderstand, but you value mechanical ease of sorting and that is why\nyou chose ascii order over alphabetical sorting.\n\n"},{"id":"389107","messageId":"xmqqo8vpfpri.fsf@gitster-ct.c.googlers.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"Re: [PATCH v2 00/17] merge: learn --autostash","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-12-30T21:49:05Z","receivedAt":"2019-12-30T21:49:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> Alban reported[1] that he expected merge to have an --autostash option,\n> just like rebase. Since there's not really any reason why merge can't\n> have it, let's implement it in this patchset.\n\nSigh.  \n\nI would actually have preferred to remove the autostash from rebase\nif the goal is to have a consistent behaviour between the two,\nthough ;-)\n\nBut it is OK as long as it does not degrade the codepath with\nchanges that wouldn't have been necessary if this weren't added.\n\nLet's see how it goes.  Queued.\n"},{"id":"389133","messageId":"4032e4bd-fa3d-54eb-fe95-38549dff3aaa@gmail.com","threadId":"52048","inReplyTo":"xmqqo8vpfpri.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 00/17] merge: learn --autostash","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-12-31T10:34:30Z","receivedAt":"2019-12-31T10:34:35Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 30/12/2019 21:49, Junio C Hamano wrote:\n> Denton Liu <liu.denton@gmail.com> writes:\n> \n>> Alban reported[1] that he expected merge to have an --autostash option,\n>> just like rebase. Since there's not really any reason why merge can't\n>> have it, let's implement it in this patchset.\n> \n> Sigh.\n> \n> I would actually have preferred to remove the autostash from rebase\n> if the goal is to have a consistent behaviour between the two,\n> though ;-)\n\nI find the --autostash option to rebase useful if I want to edit a \ncommit and I've got uncommitted changes. I wish it was called --stash \nthough as it's not automatic as one has to set a config setting or pass \nit on the commandline. I'm also not convinced the config setting is a \ngood idea as it allows rebase to run when I think I've committed \neverything but I haven't.\n\nFor `merge --autostash` I think we need to consider the interaction of \n`--no-commit` with `--autostash` as `stash pop` will refuse to run if \nthe merge modified any of the stashed paths.\n\nBest Wishes\n\nPhillip\n\n\n> But it is OK as long as it does not degrade the codepath with\n> changes that wouldn't have been necessary if this weren't added.\n> \n> Let's see how it goes.  Queued.\n> \n"},{"id":"389144","messageId":"20200101074835.GA5152@generichostname","threadId":"52048","inReplyTo":"xmqqo8vpfpri.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 00/17] merge: learn --autostash","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-01-01T07:48:35Z","receivedAt":"2020-01-01T07:48:45Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Junio,\n\nOn Mon, Dec 30, 2019 at 01:49:05PM -0800, Junio C Hamano wrote:\n> Denton Liu <liu.denton@gmail.com> writes:\n> \n> > Alban reported[1] that he expected merge to have an --autostash option,\n> > just like rebase. Since there's not really any reason why merge can't\n> > have it, let's implement it in this patchset.\n> \n> Sigh.  \n> \n> I would actually have preferred to remove the autostash from rebase\n> if the goal is to have a consistent behaviour between the two,\n> though ;-)\n> \n> But it is OK as long as it does not degrade the codepath with\n> changes that wouldn't have been necessary if this weren't added.\n> \n> Let's see how it goes.  Queued.\n\nSorry for the radio silence for the past week. I've been offline trying\nto recover from a particularly bad flu. Anyway, I have a reroll of this\ntopic planned with the `flags` change you suggested and addressing\nPhilip's concerns about --no-commit.\n\nSince this is a new topic that definitely won't be accepted for v2.25.0,\nI'll probably be taking my time with this ;)\n\nThanks,\n\nDenton\n"},{"id":"389451","messageId":"20200108060842.GA51465@generichostname","threadId":"52048","inReplyTo":"4032e4bd-fa3d-54eb-fe95-38549dff3aaa@gmail.com","subject":"Re: [PATCH v2 00/17] merge: learn --autostash","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-01-08T06:08:42Z","receivedAt":"2020-01-08T06:08:47Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Phillip,\n\nSorry for the late reply. I'm back in school so I've been pretty busy\nlately.\n\nOn Tue, Dec 31, 2019 at 10:34:30AM +0000, Phillip Wood wrote:\n> For `merge --autostash` I think we need to consider the interaction of\n> `--no-commit` with `--autostash` as `stash pop` will refuse to run if the\n> merge modified any of the stashed paths.\n\nThe only time when we run the `stash pop` is when we either commit the\nmerge or abort it. With this in mind, what do you think of the following\ntest cases? If they look good, I can squash them into the appropriate\npatch and send in a reroll.\n\n\n\tdiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\n\tindex e0c8a0bad4..af5db58e16 100755\n\t--- a/t/t7600-merge.sh\n\t+++ b/t/t7600-merge.sh\n\t@@ -727,6 +727,33 @@ test_expect_success 'conflicted merge with --autostash, --abort restores stash'\n\t\ttest_cmp file.1 file\n\t '\n\t \n\t+test_expect_success 'completed merge with --no-commit and --autostash' '\n\t+\tgit reset --hard c1 &&\n\t+\tgit merge-file file file.orig file.9 &&\n\t+\tgit diff >expect &&\n\t+\tgit merge --no-commit --autostash c2 &&\n\t+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n\t+\ttest_cmp expect actual &&\n\t+\tgit commit 2>err &&\n\t+\ttest_i18ngrep \"Applied autostash.\" err &&\n\t+\tgit show HEAD:file >merge-result &&\n\t+\ttest_cmp result.1-5 merge-result &&\n\t+\ttest_cmp result.1-5-9 file\n\t+'\n\t+\n\t+test_expect_success 'aborted merge with --no-commit and --autostash' '\n\t+\tgit reset --hard c1 &&\n\t+\tgit merge-file file file.orig file.9 &&\n\t+\tgit diff >expect &&\n\t+\tgit merge --no-commit --autostash c2 &&\n\t+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n\t+\ttest_cmp expect actual &&\n\t+\tgit merge --abort 2>err &&\n\t+\ttest_i18ngrep \"Applied autostash.\" err &&\n\t+\tgit diff >actual &&\n\t+\ttest_cmp expect actual\n\t+'\n\t+\n\t test_expect_success 'merge with conflicted --autostash changes' '\n\t\tgit reset --hard c1 &&\n\t\tgit merge-file file file.orig file.9y &&\n\nThanks,\n\nDenton\n\n> \n> Best Wishes\n> \n> Phillip\n"},{"id":"389582","messageId":"f545a13f-4b3c-9603-7511-ff80ad950ee9@gmail.com","threadId":"52048","inReplyTo":"20200108060842.GA51465@generichostname","subject":"Re: [PATCH v2 00/17] merge: learn --autostash","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-01-10T14:44:30Z","receivedAt":"2020-01-10T14:44:35Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 08/01/2020 06:08, Denton Liu wrote:\n> Hi Phillip,\n> \n> Sorry for the late reply. I'm back in school so I've been pretty busy\n> lately.\n> \n> On Tue, Dec 31, 2019 at 10:34:30AM +0000, Phillip Wood wrote:\n>> For `merge --autostash` I think we need to consider the interaction of\n>> `--no-commit` with `--autostash` as `stash pop` will refuse to run if the\n>> merge modified any of the stashed paths.\n> \n> The only time when we run the `stash pop` is when we either commit the\n> merge or abort it. With this in mind, what do you think of the following\n> test cases? If they look good, I can squash them into the appropriate\n> patch and send in a reroll.\n\nAh I misunderstood what happened with --autostash and --no-commit, the \ntests basically look fine (I've got one comment below).\n\nOne other thing - if the user runs `git reset` or `git checkout \n<branch>` then cleanup_branch_state() removes MERGE_HEAD, MERGE_MSG etc. \nIf we're not already doing so then I think we should remove \nMERGE_AUTOSTASH as well or you can get into a situation where the user \ndoes something like\n\n   git merge --autostash <something> # results in conflicts\n   git reset --hard <somewhere else>\n   git merge <something> # succeeds and confusingly pops previous stash\n\nRunning `git reset` doesn't make sense unless they want to discard the \nstashed changes as well. This is a difference with rebase where you \ncannot lose the stashed changes by running `git reset`, the only way \nthey can get lost is by running `rebase --quit`.  We could always add a \nwarning about it throwing away the stashed changes in the future.\n\nI still not keen on the name `--autostash` as it's not automatic. \n`--stash` would make more sense to me. We'd have to deprecate `rebase \n--autostash` in favor of `rebase --stash` to change it but it if we want \nto change the name it would be better now before adding `--autostash` to \nmerge and pull - what do you think?\n\n> \tdiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\n> \tindex e0c8a0bad4..af5db58e16 100755\n> \t--- a/t/t7600-merge.sh\n> \t+++ b/t/t7600-merge.sh\n> \t@@ -727,6 +727,33 @@ test_expect_success 'conflicted merge with --autostash, --abort restores stash'\n> \t\ttest_cmp file.1 file\n> \t '\n> \t\n> \t+test_expect_success 'completed merge with --no-commit and --autostash' '\n> \t+\tgit reset --hard c1 &&\n> \t+\tgit merge-file file file.orig file.9 &&\n\nIs this a complicated way of getting some unstaged changes so we can \nstash them or have I missed something?\n\nBest Wishes\n\nPhillip\n\n> \t+\tgit diff >expect &&\n> \t+\tgit merge --no-commit --autostash c2 &&\n> \t+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n> \t+\ttest_cmp expect actual &&\n> \t+\tgit commit 2>err &&\n> \t+\ttest_i18ngrep \"Applied autostash.\" err &&\n> \t+\tgit show HEAD:file >merge-result &&\n> \t+\ttest_cmp result.1-5 merge-result &&\n> \t+\ttest_cmp result.1-5-9 file\n> \t+'\n> \t+\n> \t+test_expect_success 'aborted merge with --no-commit and --autostash' '\n> \t+\tgit reset --hard c1 &&\n> \t+\tgit merge-file file file.orig file.9 &&\n> \t+\tgit diff >expect &&\n> \t+\tgit merge --no-commit --autostash c2 &&\n> \t+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n> \t+\ttest_cmp expect actual &&\n> \t+\tgit merge --abort 2>err &&\n> \t+\ttest_i18ngrep \"Applied autostash.\" err &&\n> \t+\tgit diff >actual &&\n> \t+\ttest_cmp expect actual\n> \t+'\n> \t+\n> \t test_expect_success 'merge with conflicted --autostash changes' '\n> \t\tgit reset --hard c1 &&\n> \t\tgit merge-file file file.orig file.9y &&\n> \n> Thanks,\n> \n> Denton\n> \n>>\n>> Best Wishes\n>>\n>> Phillip\n"},{"id":"389805","messageId":"20200115162014.GA51221@generichostname","threadId":"52048","inReplyTo":"f545a13f-4b3c-9603-7511-ff80ad950ee9@gmail.com","subject":"Re: [PATCH v2 00/17] merge: learn --autostash","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-01-15T16:20:14Z","receivedAt":"2020-01-15T16:20:19Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"On Fri, Jan 10, 2020 at 02:44:30PM +0000, Phillip Wood wrote:\n> Hi Denton\n> \n> On 08/01/2020 06:08, Denton Liu wrote:\n> > Hi Phillip,\n> > \n> > Sorry for the late reply. I'm back in school so I've been pretty busy\n> > lately.\n> > \n> > On Tue, Dec 31, 2019 at 10:34:30AM +0000, Phillip Wood wrote:\n> > > For `merge --autostash` I think we need to consider the interaction of\n> > > `--no-commit` with `--autostash` as `stash pop` will refuse to run if the\n> > > merge modified any of the stashed paths.\n> > \n> > The only time when we run the `stash pop` is when we either commit the\n> > merge or abort it. With this in mind, what do you think of the following\n> > test cases? If they look good, I can squash them into the appropriate\n> > patch and send in a reroll.\n> \n> Ah I misunderstood what happened with --autostash and --no-commit, the tests\n> basically look fine (I've got one comment below).\n> \n> One other thing - if the user runs `git reset` or `git checkout <branch>`\n> then cleanup_branch_state() removes MERGE_HEAD, MERGE_MSG etc. If we're not\n> already doing so then I think we should remove MERGE_AUTOSTASH as well or\n> you can get into a situation where the user does something like\n\nIn cleanup_branch_state(), I insert `apply_autostash()` at the very end\nof the function so that the stash is popped whenever this is called.\n\n>   git merge --autostash <something> # results in conflicts\n>   git reset --hard <somewhere else>\n>   git merge <something> # succeeds and confusingly pops previous stash\n> \n> Running `git reset` doesn't make sense unless they want to discard the\n> stashed changes as well. This is a difference with rebase where you cannot\n> lose the stashed changes by running `git reset`, the only way they can get\n> lost is by running `rebase --quit`.  We could always add a warning about it\n> throwing away the stashed changes in the future.\n\nCurrently, in rebase, if the stash cannot be popped cleanly, it is\nautomatically pushed onto the stash stack so that the user can deal with\nit later. Do we want to do a similar thing where if we `reset --hard`\nwith an autostash present, we store the stash and then leave the users\nwith a clean worktree (as they'd expect)?\n\n> I still not keen on the name `--autostash` as it's not automatic. `--stash`\n> would make more sense to me. We'd have to deprecate `rebase --autostash` in\n> favor of `rebase --stash` to change it but it if we want to change the name\n> it would be better now before adding `--autostash` to merge and pull - what\n> do you think?\n\nEven though I agree with you that `--autostash` would be better named as\n`--stash`, I feel that it's worse than having argument names that\nperform the same functionality but with different names. So I'd be\ninclined to keep `--autostash`.\n\n> \n> > \tdiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\n> > \tindex e0c8a0bad4..af5db58e16 100755\n> > \t--- a/t/t7600-merge.sh\n> > \t+++ b/t/t7600-merge.sh\n> > \t@@ -727,6 +727,33 @@ test_expect_success 'conflicted merge with --autostash, --abort restores stash'\n> > \t\ttest_cmp file.1 file\n> > \t '\n> > \t\n> > \t+test_expect_success 'completed merge with --no-commit and --autostash' '\n> > \t+\tgit reset --hard c1 &&\n> > \t+\tgit merge-file file file.orig file.9 &&\n> \n> Is this a complicated way of getting some unstaged changes so we can stash\n> them or have I missed something?\n\nYes, that's exactly what this does.\n\n> \n> Best Wishes\n> \n> Phillip\n> \n> > \t+\tgit diff >expect &&\n> > \t+\tgit merge --no-commit --autostash c2 &&\n> > \t+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n> > \t+\ttest_cmp expect actual &&\n> > \t+\tgit commit 2>err &&\n> > \t+\ttest_i18ngrep \"Applied autostash.\" err &&\n> > \t+\tgit show HEAD:file >merge-result &&\n> > \t+\ttest_cmp result.1-5 merge-result &&\n> > \t+\ttest_cmp result.1-5-9 file\n> > \t+'\n> > \t+\n> > \t+test_expect_success 'aborted merge with --no-commit and --autostash' '\n> > \t+\tgit reset --hard c1 &&\n> > \t+\tgit merge-file file file.orig file.9 &&\n> > \t+\tgit diff >expect &&\n> > \t+\tgit merge --no-commit --autostash c2 &&\n> > \t+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n> > \t+\ttest_cmp expect actual &&\n> > \t+\tgit merge --abort 2>err &&\n> > \t+\ttest_i18ngrep \"Applied autostash.\" err &&\n> > \t+\tgit diff >actual &&\n> > \t+\ttest_cmp expect actual\n> > \t+'\n> > \t+\n> > \t test_expect_success 'merge with conflicted --autostash changes' '\n> > \t\tgit reset --hard c1 &&\n> > \t\tgit merge-file file file.orig file.9y &&\n> > \n> > Thanks,\n> > \n> > Denton\n> > \n> > > \n> > > Best Wishes\n> > > \n> > > Phillip\n"},{"id":"393587","messageId":"70699b8b73c1e86cdbe47d9d4162bf0b678a9dbc.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 02/19] t7600: use test_write_lines()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:21Z","receivedAt":"2020-03-21T09:22:13Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In t7600, we were rewriting `printf '%s\\n' ...` to create files from\nparameters, one per line. However, we already have a function that wraps\nthis for us: test_write_lines(). Rewrite these instances to use that\nfunction instead of open coding it.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7600-merge.sh | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 132608879a..4fa0ef8e3b 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -29,15 +29,15 @@ Testing basic merge operations/option parsing.\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-gpg.sh\n \n-printf '%s\\n' 1 2 3 4 5 6 7 8 9 >file\n-printf '%s\\n' '1 X' 2 3 4 5 6 7 8 9 >file.1\n-printf '%s\\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5\n-printf '%s\\n' 1 2 3 4 5 6 7 8 '9 X' >file.9\n-printf '%s\\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n-printf '%s\\n' '1 X' 2 3 4 5 6 7 8 9 >result.1\n-printf '%s\\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n-printf '%s\\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n-printf '%s\\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n+test_write_lines 1 2 3 4 5 6 7 8 9 >file\n+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1\n+test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5\n+test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9\n+test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1\n+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n+test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n \n create_merge_msgs () {\n \techo \"Merge tag 'c2'\" >msg.1-5 &&\n@@ -81,7 +81,7 @@ verify_head () {\n }\n \n verify_parents () {\n-\tprintf '%s\\n' \"$@\" >parents.expected &&\n+\ttest_write_lines \"$@\" >parents.expected &&\n \t>parents.actual &&\n \ti=1 &&\n \twhile test $i -le $#\n@@ -95,7 +95,7 @@ verify_parents () {\n }\n \n verify_mergeheads () {\n-\tprintf '%s\\n' \"$@\" >mergehead.expected &&\n+\ttest_write_lines \"$@\" >mergehead.expected &&\n \twhile read sha1 rest\n \tdo\n \t\tgit rev-parse $sha1\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393588","messageId":"cover.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1577185374.git.liu.denton@gmail.com","subject":"[PATCH v3 00/19] merge: learn --autostash","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:19Z","receivedAt":"2020-03-21T09:22:13Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Sorry for the delay in rerolling this. It's been quite long since I've\nhad free time. This probaly requires a fresh review since quite a lot\nhas changed since I posted my last version.\n\n\nAlban reported[1] that he expected merge to have an --autostash option,\njust like rebase. Since there's not really any reason why merge can't\nhave it, let's implement it in this patchset.\n\nThe majority of this patchset is spent refactoring. This is so that the\nactual implementation in merge is done as simply as possible.\n\nChanges since v2:\n\n* Change patch 1 to refer to ASCII order[2]\n\n* Make read_oneliner() accept a collective flags arg instead of two\n  separate args[3]\n\n* `git reset --hard` now leaves the worktree clean[4]\n\n* Rebased against 'master' since the base is quite old and a new release is\n  coming out soon\n\nChanges since v1:\n\n* Completely changed how the refactoring was done\n\n* More tests and documentation\n\n[1]: https://github.com/gitgitgadget/git/issues/394\n[2]: https://lore.kernel.org/git/xmqqwoadfq8n.fsf@gitster-ct.c.googlers.com/\n[3]: https://lore.kernel.org/git/xmqqwoaikehz.fsf@gitster-ct.c.googlers.com/\n[4]: https://lore.kernel.org/git/20200123042906.GA29009@generichostname/\n\nDenton Liu (19):\n  Makefile: ASCII-sort += lists\n  t7600: use test_write_lines()\n  sequencer: use file strbuf for read_oneliner()\n  sequencer: make read_oneliner() accept flags\n  sequencer: configurably warn on non-existent files\n  sequencer: make read_oneliner() extern\n  rebase: use read_oneliner()\n  sequencer: make apply_rebase() accept a path\n  rebase: use apply_autostash() from sequencer.c\n  rebase: generify reset_head()\n  reset: extract reset_head() from rebase\n  rebase: extract create_autostash()\n  rebase: generify create_autostash()\n  sequencer: extract perform_autostash() from rebase\n  sequencer: unlink autostash in apply_autostash()\n  sequencer: implement save_autostash()\n  merge: teach --autostash option\n  t5520: make test_pull_autostash() accept expect_parent_num\n  pull: pass --autostash to merge\n\n Documentation/config/merge.txt  |  10 ++\n Documentation/git-pull.txt      |   9 -\n Documentation/merge-options.txt |   8 +\n Makefile                        |  78 ++++----\n branch.c                        |   1 +\n builtin/commit.c                |   2 +\n builtin/merge.c                 |  17 ++\n builtin/pull.c                  |   9 +-\n builtin/rebase.c                | 304 +++++---------------------------\n builtin/reset.c                 |   7 +-\n path.c                          |   1 +\n path.h                          |   4 +-\n reset.c                         | 140 +++++++++++++++\n reset.h                         |  20 +++\n sequencer.c                     | 152 +++++++++++-----\n sequencer.h                     |  19 ++\n t/t3033-merge-toplevel.sh       |  22 +++\n t/t5520-pull.sh                 |  57 ++++--\n t/t7600-merge.sh                | 126 +++++++++++--\n 19 files changed, 604 insertions(+), 382 deletions(-)\n create mode 100644 reset.c\n create mode 100644 reset.h\n\nRange-diff against v2:\n 1:  31055cdac4 !  1:  77506a39d0 Makefile: alphabetically sort += lists\n    @@ Metadata\n     Author: Denton Liu <liu.denton@gmail.com>\n     \n      ## Commit message ##\n    -    Makefile: alphabetically sort += lists\n    +    Makefile: ASCII-sort += lists\n     \n         There are many += lists in the Makefile and, over time, they have gotten\n    -    slightly out of order, alphabetically. Alphabetically sort all += lists\n    -    to bring them back in order.\n    +    slightly out of ASCII order. Sort all += lists to bring them back in\n    +    order.\n     \n    -    Note that if we omit file prefixes, the lists aren't sorted in strictly\n    -    alphabetical order (e.g. archive.o comes after archive-zip.o instead of\n    -    before archive-tar.o). This is intentional because the purpose of\n    -    maintaining the sorted list is to ensure line insertions are\n    -    deterministic. As a result, we want to ensure that anybody can easily\n    -    reproduce the sort order and that is very simple when we don't have to\n    -    treat the prefix specially.\n    +    ASCII sorting was chosen over strict alphabetical order even though, if\n    +    we omit file prefixes, the lists aren't sorted in strictly alphabetical\n    +    order (e.g. archive.o comes after archive-zip.o instead of before\n    +    archive-tar.o). This is intentional because the purpose of maintaining\n    +    the sorted list is to ensure line insertions are deterministic. By using\n    +    ASCII ordering, it is more easily mechanically reproducible in the\n    +    future, such as by using :sort in Vim.\n     \n         This patch is best viewed with `--color-moved`.\n     \n    @@ Makefile: LIB_OBJS += mailinfo.o\n      LIB_OBJS += patch-delta.o\n      LIB_OBJS += patch-ids.o\n      LIB_OBJS += path.o\n    -@@ Makefile: LIB_OBJS += range-diff.o\n    +@@ Makefile: LIB_OBJS += quote.o\n    + LIB_OBJS += range-diff.o\n      LIB_OBJS += reachable.o\n      LIB_OBJS += read-cache.o\n    +-LIB_OBJS += rebase.o\n      LIB_OBJS += rebase-interactive.o\n    ++LIB_OBJS += rebase.o\n     +LIB_OBJS += ref-filter.o\n      LIB_OBJS += reflog-walk.o\n      LIB_OBJS += refs.o\n 2:  2e0020b3e3 =  2:  70699b8b73 t7600: use test_write_lines()\n 3:  de79e27c17 =  3:  7c37777f07 sequencer: use file strbuf for read_oneliner()\n 4:  fd547aab49 <  -:  ---------- sequencer: configurably warn on non-existent files\n -:  ---------- >  4:  4f97086828 sequencer: make read_oneliner() accept flags\n -:  ---------- >  5:  e42f4bdd4d sequencer: configurably warn on non-existent files\n 5:  819fb2203b !  6:  e4d0c2d902 sequencer: make read_oneliner() extern\n    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n      \treturn 0;\n      }\n      \n    +-#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n    +-#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)\n    +-\n     -/*\n     - * Reads a file that was presumably written by a shell script, i.e. with an\n     - * end-of-line marker that needs to be stripped.\n    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n     - *\n     - * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n     - */\n    --static int read_oneliner(struct strbuf *buf, const char *path,\n    --\t\t\t int skip_if_empty, int warn_nonexistence)\n    -+int read_oneliner(struct strbuf *buf, const char *path,\n    -+\t\t  int skip_if_empty, int warn_nonexistence)\n    +-static int read_oneliner(struct strbuf *buf,\n    ++int read_oneliner(struct strbuf *buf,\n    + \tconst char *path, unsigned flags)\n      {\n      \tint ret = 0;\n    - \tstruct strbuf file_buf = STRBUF_INIT;\n     \n      ## sequencer.h ##\n     @@ sequencer.h: void print_commit_summary(struct repository *repo,\n      \t\t\t  const struct object_id *oid,\n      \t\t\t  unsigned int flags);\n      \n    ++#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n    ++#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)\n    ++\n     +/*\n     + * Reads a file that was presumably written by a shell script, i.e. with an\n     + * end-of-line marker that needs to be stripped.\n    @@ sequencer.h: void print_commit_summary(struct repository *repo,\n     + *\n     + * Returns 1 if the file was read, 0 if it could not be read.\n     + */\n    -+int read_oneliner(struct strbuf *buf, const char *path,\n    -+\t\t  int skip_if_empty, int warn_nonexistence);\n    ++int read_oneliner(struct strbuf *buf,\n    ++\tconst char *path, unsigned flags);\n      int read_author_script(const char *path, char **name, char **email, char **date,\n      \t\t       int allow_missing);\n      void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);\n 6:  06c5bd48d7 !  7:  d07a50c5b2 rebase: use read_oneliner()\n    @@ Commit message\n         of read_oneliner(), reduce code duplication by replacing read_one() with\n         read_oneliner().\n     \n    -    This was done with the following Coccinelle script:\n    +    This was done with the following Coccinelle script\n     \n                 @@\n                 expression a, b;\n                 @@\n                 - read_one(a, b)\n    -            + !read_oneliner(b, a, 0, 1)\n    +            + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)\n    +\n    +    and long lines were manually broken up.\n     \n      ## builtin/rebase.c ##\n     @@ builtin/rebase.c: static const char *state_dir_path(const char *filename, struct rebase_options *o\n    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n      \n     -\tif (read_one(state_dir_path(\"head-name\", opts), &head_name) ||\n     -\t    read_one(state_dir_path(\"onto\", opts), &buf))\n    -+\tif (!read_oneliner(&head_name, state_dir_path(\"head-name\", opts), 0, 1) ||\n    -+\t    !read_oneliner(&buf, state_dir_path(\"onto\", opts), 0, 1))\n    ++\tif (!read_oneliner(&head_name, state_dir_path(\"head-name\", opts),\n    ++\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE) ||\n    ++\t    !read_oneliner(&buf, state_dir_path(\"onto\", opts),\n    ++\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n      \t\treturn -1;\n      \topts->head_name = starts_with(head_name.buf, \"refs/\") ?\n      \t\txstrdup(head_name.buf) : NULL;\n    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n      \tstrbuf_reset(&buf);\n      \tif (file_exists(state_dir_path(\"orig-head\", opts))) {\n     -\t\tif (read_one(state_dir_path(\"orig-head\", opts), &buf))\n    -+\t\tif (!read_oneliner(&buf, state_dir_path(\"orig-head\", opts), 0, 1))\n    ++\t\tif (!read_oneliner(&buf, state_dir_path(\"orig-head\", opts),\n    ++\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n      \t\t\treturn -1;\n     -\t} else if (read_one(state_dir_path(\"head\", opts), &buf))\n    -+\t} else if (!read_oneliner(&buf, state_dir_path(\"head\", opts), 0, 1))\n    ++\t} else if (!read_oneliner(&buf, state_dir_path(\"head\", opts),\n    ++\t\t\t\t  READ_ONELINER_WARN_NON_EXISTENCE))\n      \t\treturn -1;\n      \tif (get_oid(buf.buf, &opts->orig_head))\n      \t\treturn error(_(\"invalid orig-head: '%s'\"), buf.buf);\n    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n      \t\tstrbuf_reset(&buf);\n     -\t\tif (read_one(state_dir_path(\"allow_rerere_autoupdate\", opts),\n     -\t\t\t    &buf))\n    -+\t\tif (!read_oneliner(&buf, state_dir_path(\"allow_rerere_autoupdate\", opts), 0, 1))\n    ++\t\tif (!read_oneliner(&buf, state_dir_path(\"allow_rerere_autoupdate\", opts),\n    ++\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n      \t\t\treturn -1;\n      \t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n      \t\t\topts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;\n    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n      \t\tstrbuf_reset(&buf);\n     -\t\tif (read_one(state_dir_path(\"gpg_sign_opt\", opts),\n     -\t\t\t    &buf))\n    -+\t\tif (!read_oneliner(&buf, state_dir_path(\"gpg_sign_opt\", opts), 0, 1))\n    ++\t\tif (!read_oneliner(&buf, state_dir_path(\"gpg_sign_opt\", opts),\n    ++\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n      \t\t\treturn -1;\n      \t\tfree(opts->gpg_sign_opt);\n      \t\topts->gpg_sign_opt = xstrdup(buf.buf);\n    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n      \tif (file_exists(state_dir_path(\"strategy\", opts))) {\n      \t\tstrbuf_reset(&buf);\n     -\t\tif (read_one(state_dir_path(\"strategy\", opts), &buf))\n    -+\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy\", opts), 0, 1))\n    ++\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy\", opts),\n    ++\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n      \t\t\treturn -1;\n      \t\tfree(opts->strategy);\n      \t\topts->strategy = xstrdup(buf.buf);\n    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n      \tif (file_exists(state_dir_path(\"strategy_opts\", opts))) {\n      \t\tstrbuf_reset(&buf);\n     -\t\tif (read_one(state_dir_path(\"strategy_opts\", opts), &buf))\n    -+\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy_opts\", opts), 0, 1))\n    ++\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy_opts\", opts),\n    ++\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n      \t\t\treturn -1;\n      \t\tfree(opts->strategy_opts);\n      \t\topts->strategy_opts = xstrdup(buf.buf);\n    @@ builtin/rebase.c: static int apply_autostash(struct rebase_options *opts)\n      \t\treturn 0;\n      \n     -\tif (read_one(path, &autostash))\n    -+\tif (!read_oneliner(&autostash, path, 0, 1))\n    ++\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))\n      \t\treturn error(_(\"Could not read '%s'\"), path);\n      \t/* Ensure that the hash is not mistaken for a number */\n      \tstrbuf_addstr(&autostash, \"^0\");\n 7:  12c37d11ba !  8:  5bce4aeb96 sequencer: make apply_rebase() accept a path\n    @@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,\n      \tstruct child_process child = CHILD_PROCESS_INIT;\n      \tint ret = 0;\n      \n    --\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1, 0)) {\n    -+\tif (!read_oneliner(&stash_sha1, path, 1, 0)) {\n    +-\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(),\n    ++\tif (!read_oneliner(&stash_sha1, path,\n    + \t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n      \t\tstrbuf_release(&stash_sha1);\n      \t\treturn 0;\n    - \t}\n     @@ sequencer.c: static int checkout_onto(struct repository *r, struct replay_opts *opts,\n      \t\treturn error(_(\"%s: not a valid OID\"), orig_head);\n      \n 8:  893a2f9825 !  9:  03bdaeebc9 rebase: use apply_autostash() from sequencer.c\n    @@ builtin/rebase.c: static int rebase_write_basic_state(struct rebase_options *opt\n     -\tif (!file_exists(path))\n     -\t\treturn 0;\n     -\n    --\tif (!read_oneliner(&autostash, path, 0, 1))\n    +-\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))\n     -\t\treturn error(_(\"Could not read '%s'\"), path);\n     -\t/* Ensure that the hash is not mistaken for a number */\n     -\tstrbuf_addstr(&autostash, \"^0\");\n    @@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,\n     \n      ## sequencer.h ##\n     @@ sequencer.h: void commit_post_rewrite(struct repository *r,\n    - int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,\n    - \t\t\t\t const char *commit);\n    + \t\t\t const struct commit *current_head,\n    + \t\t\t const struct object_id *new_head);\n      \n     +int apply_autostash(const char *path);\n     +\n 9:  8e3c0c0aec ! 10:  c35c5bdb8c rebase: generify reset_head()\n    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n      \t\t\t\tstrbuf_addf(&buf, \"%s: checkout %s\",\n      \t\t\t\t\t    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),\n      \t\t\t\t\t    options.switch_to);\n    --\t\t\t\tif (reset_head(&oid, \"checkout\",\n    -+\t\t\t\tif (reset_head(the_repository, &oid, \"checkout\",\n    +-\t\t\t\tif (reset_head(&options.orig_head, \"checkout\",\n    ++\t\t\t\tif (reset_head(the_repository,\n    ++\t\t\t\t\t       &options.orig_head, \"checkout\",\n      \t\t\t\t\t       options.head_name,\n      \t\t\t\t\t       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,\n     -\t\t\t\t\t       NULL, buf.buf) < 0) {\n10:  981969d230 = 11:  a14a2e0849 reset: extract reset_head() from rebase\n11:  1b5bbe66dc = 12:  1dbae3dfbf rebase: extract create_autostash()\n12:  bba7586cba = 13:  fffc038cd6 rebase: generify create_autostash()\n13:  2ff189692f ! 14:  bf55b86e2b sequencer: extract perform_autostash() from rebase\n    @@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,\n     \n      ## sequencer.h ##\n     @@ sequencer.h: void commit_post_rewrite(struct repository *r,\n    - int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,\n    - \t\t\t\t const char *commit);\n    + \t\t\t const struct commit *current_head,\n    + \t\t\t const struct object_id *new_head);\n      \n     +void create_autostash(struct repository *r, const char *path,\n     +\t\t      const char *default_reflog_action);\n14:  2b16711680 = 15:  d5af261eb8 sequencer: unlink autostash in apply_autostash()\n -:  ---------- > 16:  e21e2c6416 sequencer: implement save_autostash()\n15:  6e987052c0 ! 17:  9e3d4393ca merge: teach --autostash option\n    @@ Commit message\n                 git fetch ...\n                 git merge --autostash FETCH_HEAD\n     \n    +    When `git reset --hard` is run to abort a merge, the working tree will\n    +    be left in a clean state, as expected, with the autostash pushed onto\n    +    the stash stack.\n    +\n         Suggested-by: Alban Gruin <alban.gruin@gmail.com>\n     \n      ## Documentation/config/merge.txt ##\n    @@ builtin/merge.c: static void finish(struct commit *head_commit,\n      \t\tsquash_message(head_commit, remoteheads);\n      \t} else {\n     @@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void *cb)\n    - \t} else if (!strcmp(k, \"commit.gpgsign\")) {\n    - \t\tsign_commit = git_config_bool(k, v) ? \"\" : NULL;\n      \t\treturn 0;\n    + \t} else if (!strcmp(k, \"gpg.mintrustlevel\")) {\n    + \t\tcheck_trust_level = 0;\n     +\t} else if (!strcmp(k, \"merge.autostash\")) {\n     +\t\tautostash = git_config_bool(k, v);\n     +\t\treturn 0;\n    @@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)\n      \tgit_committer_info(IDENT_STRICT);\n      \n     \n    + ## builtin/reset.c ##\n    +@@\n    + #include \"cache-tree.h\"\n    + #include \"submodule.h\"\n    + #include \"submodule-config.h\"\n    ++#include \"sequencer.h\"\n    + \n    + #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n    + \n    +@@ builtin/reset.c: int cmd_reset(int argc, const char **argv, const char *prefix)\n    + \t\tif (reset_type == HARD && !update_ref_status && !quiet)\n    + \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n    + \t}\n    +-\tif (!pathspec.nr)\n    ++\tif (!pathspec.nr) {\n    ++\t\tif (reset_type == HARD)\n    ++\t\t\tsave_autostash(git_path_merge_autostash(the_repository));\n    ++\n    + \t\tremove_branch_state(the_repository, 0);\n    ++\t}\n    + \n    + \treturn update_ref_status;\n    + }\n    +\n      ## path.c ##\n     @@ path.c: REPO_GIT_PATH_FUNC(merge_msg, \"MERGE_MSG\")\n      REPO_GIT_PATH_FUNC(merge_rr, \"MERGE_RR\")\n    @@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '\n     +\ttest_cmp file.1 file\n     +'\n     +\n    ++test_expect_success 'completed merge with --no-commit and --autostash' '\n    ++\tgit reset --hard c1 &&\n    ++\tgit merge-file file file.orig file.9 &&\n    ++\tgit diff >expect &&\n    ++\tgit merge --no-commit --autostash c2 &&\n    ++\tgit stash show -p MERGE_AUTOSTASH >actual &&\n    ++\ttest_cmp expect actual &&\n    ++\tgit commit 2>err &&\n    ++\ttest_i18ngrep \"Applied autostash.\" err &&\n    ++\tgit show HEAD:file >merge-result &&\n    ++\ttest_cmp result.1-5 merge-result &&\n    ++\ttest_cmp result.1-5-9 file\n    ++'\n    ++\n    ++test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '\n    ++\tgit reset --hard c1 &&\n    ++\tgit merge-file file file.orig file.9 &&\n    ++\tgit diff >expect &&\n    ++\tgit merge --no-commit --autostash c2 &&\n    ++\tgit stash show -p MERGE_AUTOSTASH >actual &&\n    ++\ttest_cmp expect actual &&\n    ++\tgit merge --abort 2>err &&\n    ++\ttest_i18ngrep \"Applied autostash.\" err &&\n    ++\tgit diff >actual &&\n    ++\ttest_cmp expect actual\n    ++'\n    ++\n    ++test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '\n    ++\tgit reset --hard c1 &&\n    ++\tgit merge-file file file.orig file.9 &&\n    ++\tgit diff >expect &&\n    ++\tgit merge --no-commit --autostash c2 &&\n    ++\tgit stash show -p MERGE_AUTOSTASH >actual &&\n    ++\ttest_cmp expect actual &&\n    ++\tgit reset --hard 2>err &&\n    ++\ttest_i18ngrep \"Autostash exists; creating a new stash entry.\" err &&\n    ++\tgit diff --exit-code\n    ++'\n    ++\n     +test_expect_success 'merge with conflicted --autostash changes' '\n     +\tgit reset --hard c1 &&\n     +\tgit merge-file file file.orig file.9y &&\n16:  c70643ca9f = 18:  72aa56516e t5520: make test_pull_autostash() accept expect_parent_num\n17:  6ebae1d758 = 19:  17caf6d66f pull: pass --autostash to merge\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393589","messageId":"77506a39d04c838773757b189e1b409ce98d612e.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 01/19] Makefile: ASCII-sort += lists","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:20Z","receivedAt":"2020-03-21T09:22:13Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"There are many += lists in the Makefile and, over time, they have gotten\nslightly out of ASCII order. Sort all += lists to bring them back in\norder.\n\nASCII sorting was chosen over strict alphabetical order even though, if\nwe omit file prefixes, the lists aren't sorted in strictly alphabetical\norder (e.g. archive.o comes after archive-zip.o instead of before\narchive-tar.o). This is intentional because the purpose of maintaining\nthe sorted list is to ensure line insertions are deterministic. By using\nASCII ordering, it is more easily mechanically reproducible in the\nfuture, such as by using :sort in Vim.\n\nThis patch is best viewed with `--color-moved`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Makefile | 77 ++++++++++++++++++++++++++++----------------------------\n 1 file changed, 39 insertions(+), 38 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 9804a0758b..f709fc3f77 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -604,12 +604,12 @@ unexport CDPATH\n SCRIPT_SH += git-bisect.sh\n SCRIPT_SH += git-difftool--helper.sh\n SCRIPT_SH += git-filter-branch.sh\n+SCRIPT_SH += git-legacy-stash.sh\n SCRIPT_SH += git-merge-octopus.sh\n SCRIPT_SH += git-merge-one-file.sh\n SCRIPT_SH += git-merge-resolve.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-quiltimport.sh\n-SCRIPT_SH += git-legacy-stash.sh\n SCRIPT_SH += git-request-pull.sh\n SCRIPT_SH += git-submodule.sh\n SCRIPT_SH += git-web--browse.sh\n@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh\n SCRIPT_LIB += git-mergetool--lib\n SCRIPT_LIB += git-parse-remote\n SCRIPT_LIB += git-rebase--preserve-merges\n-SCRIPT_LIB += git-sh-setup\n SCRIPT_LIB += git-sh-i18n\n+SCRIPT_LIB += git-sh-setup\n \n SCRIPT_PERL += git-add--interactive.perl\n SCRIPT_PERL += git-archimport.perl\n@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o\n PROGRAM_OBJS += fast-import.o\n PROGRAM_OBJS += http-backend.o\n PROGRAM_OBJS += imap-send.o\n+PROGRAM_OBJS += remote-testsvn.o\n PROGRAM_OBJS += sh-i18n--envsubst.o\n PROGRAM_OBJS += shell.o\n-PROGRAM_OBJS += remote-testsvn.o\n \n # Binary suffix, set to .exe for Windows builds\n X =\n@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o\n TEST_BUILTINS_OBJS += test-example-decorate.o\n TEST_BUILTINS_OBJS += test-genrandom.o\n TEST_BUILTINS_OBJS += test-genzeros.o\n+TEST_BUILTINS_OBJS += test-hash-speed.o\n TEST_BUILTINS_OBJS += test-hash.o\n TEST_BUILTINS_OBJS += test-hashmap.o\n-TEST_BUILTINS_OBJS += test-hash-speed.o\n TEST_BUILTINS_OBJS += test-index-version.o\n TEST_BUILTINS_OBJS += test-json-writer.o\n TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o\n@@ -737,8 +737,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o\n TEST_BUILTINS_OBJS += test-run-command.o\n TEST_BUILTINS_OBJS += test-scrap-cache-tree.o\n TEST_BUILTINS_OBJS += test-serve-v2.o\n-TEST_BUILTINS_OBJS += test-sha1.o\n TEST_BUILTINS_OBJS += test-sha1-array.o\n+TEST_BUILTINS_OBJS += test-sha1.o\n TEST_BUILTINS_OBJS += test-sha256.o\n TEST_BUILTINS_OBJS += test-sigchain.o\n TEST_BUILTINS_OBJS += test-strcmp-offset.o\n@@ -748,10 +748,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o\n TEST_BUILTINS_OBJS += test-subprocess.o\n TEST_BUILTINS_OBJS += test-trace2.o\n TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n-TEST_BUILTINS_OBJS += test-xml-encode.o\n TEST_BUILTINS_OBJS += test-wildmatch.o\n TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n TEST_BUILTINS_OBJS += test-write-cache.o\n+TEST_BUILTINS_OBJS += test-xml-encode.o\n \n # Do not add more tests here unless they have extra dependencies. Add\n # them in TEST_BUILTINS_OBJS above.\n@@ -788,10 +788,10 @@ OTHER_PROGRAMS = git$X\n \n # what test wrappers are needed and 'install' will install, in bindir\n BINDIR_PROGRAMS_NEED_X += git\n-BINDIR_PROGRAMS_NEED_X += git-upload-pack\n BINDIR_PROGRAMS_NEED_X += git-receive-pack\n-BINDIR_PROGRAMS_NEED_X += git-upload-archive\n BINDIR_PROGRAMS_NEED_X += git-shell\n+BINDIR_PROGRAMS_NEED_X += git-upload-archive\n+BINDIR_PROGRAMS_NEED_X += git-upload-pack\n \n BINDIR_PROGRAMS_NO_X += git-cvsserver\n \n@@ -831,9 +831,9 @@ LIB_OBJS += advice.o\n LIB_OBJS += alias.o\n LIB_OBJS += alloc.o\n LIB_OBJS += apply.o\n-LIB_OBJS += archive.o\n LIB_OBJS += archive-tar.o\n LIB_OBJS += archive-zip.o\n+LIB_OBJS += archive.o\n LIB_OBJS += argv-array.o\n LIB_OBJS += attr.o\n LIB_OBJS += base85.o\n@@ -849,9 +849,9 @@ LIB_OBJS += checkout.o\n LIB_OBJS += color.o\n LIB_OBJS += column.o\n LIB_OBJS += combine-diff.o\n-LIB_OBJS += commit.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n+LIB_OBJS += commit.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\n LIB_OBJS += config.o\n@@ -865,17 +865,17 @@ LIB_OBJS += ctype.o\n LIB_OBJS += date.o\n LIB_OBJS += decorate.o\n LIB_OBJS += delta-islands.o\n+LIB_OBJS += diff-delta.o\n+LIB_OBJS += diff-lib.o\n+LIB_OBJS += diff-no-index.o\n+LIB_OBJS += diff.o\n LIB_OBJS += diffcore-break.o\n LIB_OBJS += diffcore-delta.o\n LIB_OBJS += diffcore-order.o\n LIB_OBJS += diffcore-pickaxe.o\n LIB_OBJS += diffcore-rename.o\n-LIB_OBJS += diff-delta.o\n-LIB_OBJS += diff-lib.o\n-LIB_OBJS += diff-no-index.o\n-LIB_OBJS += diff.o\n-LIB_OBJS += dir.o\n LIB_OBJS += dir-iterator.o\n+LIB_OBJS += dir.o\n LIB_OBJS += editor.o\n LIB_OBJS += entry.o\n LIB_OBJS += environment.o\n@@ -893,7 +893,6 @@ LIB_OBJS += gpg-interface.o\n LIB_OBJS += graph.o\n LIB_OBJS += grep.o\n LIB_OBJS += hashmap.o\n-LIB_OBJS += linear-assignment.o\n LIB_OBJS += help.o\n LIB_OBJS += hex.o\n LIB_OBJS += ident.o\n@@ -903,9 +902,10 @@ LIB_OBJS += kwset.o\n LIB_OBJS += levenshtein.o\n LIB_OBJS += line-log.o\n LIB_OBJS += line-range.o\n-LIB_OBJS += list-objects.o\n-LIB_OBJS += list-objects-filter.o\n+LIB_OBJS += linear-assignment.o\n LIB_OBJS += list-objects-filter-options.o\n+LIB_OBJS += list-objects-filter.o\n+LIB_OBJS += list-objects.o\n LIB_OBJS += ll-merge.o\n LIB_OBJS += lockfile.o\n LIB_OBJS += log-tree.o\n@@ -914,31 +914,31 @@ LIB_OBJS += mailinfo.o\n LIB_OBJS += mailmap.o\n LIB_OBJS += match-trees.o\n LIB_OBJS += mem-pool.o\n-LIB_OBJS += merge.o\n LIB_OBJS += merge-blobs.o\n LIB_OBJS += merge-recursive.o\n+LIB_OBJS += merge.o\n LIB_OBJS += mergesort.o\n LIB_OBJS += midx.o\n LIB_OBJS += name-hash.o\n LIB_OBJS += negotiator/default.o\n LIB_OBJS += negotiator/skipping.o\n-LIB_OBJS += notes.o\n LIB_OBJS += notes-cache.o\n LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\n+LIB_OBJS += notes.o\n LIB_OBJS += object.o\n LIB_OBJS += oidmap.o\n LIB_OBJS += oidset.o\n-LIB_OBJS += packfile.o\n-LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-bitmap-write.o\n+LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-objects.o\n LIB_OBJS += pack-revindex.o\n LIB_OBJS += pack-write.o\n+LIB_OBJS += packfile.o\n LIB_OBJS += pager.o\n-LIB_OBJS += parse-options.o\n LIB_OBJS += parse-options-cb.o\n+LIB_OBJS += parse-options.o\n LIB_OBJS += patch-delta.o\n LIB_OBJS += patch-ids.o\n LIB_OBJS += path.o\n@@ -955,8 +955,9 @@ LIB_OBJS += quote.o\n LIB_OBJS += range-diff.o\n LIB_OBJS += reachable.o\n LIB_OBJS += read-cache.o\n-LIB_OBJS += rebase.o\n LIB_OBJS += rebase-interactive.o\n+LIB_OBJS += rebase.o\n+LIB_OBJS += ref-filter.o\n LIB_OBJS += reflog-walk.o\n LIB_OBJS += refs.o\n LIB_OBJS += refs/files-backend.o\n@@ -964,7 +965,6 @@ LIB_OBJS += refs/iterator.o\n LIB_OBJS += refs/packed-backend.o\n LIB_OBJS += refs/ref-cache.o\n LIB_OBJS += refspec.o\n-LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace-object.o\n LIB_OBJS += repo-settings.o\n@@ -979,8 +979,8 @@ LIB_OBJS += serve.o\n LIB_OBJS += server-info.o\n LIB_OBJS += setup.o\n LIB_OBJS += sha1-array.o\n-LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1-file.o\n+LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1-name.o\n LIB_OBJS += shallow.o\n LIB_OBJS += sideband.o\n@@ -990,9 +990,9 @@ LIB_OBJS += stable-qsort.o\n LIB_OBJS += strbuf.o\n LIB_OBJS += streaming.o\n LIB_OBJS += string-list.o\n-LIB_OBJS += submodule.o\n-LIB_OBJS += submodule-config.o\n LIB_OBJS += sub-process.o\n+LIB_OBJS += submodule-config.o\n+LIB_OBJS += submodule.o\n LIB_OBJS += symlinks.o\n LIB_OBJS += tag.o\n LIB_OBJS += tempfile.o\n@@ -1011,11 +1011,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o\n LIB_OBJS += trace2/tr2_tgt_perf.o\n LIB_OBJS += trace2/tr2_tls.o\n LIB_OBJS += trailer.o\n-LIB_OBJS += transport.o\n LIB_OBJS += transport-helper.o\n+LIB_OBJS += transport.o\n LIB_OBJS += tree-diff.o\n-LIB_OBJS += tree.o\n LIB_OBJS += tree-walk.o\n+LIB_OBJS += tree.o\n LIB_OBJS += unpack-trees.o\n LIB_OBJS += upload-pack.o\n LIB_OBJS += url.o\n@@ -1055,9 +1055,9 @@ BUILTIN_OBJS += builtin/checkout.o\n BUILTIN_OBJS += builtin/clean.o\n BUILTIN_OBJS += builtin/clone.o\n BUILTIN_OBJS += builtin/column.o\n+BUILTIN_OBJS += builtin/commit-graph.o\n BUILTIN_OBJS += builtin/commit-tree.o\n BUILTIN_OBJS += builtin/commit.o\n-BUILTIN_OBJS += builtin/commit-graph.o\n BUILTIN_OBJS += builtin/config.o\n BUILTIN_OBJS += builtin/count-objects.o\n BUILTIN_OBJS += builtin/credential.o\n@@ -1088,13 +1088,13 @@ BUILTIN_OBJS += builtin/ls-remote.o\n BUILTIN_OBJS += builtin/ls-tree.o\n BUILTIN_OBJS += builtin/mailinfo.o\n BUILTIN_OBJS += builtin/mailsplit.o\n-BUILTIN_OBJS += builtin/merge.o\n BUILTIN_OBJS += builtin/merge-base.o\n BUILTIN_OBJS += builtin/merge-file.o\n BUILTIN_OBJS += builtin/merge-index.o\n BUILTIN_OBJS += builtin/merge-ours.o\n BUILTIN_OBJS += builtin/merge-recursive.o\n BUILTIN_OBJS += builtin/merge-tree.o\n+BUILTIN_OBJS += builtin/merge.o\n BUILTIN_OBJS += builtin/mktag.o\n BUILTIN_OBJS += builtin/mktree.o\n BUILTIN_OBJS += builtin/multi-pack-index.o\n@@ -1114,9 +1114,9 @@ BUILTIN_OBJS += builtin/read-tree.o\n BUILTIN_OBJS += builtin/rebase.o\n BUILTIN_OBJS += builtin/receive-pack.o\n BUILTIN_OBJS += builtin/reflog.o\n-BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/remote-ext.o\n BUILTIN_OBJS += builtin/remote-fd.o\n+BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/rerere.o\n@@ -2335,16 +2335,16 @@ reconfigure config.mak.autogen: config.status\n endif\n \n XDIFF_OBJS += xdiff/xdiffi.o\n-XDIFF_OBJS += xdiff/xprepare.o\n-XDIFF_OBJS += xdiff/xutils.o\n XDIFF_OBJS += xdiff/xemit.o\n+XDIFF_OBJS += xdiff/xhistogram.o\n XDIFF_OBJS += xdiff/xmerge.o\n XDIFF_OBJS += xdiff/xpatience.o\n-XDIFF_OBJS += xdiff/xhistogram.o\n+XDIFF_OBJS += xdiff/xprepare.o\n+XDIFF_OBJS += xdiff/xutils.o\n \n+VCSSVN_OBJS += vcs-svn/fast_export.o\n VCSSVN_OBJS += vcs-svn/line_buffer.o\n VCSSVN_OBJS += vcs-svn/sliding_window.o\n-VCSSVN_OBJS += vcs-svn/fast_export.o\n VCSSVN_OBJS += vcs-svn/svndiff.o\n VCSSVN_OBJS += vcs-svn/svndump.o\n \n@@ -3152,9 +3152,10 @@ endif\n #\n ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS)\n ALL_COMMANDS += git\n+ALL_COMMANDS += git-citool\n+ALL_COMMANDS += git-gui\n ALL_COMMANDS += gitk\n ALL_COMMANDS += gitweb\n-ALL_COMMANDS += git-gui git-citool\n \n .PHONY: check-docs\n check-docs::\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393590","messageId":"7c37777f07191c8ac1d26300f9465b90758550b2.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 03/19] sequencer: use file strbuf for read_oneliner()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:22Z","receivedAt":"2020-03-21T09:22:23Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the original read_oneliner logic, we duplicated the logic for\nstrbuf_trim_trailing_newline() with one exception: instead of preventing\nbuffer accesses below index 0, it would prevent buffer accesses below\nindex `orig_len`. Although this is correct, it isn't worth having the\nduplicated logic around.\n\nAdd a second strbuf to which files are read and run\nstrbuf_trim_trailing_newline() directly on this strbuf then concatenate\nthis strbuf with the argument strbuf at the end of the function. The\nfunction's external behaviour is unchanged.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 24 +++++++++++++-----------\n 1 file changed, 13 insertions(+), 11 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex e528225e78..c49fe76fe6 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -431,26 +431,28 @@ static int write_message(const void *buf, size_t len, const char *filename,\n static int read_oneliner(struct strbuf *buf,\n \tconst char *path, int skip_if_empty)\n {\n-\tint orig_len = buf->len;\n+\tint ret = 0;\n+\tstruct strbuf file_buf = STRBUF_INIT;\n \n \tif (!file_exists(path))\n \t\treturn 0;\n \n-\tif (strbuf_read_file(buf, path, 0) < 0) {\n+\tif (strbuf_read_file(&file_buf, path, 0) < 0) {\n \t\twarning_errno(_(\"could not read '%s'\"), path);\n-\t\treturn 0;\n+\t\tgoto done;\n \t}\n \n-\tif (buf->len > orig_len && buf->buf[buf->len - 1] == '\\n') {\n-\t\tif (--buf->len > orig_len && buf->buf[buf->len - 1] == '\\r')\n-\t\t\t--buf->len;\n-\t\tbuf->buf[buf->len] = '\\0';\n-\t}\n+\tstrbuf_trim_trailing_newline(&file_buf);\n \n-\tif (skip_if_empty && buf->len == orig_len)\n-\t\treturn 0;\n+\tif (skip_if_empty && !file_buf.len)\n+\t\tgoto done;\n \n-\treturn 1;\n+\tstrbuf_addbuf(buf, &file_buf);\n+\tret = 1;\n+\n+done:\n+\tstrbuf_release(&file_buf);\n+\treturn ret;\n }\n \n static struct tree *empty_tree(struct repository *r)\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393591","messageId":"4f9708682877f4d3689781009dc2695b913eff29.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 04/19] sequencer: make read_oneliner() accept flags","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:23Z","receivedAt":"2020-03-21T09:22:24Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future commit, we will need read_oneliner() to accept flags other\nthan just `skip_if_empty`. Instead of having an argument for each flag,\nteach read_oneliner() to accept the bitfield `flags` instead. For now,\nonly recognize the `READ_ONELINER_SKIP_IF_EMPTY` flag. More flags will\nbe added in a future commit.\n\nThe result of this is that parallel topics which introduce invocations\nof read_oneliner() will still be compatible with this new function\nsignature since, instead of passing 1 or 0 for `skip_if_empty`, they'll\nbe passing 1 or 0 to `flags`, which gives equivalent behavior.\n\nMechanically fix up invocations of read_oneliner() with the following\nspatch\n\n\t@@\n\texpression a, b;\n\t@@\n\t  read_oneliner(a, b,\n\t- 1\n\t+ READ_ONELINER_SKIP_IF_EMPTY\n\t  )\n\nand manually break up long lines in the result.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 21 ++++++++++++++-------\n 1 file changed, 14 insertions(+), 7 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex c49fe76fe6..abb2a21e9d 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -419,6 +419,8 @@ static int write_message(const void *buf, size_t len, const char *filename,\n \treturn 0;\n }\n \n+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n+\n /*\n  * Reads a file that was presumably written by a shell script, i.e. with an\n  * end-of-line marker that needs to be stripped.\n@@ -429,7 +431,7 @@ static int write_message(const void *buf, size_t len, const char *filename,\n  * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n  */\n static int read_oneliner(struct strbuf *buf,\n-\tconst char *path, int skip_if_empty)\n+\tconst char *path, unsigned flags)\n {\n \tint ret = 0;\n \tstruct strbuf file_buf = STRBUF_INIT;\n@@ -444,7 +446,7 @@ static int read_oneliner(struct strbuf *buf,\n \n \tstrbuf_trim_trailing_newline(&file_buf);\n \n-\tif (skip_if_empty && !file_buf.len)\n+\tif ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !file_buf.len)\n \t\tgoto done;\n \n \tstrbuf_addbuf(buf, &file_buf);\n@@ -2488,7 +2490,8 @@ static int read_populate_opts(struct replay_opts *opts)\n \tif (is_rebase_i(opts)) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {\n+\t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(),\n+\t\t\t\t  READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\t\tif (!starts_with(buf.buf, \"-S\"))\n \t\t\t\tstrbuf_reset(&buf);\n \t\t\telse {\n@@ -2498,7 +2501,8 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t\tstrbuf_reset(&buf);\n \t\t}\n \n-\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {\n+\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(),\n+\t\t\t\t  READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n \t\t\t\topts->allow_rerere_auto = RERERE_AUTOUPDATE;\n \t\t\telse if (!strcmp(buf.buf, \"--no-rerere-autoupdate\"))\n@@ -2530,7 +2534,8 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\tstrbuf_release(&buf);\n \n \t\tif (read_oneliner(&opts->current_fixups,\n-\t\t\t\t  rebase_path_current_fixups(), 1)) {\n+\t\t\t\t  rebase_path_current_fixups(),\n+\t\t\t\t  READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\t\tconst char *p = opts->current_fixups.buf;\n \t\t\topts->current_fixup_count = 1;\n \t\t\twhile ((p = strchr(p, '\\n'))) {\n@@ -3667,7 +3672,8 @@ static int apply_autostash(struct replay_opts *opts)\n \tstruct child_process child = CHILD_PROCESS_INIT;\n \tint ret = 0;\n \n-\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {\n+\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(),\n+\t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\tstrbuf_release(&stash_sha1);\n \t\treturn 0;\n \t}\n@@ -4291,7 +4297,8 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tstruct object_id oid;\n \n-\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&\n+\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(),\n+\t\t\t\t  READ_ONELINER_SKIP_IF_EMPTY) &&\n \t\t    !get_oid_committish(buf.buf, &oid))\n \t\t\trecord_in_rewritten(&oid, peek_command(&todo_list, 0));\n \t\tstrbuf_release(&buf);\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393592","messageId":"e42f4bdd4d763be4062f12fa6f1481849e87a0ad.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 05/19] sequencer: configurably warn on non-existent files","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:24Z","receivedAt":"2020-03-21T09:22:24Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we plan on externing read_oneliner(). Future users of\nread_oneliner() will want the ability to output warnings in the event\nthat the `path` doesn't exist. Introduce the\n`READ_ONELINER_WARN_NON_EXISTENCE` flag which, if active, would issue a\nwarning when a file doesn't exist by skipping the `!file_exists()` check\nand letting `strbuf_read_file()` handle that case.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex abb2a21e9d..92e8d38290 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename,\n }\n \n #define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n+#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)\n \n /*\n  * Reads a file that was presumably written by a shell script, i.e. with an\n@@ -436,7 +437,7 @@ static int read_oneliner(struct strbuf *buf,\n \tint ret = 0;\n \tstruct strbuf file_buf = STRBUF_INIT;\n \n-\tif (!file_exists(path))\n+\tif (!(flags & READ_ONELINER_WARN_NON_EXISTENCE) && !file_exists(path))\n \t\treturn 0;\n \n \tif (strbuf_read_file(&file_buf, path, 0) < 0) {\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393593","messageId":"d07a50c5b22128263aaa8eef94546fe8bc8a9ec8.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 07/19] rebase: use read_oneliner()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:26Z","receivedAt":"2020-03-21T09:22:24Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Since in sequencer.c, read_one() basically duplicates the functionality\nof read_oneliner(), reduce code duplication by replacing read_one() with\nread_oneliner().\n\nThis was done with the following Coccinelle script\n\n\t@@\n\texpression a, b;\n\t@@\n\t- read_one(a, b)\n\t+ !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)\n\nand long lines were manually broken up.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 37 +++++++++++++++++--------------------\n 1 file changed, 17 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex bff53d5d16..1146463099 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -586,15 +586,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o\n \treturn path.buf;\n }\n \n-/* Read one file, then strip line endings */\n-static int read_one(const char *path, struct strbuf *buf)\n-{\n-\tif (strbuf_read_file(buf, path, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'\"), path);\n-\tstrbuf_trim_trailing_newline(buf);\n-\treturn 0;\n-}\n-\n /* Initialize the rebase options from the state directory. */\n static int read_basic_state(struct rebase_options *opts)\n {\n@@ -602,8 +593,10 @@ static int read_basic_state(struct rebase_options *opts)\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct object_id oid;\n \n-\tif (read_one(state_dir_path(\"head-name\", opts), &head_name) ||\n-\t    read_one(state_dir_path(\"onto\", opts), &buf))\n+\tif (!read_oneliner(&head_name, state_dir_path(\"head-name\", opts),\n+\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE) ||\n+\t    !read_oneliner(&buf, state_dir_path(\"onto\", opts),\n+\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n \t\treturn -1;\n \topts->head_name = starts_with(head_name.buf, \"refs/\") ?\n \t\txstrdup(head_name.buf) : NULL;\n@@ -619,9 +612,11 @@ static int read_basic_state(struct rebase_options *opts)\n \t */\n \tstrbuf_reset(&buf);\n \tif (file_exists(state_dir_path(\"orig-head\", opts))) {\n-\t\tif (read_one(state_dir_path(\"orig-head\", opts), &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"orig-head\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n \t\t\treturn -1;\n-\t} else if (read_one(state_dir_path(\"head\", opts), &buf))\n+\t} else if (!read_oneliner(&buf, state_dir_path(\"head\", opts),\n+\t\t\t\t  READ_ONELINER_WARN_NON_EXISTENCE))\n \t\treturn -1;\n \tif (get_oid(buf.buf, &opts->orig_head))\n \t\treturn error(_(\"invalid orig-head: '%s'\"), buf.buf);\n@@ -641,8 +636,8 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tif (file_exists(state_dir_path(\"allow_rerere_autoupdate\", opts))) {\n \t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"allow_rerere_autoupdate\", opts),\n-\t\t\t    &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"allow_rerere_autoupdate\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n \t\t\treturn -1;\n \t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n \t\t\topts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;\n@@ -655,8 +650,8 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tif (file_exists(state_dir_path(\"gpg_sign_opt\", opts))) {\n \t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"gpg_sign_opt\", opts),\n-\t\t\t    &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"gpg_sign_opt\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n \t\t\treturn -1;\n \t\tfree(opts->gpg_sign_opt);\n \t\topts->gpg_sign_opt = xstrdup(buf.buf);\n@@ -664,7 +659,8 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tif (file_exists(state_dir_path(\"strategy\", opts))) {\n \t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"strategy\", opts), &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n \t\t\treturn -1;\n \t\tfree(opts->strategy);\n \t\topts->strategy = xstrdup(buf.buf);\n@@ -672,7 +668,8 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tif (file_exists(state_dir_path(\"strategy_opts\", opts))) {\n \t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"strategy_opts\", opts), &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy_opts\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n \t\t\treturn -1;\n \t\tfree(opts->strategy_opts);\n \t\topts->strategy_opts = xstrdup(buf.buf);\n@@ -724,7 +721,7 @@ static int apply_autostash(struct rebase_options *opts)\n \tif (!file_exists(path))\n \t\treturn 0;\n \n-\tif (read_one(path, &autostash))\n+\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))\n \t\treturn error(_(\"Could not read '%s'\"), path);\n \t/* Ensure that the hash is not mistaken for a number */\n \tstrbuf_addstr(&autostash, \"^0\");\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393594","messageId":"e4d0c2d9020e1f76928d590c2692502e87babcfa.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 06/19] sequencer: make read_oneliner() extern","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:25Z","receivedAt":"2020-03-21T09:22:24Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"The function read_oneliner() is a generally useful util function.\nInstead of hiding it as a static function within sequencer.c, extern it\nso that it can be reused by others.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 14 +-------------\n sequencer.h | 14 ++++++++++++++\n 2 files changed, 15 insertions(+), 13 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 92e8d38290..c2102cc2eb 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -419,19 +419,7 @@ static int write_message(const void *buf, size_t len, const char *filename,\n \treturn 0;\n }\n \n-#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n-#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)\n-\n-/*\n- * Reads a file that was presumably written by a shell script, i.e. with an\n- * end-of-line marker that needs to be stripped.\n- *\n- * Note that only the last end-of-line marker is stripped, consistent with the\n- * behavior of \"$(cat path)\" in a shell script.\n- *\n- * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n- */\n-static int read_oneliner(struct strbuf *buf,\n+int read_oneliner(struct strbuf *buf,\n \tconst char *path, unsigned flags)\n {\n \tint ret = 0;\ndiff --git a/sequencer.h b/sequencer.h\nindex 718a07426d..5c9662a60a 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -198,6 +198,20 @@ void print_commit_summary(struct repository *repo,\n \t\t\t  const struct object_id *oid,\n \t\t\t  unsigned int flags);\n \n+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n+#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)\n+\n+/*\n+ * Reads a file that was presumably written by a shell script, i.e. with an\n+ * end-of-line marker that needs to be stripped.\n+ *\n+ * Note that only the last end-of-line marker is stripped, consistent with the\n+ * behavior of \"$(cat path)\" in a shell script.\n+ *\n+ * Returns 1 if the file was read, 0 if it could not be read.\n+ */\n+int read_oneliner(struct strbuf *buf,\n+\tconst char *path, unsigned flags);\n int read_author_script(const char *path, char **name, char **email, char **date,\n \t\t       int allow_missing);\n void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393595","messageId":"5bce4aeb96f21d39677a735e6fbdd42f7405d28a.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 08/19] sequencer: make apply_rebase() accept a path","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:27Z","receivedAt":"2020-03-21T09:22:28Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In order to make apply_rebase() more generic for future extraction, make\nit accept a `path` argument so that the location from where to read the\nreference to the autostash commit can be customized. Remove the `opts`\nargument since it was unused before anyway.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 14 +++++++-------\n 1 file changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex c2102cc2eb..ad40a8b3fc 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3655,13 +3655,13 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n-static int apply_autostash(struct replay_opts *opts)\n+static int apply_autostash(const char *path)\n {\n \tstruct strbuf stash_sha1 = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\n \tint ret = 0;\n \n-\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(),\n+\tif (!read_oneliner(&stash_sha1, path,\n \t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\tstrbuf_release(&stash_sha1);\n \t\treturn 0;\n@@ -3754,7 +3754,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,\n \t\treturn error(_(\"%s: not a valid OID\"), orig_head);\n \n \tif (run_git_checkout(r, opts, oid_to_hex(onto), action)) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \t\treturn error(_(\"could not detach HEAD\"));\n \t}\n@@ -4068,7 +4068,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\trun_command(&hook);\n \t\t\t}\n \t\t}\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \n \t\tif (!opts->quiet) {\n \t\t\tif (!opts->verbose)\n@@ -5077,7 +5077,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\ttodo_list_add_exec_commands(todo_list, commands);\n \n \tif (count_commands(todo_list) == 0) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \n \t\treturn error(_(\"nothing to do\"));\n@@ -5088,12 +5088,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (res == -1)\n \t\treturn -1;\n \telse if (res == -2) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \n \t\treturn -1;\n \t} else if (res == -3) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \t\ttodo_list_release(&new_todo);\n \n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393596","messageId":"03bdaeebc993058bf14a0d196f5a1a68800e538e.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 09/19] rebase: use apply_autostash() from sequencer.c","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:28Z","receivedAt":"2020-03-21T09:22:29Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"The apply_autostash() function in builtin/rebase.c is similar enough to\nthe apply_autostash() function in sequencer.c that they are almost\ninterchangeable. Make the sequencer.c version extern and use it in\nrebase.\n\nThe rebase version was introduced in 6defce2b02 (builtin rebase: support\n`--autostash` option, 2018-09-04) as part of the shell to C conversion.\nIt opted to duplicate the function because, at the time, there was\nanother in-progress project converting interactive rebase from shell to\nC as well and they did not want to clash with them by refactoring\nsequencer.c version of apply_autostash(). Since both efforts are long\ndone, we can freely combine them together now.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 49 ++----------------------------------------------\n sequencer.c      |  2 +-\n sequencer.h      |  2 ++\n 3 files changed, 5 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 1146463099..ceb115247a 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -712,51 +712,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)\n \treturn 0;\n }\n \n-static int apply_autostash(struct rebase_options *opts)\n-{\n-\tconst char *path = state_dir_path(\"autostash\", opts);\n-\tstruct strbuf autostash = STRBUF_INIT;\n-\tstruct child_process stash_apply = CHILD_PROCESS_INIT;\n-\n-\tif (!file_exists(path))\n-\t\treturn 0;\n-\n-\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))\n-\t\treturn error(_(\"Could not read '%s'\"), path);\n-\t/* Ensure that the hash is not mistaken for a number */\n-\tstrbuf_addstr(&autostash, \"^0\");\n-\targv_array_pushl(&stash_apply.args,\n-\t\t\t \"stash\", \"apply\", autostash.buf, NULL);\n-\tstash_apply.git_cmd = 1;\n-\tstash_apply.no_stderr = stash_apply.no_stdout =\n-\t\tstash_apply.no_stdin = 1;\n-\tif (!run_command(&stash_apply))\n-\t\tprintf(_(\"Applied autostash.\\n\"));\n-\telse {\n-\t\tstruct argv_array args = ARGV_ARRAY_INIT;\n-\t\tint res = 0;\n-\n-\t\targv_array_pushl(&args,\n-\t\t\t\t \"stash\", \"store\", \"-m\", \"autostash\", \"-q\",\n-\t\t\t\t autostash.buf, NULL);\n-\t\tif (run_command_v_opt(args.argv, RUN_GIT_CMD))\n-\t\t\tres = error(_(\"Cannot store %s\"), autostash.buf);\n-\t\targv_array_clear(&args);\n-\t\tstrbuf_release(&autostash);\n-\t\tif (res)\n-\t\t\treturn res;\n-\n-\t\tfprintf(stderr,\n-\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n-\t\t\t  \"Your changes are safe in the stash.\\n\"\n-\t\t\t  \"You can run \\\"git stash pop\\\" or \\\"git stash drop\\\" \"\n-\t\t\t  \"at any time.\\n\"));\n-\t}\n-\n-\tstrbuf_release(&autostash);\n-\treturn 0;\n-}\n-\n static int finish_rebase(struct rebase_options *opts)\n {\n \tstruct strbuf dir = STRBUF_INIT;\n@@ -764,7 +719,7 @@ static int finish_rebase(struct rebase_options *opts)\n \tint ret = 0;\n \n \tdelete_ref(NULL, \"REBASE_HEAD\", NULL, REF_NO_DEREF);\n-\tapply_autostash(opts);\n+\tapply_autostash(state_dir_path(\"autostash\", opts));\n \tclose_object_store(the_repository->objects);\n \t/*\n \t * We ignore errors in 'gc --auto', since the\n@@ -1209,7 +1164,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)\n \t} else if (status == 2) {\n \t\tstruct strbuf dir = STRBUF_INIT;\n \n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(state_dir_path(\"autostash\", opts));\n \t\tstrbuf_addstr(&dir, opts->state_dir);\n \t\tremove_dir_recursively(&dir, 0);\n \t\tstrbuf_release(&dir);\ndiff --git a/sequencer.c b/sequencer.c\nindex ad40a8b3fc..b52668f8de 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3655,7 +3655,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n-static int apply_autostash(const char *path)\n+int apply_autostash(const char *path)\n {\n \tstruct strbuf stash_sha1 = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\ndiff --git a/sequencer.h b/sequencer.h\nindex 5c9662a60a..bab910f012 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,\n \t\t\t const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n \n+int apply_autostash(const char *path);\n+\n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n void print_commit_summary(struct repository *repo,\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393597","messageId":"c35c5bdb8c52033ab2c7c369cf5db27880d6a56d.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 10/19] rebase: generify reset_head()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:29Z","receivedAt":"2020-03-21T09:22:30Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we plan on lib-ifying reset_head() so we need it to\nbe more generic. Make it more generic by making it accept a\n`struct repository` argument instead of implicitly using the non-repo\nfunctions. Also, make it accept a `const char *default_reflog_action`\nargument so that the default action of \"rebase\" isn't hardcoded in.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 64 ++++++++++++++++++++++++++++--------------------\n 1 file changed, 37 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex ceb115247a..52eb45b6c4 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -28,6 +28,8 @@\n #include \"sequencer.h\"\n #include \"rebase-interactive.h\"\n \n+#define DEFAULT_REFLOG_ACTION \"rebase\"\n+\n static char const * const builtin_rebase_usage[] = {\n \tN_(\"git rebase [-i] [options] [--exec <cmd>] \"\n \t\t\"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]\"),\n@@ -772,9 +774,10 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)\n #define RESET_HEAD_REFS_ONLY (1<<3)\n #define RESET_ORIG_HEAD (1<<4)\n \n-static int reset_head(struct object_id *oid, const char *action,\n+static int reset_head(struct repository *r, struct object_id *oid, const char *action,\n \t\t      const char *switch_to_branch, unsigned flags,\n-\t\t      const char *reflog_orig_head, const char *reflog_head)\n+\t\t      const char *reflog_orig_head, const char *reflog_head,\n+\t\t      const char *default_reflog_action)\n {\n \tunsigned detach_head = flags & RESET_HEAD_DETACH;\n \tunsigned reset_hard = flags & RESET_HEAD_HARD;\n@@ -796,7 +799,7 @@ static int reset_head(struct object_id *oid, const char *action,\n \tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n \t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n \n-\tif (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {\n+\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n \t\tret = -1;\n \t\tgoto leave_reset_head;\n \t}\n@@ -815,26 +818,26 @@ static int reset_head(struct object_id *oid, const char *action,\n \tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n \tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n \tunpack_tree_opts.head_idx = 1;\n-\tunpack_tree_opts.src_index = the_repository->index;\n-\tunpack_tree_opts.dst_index = the_repository->index;\n+\tunpack_tree_opts.src_index = r->index;\n+\tunpack_tree_opts.dst_index = r->index;\n \tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n \tunpack_tree_opts.update = 1;\n \tunpack_tree_opts.merge = 1;\n \tif (!detach_head)\n \t\tunpack_tree_opts.reset = 1;\n \n-\tif (repo_read_index_unmerged(the_repository) < 0) {\n+\tif (repo_read_index_unmerged(r) < 0) {\n \t\tret = error(_(\"could not read index\"));\n \t\tgoto leave_reset_head;\n \t}\n \n-\tif (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {\n+\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n \t\tret = error(_(\"failed to find tree of %s\"),\n \t\t\t    oid_to_hex(&head_oid));\n \t\tgoto leave_reset_head;\n \t}\n \n-\tif (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {\n+\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n \t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n \t\tgoto leave_reset_head;\n \t}\n@@ -845,16 +848,16 @@ static int reset_head(struct object_id *oid, const char *action,\n \t}\n \n \ttree = parse_tree_indirect(oid);\n-\tprime_cache_tree(the_repository, the_repository->index, tree);\n+\tprime_cache_tree(r, r->index, tree);\n \n-\tif (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {\n+\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n \t\tret = error(_(\"could not write index\"));\n \t\tgoto leave_reset_head;\n \t}\n \n reset_head_refs:\n \treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n-\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : \"rebase\");\n+\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n \tprefix_len = msg.len;\n \n \tif (update_orig_head) {\n@@ -916,8 +919,10 @@ static int move_to_original_branch(struct rebase_options *opts)\n \t\t    opts->head_name, oid_to_hex(&opts->onto->object.oid));\n \tstrbuf_addf(&head_reflog, \"rebase finished: returning to %s\",\n \t\t    opts->head_name);\n-\tret = reset_head(NULL, \"\", opts->head_name, RESET_HEAD_REFS_ONLY,\n-\t\t\t orig_head_reflog.buf, head_reflog.buf);\n+\tret = reset_head(the_repository, NULL, \"\", opts->head_name,\n+\t\t\t RESET_HEAD_REFS_ONLY,\n+\t\t\t orig_head_reflog.buf, head_reflog.buf,\n+\t\t\t DEFAULT_REFLOG_ACTION);\n \n \tstrbuf_release(&orig_head_reflog);\n \tstrbuf_release(&head_reflog);\n@@ -1005,8 +1010,9 @@ static int run_am(struct rebase_options *opts)\n \t\tfree(rebased_patches);\n \t\targv_array_clear(&am.args);\n \n-\t\treset_head(&opts->orig_head, \"checkout\", opts->head_name, 0,\n-\t\t\t   \"HEAD\", NULL);\n+\t\treset_head(the_repository, &opts->orig_head, \"checkout\",\n+\t\t\t   opts->head_name, 0,\n+\t\t\t   \"HEAD\", NULL, DEFAULT_REFLOG_ACTION);\n \t\terror(_(\"\\ngit encountered an error while preparing the \"\n \t\t\t\"patches to replay\\n\"\n \t\t\t\"these revisions:\\n\"\n@@ -1661,8 +1667,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\trerere_clear(the_repository, &merge_rr);\n \t\tstring_list_clear(&merge_rr, 1);\n \n-\t\tif (reset_head(NULL, \"reset\", NULL, RESET_HEAD_HARD,\n-\t\t\t       NULL, NULL) < 0)\n+\t\tif (reset_head(the_repository, NULL, \"reset\", NULL, RESET_HEAD_HARD,\n+\t\t\t       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)\n \t\t\tdie(_(\"could not discard worktree changes\"));\n \t\tremove_branch_state(the_repository, 0);\n \t\tif (read_basic_state(&options))\n@@ -1679,9 +1685,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \t\tif (read_basic_state(&options))\n \t\t\texit(1);\n-\t\tif (reset_head(&options.orig_head, \"reset\",\n+\t\tif (reset_head(the_repository, &options.orig_head, \"reset\",\n \t\t\t       options.head_name, RESET_HEAD_HARD,\n-\t\t\t       NULL, NULL) < 0)\n+\t\t\t       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)\n \t\t\tdie(_(\"could not move back to %s\"),\n \t\t\t    oid_to_hex(&options.orig_head));\n \t\tremove_branch_state(the_repository, 0);\n@@ -2073,8 +2079,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\t    options.state_dir);\n \t\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n \t\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\t\tif (reset_head(NULL, \"reset --hard\",\n-\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)\n+\t\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n+\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n+\t\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n \t\t\t\tdie(_(\"could not reset --hard\"));\n \n \t\t\tif (discard_index(the_repository->index) < 0 ||\n@@ -2114,10 +2121,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\tstrbuf_addf(&buf, \"%s: checkout %s\",\n \t\t\t\t\t    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),\n \t\t\t\t\t    options.switch_to);\n-\t\t\t\tif (reset_head(&options.orig_head, \"checkout\",\n+\t\t\t\tif (reset_head(the_repository,\n+\t\t\t\t\t       &options.orig_head, \"checkout\",\n \t\t\t\t\t       options.head_name,\n \t\t\t\t\t       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,\n-\t\t\t\t\t       NULL, buf.buf) < 0) {\n+\t\t\t\t\t       NULL, buf.buf,\n+\t\t\t\t\t       DEFAULT_REFLOG_ACTION) < 0) {\n \t\t\t\t\tret = !!error(_(\"could not switch to \"\n \t\t\t\t\t\t\t\"%s\"),\n \t\t\t\t\t\t      options.switch_to);\n@@ -2189,10 +2198,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tstrbuf_addf(&msg, \"%s: checkout %s\",\n \t\t    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);\n-\tif (reset_head(&options.onto->object.oid, \"checkout\", NULL,\n+\tif (reset_head(the_repository, &options.onto->object.oid, \"checkout\", NULL,\n \t\t       RESET_HEAD_DETACH | RESET_ORIG_HEAD |\n \t\t       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,\n-\t\t       NULL, msg.buf))\n+\t\t       NULL, msg.buf, DEFAULT_REFLOG_ACTION))\n \t\tdie(_(\"Could not detach HEAD\"));\n \tstrbuf_release(&msg);\n \n@@ -2207,8 +2216,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addf(&msg, \"rebase finished: %s onto %s\",\n \t\t\toptions.head_name ? options.head_name : \"detached HEAD\",\n \t\t\toid_to_hex(&options.onto->object.oid));\n-\t\treset_head(NULL, \"Fast-forwarded\", options.head_name,\n-\t\t\t   RESET_HEAD_REFS_ONLY, \"HEAD\", msg.buf);\n+\t\treset_head(the_repository, NULL, \"Fast-forwarded\", options.head_name,\n+\t\t\t   RESET_HEAD_REFS_ONLY, \"HEAD\", msg.buf,\n+\t\t\t   DEFAULT_REFLOG_ACTION);\n \t\tstrbuf_release(&msg);\n \t\tret = !!finish_rebase(&options);\n \t\tgoto cleanup;\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393598","messageId":"a14a2e0849fea91c6c41cf71b8d049123bea0c6d.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 11/19] reset: extract reset_head() from rebase","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:30Z","receivedAt":"2020-03-21T09:22:31Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Continue the process of lib-ifying the autostash code. In a future\ncommit, this will be used to implement `--autostash` in other builtins.\n\nThis patch is best viewed with `--color-moved`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Makefile         |   1 +\n builtin/rebase.c | 139 +---------------------------------------------\n reset.c          | 140 +++++++++++++++++++++++++++++++++++++++++++++++\n reset.h          |  20 +++++++\n 4 files changed, 162 insertions(+), 138 deletions(-)\n create mode 100644 reset.c\n create mode 100644 reset.h\n\ndiff --git a/Makefile b/Makefile\nindex f709fc3f77..338fb55b73 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -970,6 +970,7 @@ LIB_OBJS += replace-object.o\n LIB_OBJS += repo-settings.o\n LIB_OBJS += repository.o\n LIB_OBJS += rerere.o\n+LIB_OBJS += reset.o\n LIB_OBJS += resolve-undo.o\n LIB_OBJS += revision.o\n LIB_OBJS += run-command.o\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 52eb45b6c4..caff67b00d 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -27,6 +27,7 @@\n #include \"branch.h\"\n #include \"sequencer.h\"\n #include \"rebase-interactive.h\"\n+#include \"reset.h\"\n \n #define DEFAULT_REFLOG_ACTION \"rebase\"\n \n@@ -766,144 +767,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)\n \t}\n }\n \n-#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n-\n-#define RESET_HEAD_DETACH (1<<0)\n-#define RESET_HEAD_HARD (1<<1)\n-#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n-#define RESET_HEAD_REFS_ONLY (1<<3)\n-#define RESET_ORIG_HEAD (1<<4)\n-\n-static int reset_head(struct repository *r, struct object_id *oid, const char *action,\n-\t\t      const char *switch_to_branch, unsigned flags,\n-\t\t      const char *reflog_orig_head, const char *reflog_head,\n-\t\t      const char *default_reflog_action)\n-{\n-\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n-\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n-\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n-\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n-\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n-\tstruct object_id head_oid;\n-\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n-\tstruct lock_file lock = LOCK_INIT;\n-\tstruct unpack_trees_options unpack_tree_opts;\n-\tstruct tree *tree;\n-\tconst char *reflog_action;\n-\tstruct strbuf msg = STRBUF_INIT;\n-\tsize_t prefix_len;\n-\tstruct object_id *orig = NULL, oid_orig,\n-\t\t*old_orig = NULL, oid_old_orig;\n-\tint ret = 0, nr = 0;\n-\n-\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n-\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n-\n-\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n-\t\tret = -1;\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n-\t\tret = error(_(\"could not determine HEAD revision\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!oid)\n-\t\toid = &head_oid;\n-\n-\tif (refs_only)\n-\t\tgoto reset_head_refs;\n-\n-\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n-\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n-\tunpack_tree_opts.head_idx = 1;\n-\tunpack_tree_opts.src_index = r->index;\n-\tunpack_tree_opts.dst_index = r->index;\n-\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n-\tunpack_tree_opts.update = 1;\n-\tunpack_tree_opts.merge = 1;\n-\tif (!detach_head)\n-\t\tunpack_tree_opts.reset = 1;\n-\n-\tif (repo_read_index_unmerged(r) < 0) {\n-\t\tret = error(_(\"could not read index\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n-\t\tret = error(_(\"failed to find tree of %s\"),\n-\t\t\t    oid_to_hex(&head_oid));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n-\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n-\t\tret = -1;\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\ttree = parse_tree_indirect(oid);\n-\tprime_cache_tree(r, r->index, tree);\n-\n-\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n-\t\tret = error(_(\"could not write index\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-reset_head_refs:\n-\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n-\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n-\tprefix_len = msg.len;\n-\n-\tif (update_orig_head) {\n-\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n-\t\t\told_orig = &oid_old_orig;\n-\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n-\t\t\torig = &oid_orig;\n-\t\t\tif (!reflog_orig_head) {\n-\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n-\t\t\t\treflog_orig_head = msg.buf;\n-\t\t\t}\n-\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n-\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n-\t\t} else if (old_orig)\n-\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n-\t}\n-\n-\tif (!reflog_head) {\n-\t\tstrbuf_setlen(&msg, prefix_len);\n-\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n-\t\treflog_head = msg.buf;\n-\t}\n-\tif (!switch_to_branch)\n-\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n-\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n-\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n-\telse {\n-\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n-\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n-\t\tif (!ret)\n-\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n-\t\t\t\t\t    reflog_head);\n-\t}\n-\tif (run_hook)\n-\t\trun_hook_le(NULL, \"post-checkout\",\n-\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n-\t\t\t    oid_to_hex(oid), \"1\", NULL);\n-\n-leave_reset_head:\n-\tstrbuf_release(&msg);\n-\trollback_lock_file(&lock);\n-\twhile (nr)\n-\t\tfree((void *)desc[--nr].buffer);\n-\treturn ret;\n-}\n-\n static int move_to_original_branch(struct rebase_options *opts)\n {\n \tstruct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;\ndiff --git a/reset.c b/reset.c\nnew file mode 100644\nindex 0000000000..79b683bffa\n--- /dev/null\n+++ b/reset.c\n@@ -0,0 +1,140 @@\n+#include \"git-compat-util.h\"\n+#include \"cache-tree.h\"\n+#include \"lockfile.h\"\n+#include \"refs.h\"\n+#include \"reset.h\"\n+#include \"run-command.h\"\n+#include \"tree-walk.h\"\n+#include \"tree.h\"\n+#include \"unpack-trees.h\"\n+\n+int reset_head(struct repository *r, struct object_id *oid, const char *action,\n+\t       const char *switch_to_branch, unsigned flags,\n+\t       const char *reflog_orig_head, const char *reflog_head,\n+\t       const char *default_reflog_action)\n+{\n+\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n+\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n+\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n+\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n+\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n+\tstruct object_id head_oid;\n+\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n+\tstruct lock_file lock = LOCK_INIT;\n+\tstruct unpack_trees_options unpack_tree_opts;\n+\tstruct tree *tree;\n+\tconst char *reflog_action;\n+\tstruct strbuf msg = STRBUF_INIT;\n+\tsize_t prefix_len;\n+\tstruct object_id *orig = NULL, oid_orig,\n+\t\t*old_orig = NULL, oid_old_orig;\n+\tint ret = 0, nr = 0;\n+\n+\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n+\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n+\n+\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n+\t\tret = -1;\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n+\t\tret = error(_(\"could not determine HEAD revision\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!oid)\n+\t\toid = &head_oid;\n+\n+\tif (refs_only)\n+\t\tgoto reset_head_refs;\n+\n+\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n+\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n+\tunpack_tree_opts.head_idx = 1;\n+\tunpack_tree_opts.src_index = r->index;\n+\tunpack_tree_opts.dst_index = r->index;\n+\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n+\tunpack_tree_opts.update = 1;\n+\tunpack_tree_opts.merge = 1;\n+\tif (!detach_head)\n+\t\tunpack_tree_opts.reset = 1;\n+\n+\tif (repo_read_index_unmerged(r) < 0) {\n+\t\tret = error(_(\"could not read index\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n+\t\tret = error(_(\"failed to find tree of %s\"),\n+\t\t\t    oid_to_hex(&head_oid));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n+\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n+\t\tret = -1;\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\ttree = parse_tree_indirect(oid);\n+\tprime_cache_tree(r, r->index, tree);\n+\n+\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n+\t\tret = error(_(\"could not write index\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+reset_head_refs:\n+\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n+\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n+\tprefix_len = msg.len;\n+\n+\tif (update_orig_head) {\n+\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n+\t\t\told_orig = &oid_old_orig;\n+\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n+\t\t\torig = &oid_orig;\n+\t\t\tif (!reflog_orig_head) {\n+\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n+\t\t\t\treflog_orig_head = msg.buf;\n+\t\t\t}\n+\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n+\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n+\t\t} else if (old_orig)\n+\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n+\t}\n+\n+\tif (!reflog_head) {\n+\t\tstrbuf_setlen(&msg, prefix_len);\n+\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n+\t\treflog_head = msg.buf;\n+\t}\n+\tif (!switch_to_branch)\n+\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n+\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n+\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n+\telse {\n+\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n+\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n+\t\tif (!ret)\n+\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n+\t\t\t\t\t    reflog_head);\n+\t}\n+\tif (run_hook)\n+\t\trun_hook_le(NULL, \"post-checkout\",\n+\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n+\t\t\t    oid_to_hex(oid), \"1\", NULL);\n+\n+leave_reset_head:\n+\tstrbuf_release(&msg);\n+\trollback_lock_file(&lock);\n+\twhile (nr)\n+\t\tfree((void *)desc[--nr].buffer);\n+\treturn ret;\n+\n+}\ndiff --git a/reset.h b/reset.h\nnew file mode 100644\nindex 0000000000..12f83c78e2\n--- /dev/null\n+++ b/reset.h\n@@ -0,0 +1,20 @@\n+#ifndef RESET_H\n+#define RESET_H\n+\n+#include \"hash.h\"\n+#include \"repository.h\"\n+\n+#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n+\n+#define RESET_HEAD_DETACH (1<<0)\n+#define RESET_HEAD_HARD (1<<1)\n+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n+#define RESET_HEAD_REFS_ONLY (1<<3)\n+#define RESET_ORIG_HEAD (1<<4)\n+\n+int reset_head(struct repository *r, struct object_id *oid, const char *action,\n+\t       const char *switch_to_branch, unsigned flags,\n+\t       const char *reflog_orig_head, const char *reflog_head,\n+\t       const char *default_reflog_action);\n+\n+#endif\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393599","messageId":"1dbae3dfbf0a9dbd0ef68d8788771b7dd04e7d0c.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 12/19] rebase: extract create_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:31Z","receivedAt":"2020-03-21T09:22:33Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future commit, we will lib-ify this code. In preparation for\nthis, extract the code into the create_autostash() function so that it\ncan be cleaned up before it is finally lib-ified.\n\nThis patch is best viewed with `--color-moved` and\n`--color-moved-ws=allow-indentation-change`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 94 +++++++++++++++++++++++++-----------------------\n 1 file changed, 50 insertions(+), 44 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex caff67b00d..037effc510 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1274,6 +1274,55 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n+static void create_autostash(struct rebase_options *options)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct lock_file lock_file = LOCK_INIT;\n+\tint fd;\n+\n+\tfd = hold_locked_index(&lock_file, 0);\n+\trefresh_cache(REFRESH_QUIET);\n+\tif (0 <= fd)\n+\t\trepo_update_index_if_able(the_repository, &lock_file);\n+\trollback_lock_file(&lock_file);\n+\n+\tif (has_unstaged_changes(the_repository, 1) ||\n+\t    has_uncommitted_changes(the_repository, 1)) {\n+\t\tconst char *autostash =\n+\t\t\tstate_dir_path(\"autostash\", options);\n+\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n+\t\tstruct object_id oid;\n+\n+\t\targv_array_pushl(&stash.args,\n+\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n+\t\tstash.git_cmd = 1;\n+\t\tstash.no_stdin = 1;\n+\t\tstrbuf_reset(&buf);\n+\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n+\t\t\tdie(_(\"Cannot autostash\"));\n+\t\tstrbuf_trim_trailing_newline(&buf);\n+\t\tif (get_oid(buf.buf, &oid))\n+\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n+\t\t\t    buf.buf);\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n+\n+\t\tif (safe_create_leading_directories_const(autostash))\n+\t\t\tdie(_(\"Could not create directory for '%s'\"),\n+\t\t\t    options->state_dir);\n+\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n+\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n+\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n+\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n+\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n+\t\t\tdie(_(\"could not reset --hard\"));\n+\n+\t\tif (discard_index(the_repository->index) < 0 ||\n+\t\t\trepo_read_index(the_repository) < 0)\n+\t\t\tdie(_(\"could not read index\"));\n+\t}\n+\tstrbuf_release(&buf);\n+}\n \n int cmd_rebase(int argc, const char **argv, const char *prefix)\n {\n@@ -1907,50 +1956,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read index\"));\n \n \tif (options.autostash) {\n-\t\tstruct lock_file lock_file = LOCK_INIT;\n-\t\tint fd;\n-\n-\t\tfd = hold_locked_index(&lock_file, 0);\n-\t\trefresh_cache(REFRESH_QUIET);\n-\t\tif (0 <= fd)\n-\t\t\trepo_update_index_if_able(the_repository, &lock_file);\n-\t\trollback_lock_file(&lock_file);\n-\n-\t\tif (has_unstaged_changes(the_repository, 1) ||\n-\t\t    has_uncommitted_changes(the_repository, 1)) {\n-\t\t\tconst char *autostash =\n-\t\t\t\tstate_dir_path(\"autostash\", &options);\n-\t\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n-\t\t\tstruct object_id oid;\n-\n-\t\t\targv_array_pushl(&stash.args,\n-\t\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n-\t\t\tstash.git_cmd = 1;\n-\t\t\tstash.no_stdin = 1;\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n-\t\t\t\tdie(_(\"Cannot autostash\"));\n-\t\t\tstrbuf_trim_trailing_newline(&buf);\n-\t\t\tif (get_oid(buf.buf, &oid))\n-\t\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n-\t\t\t\t    buf.buf);\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n-\n-\t\t\tif (safe_create_leading_directories_const(autostash))\n-\t\t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t\t    options.state_dir);\n-\t\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n-\t\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n-\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n-\t\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n-\t\t\t\tdie(_(\"could not reset --hard\"));\n-\n-\t\t\tif (discard_index(the_repository->index) < 0 ||\n-\t\t\t\trepo_read_index(the_repository) < 0)\n-\t\t\t\tdie(_(\"could not read index\"));\n-\t\t}\n+\t\tcreate_autostash(&options);\n \t}\n \n \tif (require_clean_work_tree(the_repository, \"rebase\",\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393600","messageId":"fffc038cd6d81f50a369904a8ecc2b1b6fab0e2b.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 13/19] rebase: generify create_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:32Z","receivedAt":"2020-03-21T09:22:36Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we plan on lib-ifying create_autostash() so we need it to\nbe more generic. Make it more generic by making it accept a\n`struct repository` argument instead of implicitly using the non-repo\nfunctions and `the_repository`. Also, make it accept a `path` argument\nso that we no longer rely have to rely on `struct rebase_options`.\nFinally, make it accept a `default_reflog_action` argument so we no\nlonger have to rely on `DEFAULT_REFLOG_ACTION`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 32 ++++++++++++++++----------------\n 1 file changed, 16 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 037effc510..3370c8f2c4 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1274,22 +1274,21 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n-static void create_autostash(struct rebase_options *options)\n+static void create_autostash(struct repository *r, const char *path,\n+\t\t\t     const char *default_reflog_action)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct lock_file lock_file = LOCK_INIT;\n \tint fd;\n \n-\tfd = hold_locked_index(&lock_file, 0);\n-\trefresh_cache(REFRESH_QUIET);\n+\tfd = repo_hold_locked_index(r, &lock_file, 0);\n+\trefresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);\n \tif (0 <= fd)\n-\t\trepo_update_index_if_able(the_repository, &lock_file);\n+\t\trepo_update_index_if_able(r, &lock_file);\n \trollback_lock_file(&lock_file);\n \n-\tif (has_unstaged_changes(the_repository, 1) ||\n-\t    has_uncommitted_changes(the_repository, 1)) {\n-\t\tconst char *autostash =\n-\t\t\tstate_dir_path(\"autostash\", options);\n+\tif (has_unstaged_changes(r, 1) ||\n+\t    has_uncommitted_changes(r, 1)) {\n \t\tstruct child_process stash = CHILD_PROCESS_INIT;\n \t\tstruct object_id oid;\n \n@@ -1307,18 +1306,18 @@ static void create_autostash(struct rebase_options *options)\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n \n-\t\tif (safe_create_leading_directories_const(autostash))\n+\t\tif (safe_create_leading_directories_const(path))\n \t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t    options->state_dir);\n-\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n+\t\t\t    path);\n+\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n \t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n+\t\tif (reset_head(r, NULL, \"reset --hard\",\n \t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n-\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n+\t\t\t       default_reflog_action) < 0)\n \t\t\tdie(_(\"could not reset --hard\"));\n \n-\t\tif (discard_index(the_repository->index) < 0 ||\n-\t\t\trepo_read_index(the_repository) < 0)\n+\t\tif (discard_index(r->index) < 0 ||\n+\t\t\trepo_read_index(r) < 0)\n \t\t\tdie(_(\"could not read index\"));\n \t}\n \tstrbuf_release(&buf);\n@@ -1956,7 +1955,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read index\"));\n \n \tif (options.autostash) {\n-\t\tcreate_autostash(&options);\n+\t\tcreate_autostash(the_repository, state_dir_path(\"autostash\", &options),\n+\t\t\t\t DEFAULT_REFLOG_ACTION);\n \t}\n \n \tif (require_clean_work_tree(the_repository, \"rebase\",\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393601","messageId":"bf55b86e2bd64d995ef559611558e885bf63bc38.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 14/19] sequencer: extract perform_autostash() from rebase","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:33Z","receivedAt":"2020-03-21T09:22:37Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Lib-ify the autostash code by extracting perform_autostash() from rebase\ninto sequencer. In a future commit, this will be used to implement\n`--autostash` in other builtins.\n\nThis patch is best viewed with `--color-moved`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 49 -----------------------------------------------\n sequencer.c      | 50 ++++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |  2 ++\n 3 files changed, 52 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 3370c8f2c4..b6821be62b 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1274,55 +1274,6 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n-static void create_autostash(struct repository *r, const char *path,\n-\t\t\t     const char *default_reflog_action)\n-{\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct lock_file lock_file = LOCK_INIT;\n-\tint fd;\n-\n-\tfd = repo_hold_locked_index(r, &lock_file, 0);\n-\trefresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);\n-\tif (0 <= fd)\n-\t\trepo_update_index_if_able(r, &lock_file);\n-\trollback_lock_file(&lock_file);\n-\n-\tif (has_unstaged_changes(r, 1) ||\n-\t    has_uncommitted_changes(r, 1)) {\n-\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n-\t\tstruct object_id oid;\n-\n-\t\targv_array_pushl(&stash.args,\n-\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n-\t\tstash.git_cmd = 1;\n-\t\tstash.no_stdin = 1;\n-\t\tstrbuf_reset(&buf);\n-\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n-\t\t\tdie(_(\"Cannot autostash\"));\n-\t\tstrbuf_trim_trailing_newline(&buf);\n-\t\tif (get_oid(buf.buf, &oid))\n-\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n-\t\t\t    buf.buf);\n-\t\tstrbuf_reset(&buf);\n-\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n-\n-\t\tif (safe_create_leading_directories_const(path))\n-\t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t    path);\n-\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n-\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\tif (reset_head(r, NULL, \"reset --hard\",\n-\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n-\t\t\t       default_reflog_action) < 0)\n-\t\t\tdie(_(\"could not reset --hard\"));\n-\n-\t\tif (discard_index(r->index) < 0 ||\n-\t\t\trepo_read_index(r) < 0)\n-\t\t\tdie(_(\"could not read index\"));\n-\t}\n-\tstrbuf_release(&buf);\n-}\n-\n int cmd_rebase(int argc, const char **argv, const char *prefix)\n {\n \tstruct rebase_options options = REBASE_OPTIONS_INIT;\ndiff --git a/sequencer.c b/sequencer.c\nindex b52668f8de..b2951843a1 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -32,6 +32,7 @@\n #include \"alias.h\"\n #include \"commit-reach.h\"\n #include \"rebase-interactive.h\"\n+#include \"reset.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -3655,6 +3656,55 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n+void create_autostash(struct repository *r, const char *path,\n+\t\t      const char *default_reflog_action)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct lock_file lock_file = LOCK_INIT;\n+\tint fd;\n+\n+\tfd = repo_hold_locked_index(r, &lock_file, 0);\n+\trefresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);\n+\tif (0 <= fd)\n+\t\trepo_update_index_if_able(r, &lock_file);\n+\trollback_lock_file(&lock_file);\n+\n+\tif (has_unstaged_changes(r, 1) ||\n+\t    has_uncommitted_changes(r, 1)) {\n+\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n+\t\tstruct object_id oid;\n+\n+\t\targv_array_pushl(&stash.args,\n+\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n+\t\tstash.git_cmd = 1;\n+\t\tstash.no_stdin = 1;\n+\t\tstrbuf_reset(&buf);\n+\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n+\t\t\tdie(_(\"Cannot autostash\"));\n+\t\tstrbuf_trim_trailing_newline(&buf);\n+\t\tif (get_oid(buf.buf, &oid))\n+\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n+\t\t\t    buf.buf);\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n+\n+\t\tif (safe_create_leading_directories_const(path))\n+\t\t\tdie(_(\"Could not create directory for '%s'\"),\n+\t\t\t    path);\n+\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n+\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n+\t\tif (reset_head(r, NULL, \"reset --hard\",\n+\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n+\t\t\t       default_reflog_action) < 0)\n+\t\t\tdie(_(\"could not reset --hard\"));\n+\n+\t\tif (discard_index(r->index) < 0 ||\n+\t\t\trepo_read_index(r) < 0)\n+\t\t\tdie(_(\"could not read index\"));\n+\t}\n+\tstrbuf_release(&buf);\n+}\n+\n int apply_autostash(const char *path)\n {\n \tstruct strbuf stash_sha1 = STRBUF_INIT;\ndiff --git a/sequencer.h b/sequencer.h\nindex bab910f012..44ecfee7f3 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,\n \t\t\t const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n \n+void create_autostash(struct repository *r, const char *path,\n+\t\t      const char *default_reflog_action);\n int apply_autostash(const char *path);\n \n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393602","messageId":"d5af261eb813cc5bb4dd8ad69d63cd322303179c.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 15/19] sequencer: unlink autostash in apply_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:34Z","receivedAt":"2020-03-21T09:22:39Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Explicitly remove autostash file in apply_autostash() once it has been\napplied successfully.\n\nThis is currently a no-op because the only users of this function will unlink\nthe state (including the autostash file) after this function runs.\nHowever, in the future, we will introduce a user of the function that\ndoes not explicitly remove the state so we do it here.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b2951843a1..81b6c700d1 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3746,6 +3746,7 @@ int apply_autostash(const char *path)\n \t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"));\n \t}\n \n+\tunlink(path);\n \tstrbuf_release(&stash_sha1);\n \treturn ret;\n }\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393603","messageId":"e21e2c6416766234114ba94cbaf5b47f0595ee69.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 16/19] sequencer: implement save_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:35Z","receivedAt":"2020-03-21T09:22:41Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Extract common functionality of apply_autostash() into\napply_save_autostash() and use it to implement save_autostash(). This\nfunction will be used in a future commit.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 37 +++++++++++++++++++++++++++----------\n sequencer.h |  1 +\n 2 files changed, 28 insertions(+), 10 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 81b6c700d1..d67d47b332 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3705,7 +3705,7 @@ void create_autostash(struct repository *r, const char *path,\n \tstrbuf_release(&buf);\n }\n \n-int apply_autostash(const char *path)\n+static int apply_save_autostash(const char *path, int attempt_apply)\n {\n \tstruct strbuf stash_sha1 = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\n@@ -3718,13 +3718,17 @@ int apply_autostash(const char *path)\n \t}\n \tstrbuf_trim(&stash_sha1);\n \n-\tchild.git_cmd = 1;\n-\tchild.no_stdout = 1;\n-\tchild.no_stderr = 1;\n-\targv_array_push(&child.args, \"stash\");\n-\targv_array_push(&child.args, \"apply\");\n-\targv_array_push(&child.args, stash_sha1.buf);\n-\tif (!run_command(&child))\n+\tif (attempt_apply) {\n+\t\tchild.git_cmd = 1;\n+\t\tchild.no_stdout = 1;\n+\t\tchild.no_stderr = 1;\n+\t\targv_array_push(&child.args, \"stash\");\n+\t\targv_array_push(&child.args, \"apply\");\n+\t\targv_array_push(&child.args, stash_sha1.buf);\n+\t\tret = run_command(&child);\n+\t}\n+\n+\tif (attempt_apply && !ret)\n \t\tfprintf(stderr, _(\"Applied autostash.\\n\"));\n \telse {\n \t\tstruct child_process store = CHILD_PROCESS_INIT;\n@@ -3740,10 +3744,13 @@ int apply_autostash(const char *path)\n \t\t\tret = error(_(\"cannot store %s\"), stash_sha1.buf);\n \t\telse\n \t\t\tfprintf(stderr,\n-\t\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n+\t\t\t\t_(\"%s\\n\"\n \t\t\t\t  \"Your changes are safe in the stash.\\n\"\n \t\t\t\t  \"You can run \\\"git stash pop\\\" or\"\n-\t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"));\n+\t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"),\n+\t\t\t\tattempt_apply ?\n+\t\t\t\t_(\"Applying autostash resulted in conflicts.\") :\n+\t\t\t\t_(\"Autostash exists; creating a new stash entry.\"));\n \t}\n \n \tunlink(path);\n@@ -3751,6 +3758,16 @@ int apply_autostash(const char *path)\n \treturn ret;\n }\n \n+int save_autostash(const char *path)\n+{\n+\treturn apply_save_autostash(path, 0);\n+}\n+\n+int apply_autostash(const char *path)\n+{\n+\treturn apply_save_autostash(path, 1);\n+}\n+\n static const char *reflog_message(struct replay_opts *opts,\n \tconst char *sub_action, const char *fmt, ...)\n {\ndiff --git a/sequencer.h b/sequencer.h\nindex 44ecfee7f3..1cb0e4e4b7 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -193,6 +193,7 @@ void commit_post_rewrite(struct repository *r,\n \n void create_autostash(struct repository *r, const char *path,\n \t\t      const char *default_reflog_action);\n+int save_autostash(const char *path);\n int apply_autostash(const char *path);\n \n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393604","messageId":"72aa56516efaf4e58c271da362ff157bcb8a9550.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 18/19] t5520: make test_pull_autostash() accept expect_parent_num","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:37Z","receivedAt":"2020-03-21T09:22:42Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Before, test_pull_autostash() was hardcoded to run\n`test_cmp_rev HEAD^ copy` to test that a rebase happened. However, in a\nfuture patch, we plan on testing merging as well. Make\ntest_pull_autostash() accept a parent number as an argument so that, in\nthe future, we can test if a merge happened in addition to a rebase.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t5520-pull.sh | 14 ++++++++------\n 1 file changed, 8 insertions(+), 6 deletions(-)\n\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 2f86fca042..f610dc14de 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -10,11 +10,13 @@ modify () {\n }\n \n test_pull_autostash () {\n+\texpect_parent_num=\"$1\" &&\n+\tshift &&\n \tgit reset --hard before-rebase &&\n \techo dirty >new_file &&\n \tgit add new_file &&\n \tgit pull \"$@\" . copy &&\n-\ttest_cmp_rev HEAD^ copy &&\n+\ttest_cmp_rev HEAD^\"$expect_parent_num\" copy &&\n \techo dirty >expect &&\n \ttest_cmp expect new_file &&\n \techo \"modified again\" >expect &&\n@@ -369,22 +371,22 @@ test_expect_success '--rebase fails with multiple branches' '\n \n test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' '\n \ttest_config rebase.autostash true &&\n-\ttest_pull_autostash --rebase\n+\ttest_pull_autostash 1 --rebase\n '\n \n test_expect_success 'pull --rebase --autostash & rebase.autostash=true' '\n \ttest_config rebase.autostash true &&\n-\ttest_pull_autostash --rebase --autostash\n+\ttest_pull_autostash 1 --rebase --autostash\n '\n \n test_expect_success 'pull --rebase --autostash & rebase.autostash=false' '\n \ttest_config rebase.autostash false &&\n-\ttest_pull_autostash --rebase --autostash\n+\ttest_pull_autostash 1 --rebase --autostash\n '\n \n test_expect_success 'pull --rebase --autostash & rebase.autostash unset' '\n \ttest_unconfig rebase.autostash &&\n-\ttest_pull_autostash --rebase --autostash\n+\ttest_pull_autostash 1 --rebase --autostash\n '\n \n test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '\n@@ -422,7 +424,7 @@ test_expect_success 'pull.rebase' '\n \n test_expect_success 'pull --autostash & pull.rebase=true' '\n \ttest_config pull.rebase true &&\n-\ttest_pull_autostash --autostash\n+\ttest_pull_autostash 1 --autostash\n '\n \n test_expect_success 'pull --no-autostash & pull.rebase=true' '\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393605","messageId":"9e3d4393cae8813cc4718c6ffcc28231b1344fbe.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 17/19] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:36Z","receivedAt":"2020-03-21T09:22:47Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In rebase, one can pass the `--autostash` option to cause the worktree\nto be automatically stashed before continuing with the rebase. This\noption is missing in merge, however.\n\nImplement the `--autostash` option and corresponding `merge.autoStash`\noption in merge which stashes before merging and then pops after.\n\nThis option is useful when a developer has some local changes on a topic\nbranch but they realize that their work depends on another branch.\nPreviously, they had to run something like\n\n\tgit fetch ...\n\tgit stash push\n\tgit merge FETCH_HEAD\n\tgit stash pop\n\nbut now, that is reduced to\n\n\tgit fetch ...\n\tgit merge --autostash FETCH_HEAD\n\nWhen `git reset --hard` is run to abort a merge, the working tree will\nbe left in a clean state, as expected, with the autostash pushed onto\nthe stash stack.\n\nSuggested-by: Alban Gruin <alban.gruin@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/merge.txt  |  10 +++\n Documentation/merge-options.txt |   8 +++\n branch.c                        |   1 +\n builtin/commit.c                |   2 +\n builtin/merge.c                 |  17 ++++++\n builtin/reset.c                 |   7 ++-\n path.c                          |   1 +\n path.h                          |   4 +-\n t/t3033-merge-toplevel.sh       |  22 +++++++\n t/t7600-merge.sh                | 104 ++++++++++++++++++++++++++++++++\n 10 files changed, 174 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex 6a313937f8..88b29127bf 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -70,6 +70,16 @@ merge.stat::\n \tWhether to print the diffstat between ORIG_HEAD and the merge result\n \tat the end of the merge.  True by default.\n \n+merge.autoStash::\n+\tWhen set to true, automatically create a temporary stash entry\n+\tbefore the operation begins, and apply it after the operation\n+\tends.  This means that you can run rebase on a dirty worktree.\n+\tHowever, use with care: the final stash application after a\n+\tsuccessful rebase might result in non-trivial conflicts.\n+\tThis option can be overridden by the `--no-autostash` and\n+\t`--autostash` options of linkgit:git-merge[1].\n+\tDefaults to false.\n+\n merge.tool::\n \tControls which merge tool is used by linkgit:git-mergetool[1].\n \tThe list below shows the valid built-in values.\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 40dc4f5e8c..34493eb58b 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -155,6 +155,14 @@ ifndef::git-pull[]\n \tNote that not all merge strategies may support progress\n \treporting.\n \n+--autostash::\n+--no-autostash::\n+\tAutomatically create a temporary stash entry before the operation\n+\tbegins, and apply it after the operation ends.  This means\n+\tthat you can run rebase on a dirty worktree.  However, use\n+\twith care: the final stash application after a successful\n+\trebase might result in non-trivial conflicts.\n+\n endif::git-pull[]\n \n --allow-unrelated-histories::\ndiff --git a/branch.c b/branch.c\nindex 579494738a..bf2536c70d 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)\n \tunlink(git_path_merge_rr(r));\n \tunlink(git_path_merge_msg(r));\n \tunlink(git_path_merge_mode(r));\n+\tapply_autostash(git_path_merge_autostash(r));\n }\n \n void remove_branch_state(struct repository *r, int verbose)\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 7ba33a3bec..c11894423a 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \tunlink(git_path_merge_mode(the_repository));\n \tunlink(git_path_squash_msg(the_repository));\n \n+\tapply_autostash(git_path_merge_autostash(the_repository));\n+\n \tif (commit_index_files())\n \t\tdie(_(\"repository has been updated, but unable to write\\n\"\n \t\t      \"new_index file. Check that disk is not full and quota is\\n\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex d127d2225f..e038bef5ad 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -82,6 +82,7 @@ static int show_progress = -1;\n static int default_to_upstream = 1;\n static int signoff;\n static const char *sign_commit;\n+static int autostash;\n static int no_verify;\n \n static struct strategy all_strategy[] = {\n@@ -286,6 +287,8 @@ static struct option builtin_merge_options[] = {\n \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n+\tOPT_BOOL(0, \"autostash\", &autostash,\n+\t      N_(\"automatically stash/stash pop before and after\")),\n \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n@@ -441,6 +444,7 @@ static void finish(struct commit *head_commit,\n \t\tstrbuf_addf(&reflog_message, \"%s: %s\",\n \t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n \t}\n+\tapply_autostash(git_path_merge_autostash(the_repository));\n \tif (squash) {\n \t\tsquash_message(head_commit, remoteheads);\n \t} else {\n@@ -634,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn 0;\n \t} else if (!strcmp(k, \"gpg.mintrustlevel\")) {\n \t\tcheck_trust_level = 0;\n+\t} else if (!strcmp(k, \"merge.autostash\")) {\n+\t\tautostash = git_config_bool(k, v);\n+\t\treturn 0;\n \t}\n \n \tstatus = fmt_merge_msg_config(k, v, cb);\n@@ -1291,6 +1298,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \n \t\t/* Invoke 'git reset --merge' */\n \t\tret = cmd_reset(nargc, nargv, prefix);\n+\t\tapply_autostash(git_path_merge_autostash(the_repository));\n \t\tgoto done;\n \t}\n \n@@ -1513,6 +1521,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\tgoto done;\n \t\t}\n \n+\t\tif (autostash)\n+\t\t\tcreate_autostash(the_repository,\n+\t\t\t\t\t git_path_merge_autostash(the_repository),\n+\t\t\t\t\t \"merge\");\n \t\tif (checkout_fast_forward(the_repository,\n \t\t\t\t\t  &head_commit->object.oid,\n \t\t\t\t\t  &commit->object.oid,\n@@ -1579,6 +1591,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (fast_forward == FF_ONLY)\n \t\tdie(_(\"Not possible to fast-forward, aborting.\"));\n \n+\tif (autostash)\n+\t\tcreate_autostash(the_repository,\n+\t\t\t\t git_path_merge_autostash(the_repository),\n+\t\t\t\t \"merge\");\n+\n \t/* We are going to make a new commit. */\n \tgit_committer_info(IDENT_STRICT);\n \ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex 18228c312e..038c8532eb 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -25,6 +25,7 @@\n #include \"cache-tree.h\"\n #include \"submodule.h\"\n #include \"submodule-config.h\"\n+#include \"sequencer.h\"\n \n #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n \n@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \t\tif (reset_type == HARD && !update_ref_status && !quiet)\n \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n \t}\n-\tif (!pathspec.nr)\n+\tif (!pathspec.nr) {\n+\t\tif (reset_type == HARD)\n+\t\t\tsave_autostash(git_path_merge_autostash(the_repository));\n+\n \t\tremove_branch_state(the_repository, 0);\n+\t}\n \n \treturn update_ref_status;\n }\ndiff --git a/path.c b/path.c\nindex 88cf593007..d764738146 100644\n--- a/path.c\n+++ b/path.c\n@@ -1535,5 +1535,6 @@ REPO_GIT_PATH_FUNC(merge_msg, \"MERGE_MSG\")\n REPO_GIT_PATH_FUNC(merge_rr, \"MERGE_RR\")\n REPO_GIT_PATH_FUNC(merge_mode, \"MERGE_MODE\")\n REPO_GIT_PATH_FUNC(merge_head, \"MERGE_HEAD\")\n+REPO_GIT_PATH_FUNC(merge_autostash, \"MERGE_AUTOSTASH\")\n REPO_GIT_PATH_FUNC(fetch_head, \"FETCH_HEAD\")\n REPO_GIT_PATH_FUNC(shallow, \"shallow\")\ndiff --git a/path.h b/path.h\nindex 14d6dcad16..1f1bf8f87a 100644\n--- a/path.h\n+++ b/path.h\n@@ -177,11 +177,12 @@ struct path_cache {\n \tconst char *merge_rr;\n \tconst char *merge_mode;\n \tconst char *merge_head;\n+\tconst char *merge_autostash;\n \tconst char *fetch_head;\n \tconst char *shallow;\n };\n \n-#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n+#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n \n const char *git_path_cherry_pick_head(struct repository *r);\n const char *git_path_revert_head(struct repository *r);\n@@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);\n const char *git_path_merge_rr(struct repository *r);\n const char *git_path_merge_mode(struct repository *r);\n const char *git_path_merge_head(struct repository *r);\n+const char *git_path_merge_autostash(struct repository *r);\n const char *git_path_fetch_head(struct repository *r);\n const char *git_path_shallow(struct repository *r);\n \ndiff --git a/t/t3033-merge-toplevel.sh b/t/t3033-merge-toplevel.sh\nindex d314599428..e29c284b9b 100755\n--- a/t/t3033-merge-toplevel.sh\n+++ b/t/t3033-merge-toplevel.sh\n@@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' '\n \ttest_must_fail git merge five\n '\n \n+test_expect_success 'refuse two-project merge by default, quit before --autostash happens' '\n+\tt3033_reset &&\n+\tgit reset --hard four &&\n+\techo change >>one.t &&\n+\tgit diff >expect &&\n+\ttest_must_fail git merge --autostash five 2>err &&\n+\ttest_i18ngrep ! \"stash\" err &&\n+\tgit diff >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'two-project merge with --allow-unrelated-histories' '\n \tt3033_reset &&\n \tgit reset --hard four &&\n@@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' '\n \tgit diff --exit-code five\n '\n \n+test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' '\n+\tt3033_reset &&\n+\tgit reset --hard four &&\n+\techo change >>one.t &&\n+\tgit diff one.t >expect &&\n+\tgit merge --allow-unrelated-histories --autostash five 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit diff one.t >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 4fa0ef8e3b..c08e4315f4 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -30,13 +30,17 @@ Testing basic merge operations/option parsing.\n . \"$TEST_DIRECTORY\"/lib-gpg.sh\n \n test_write_lines 1 2 3 4 5 6 7 8 9 >file\n+cp file file.orig\n test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1\n+test_write_lines 1 2 '3 X' 4 5 6 7 8 9 >file.3\n test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5\n test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9\n test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1\n test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n+test_write_lines '1 X' 2 3 4 5 6 7 8 '9 X' >result.1-9\n test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n+test_write_lines '1 X' 2 '3 X' 4 '5 X' 6 7 8 '9 X' >result.1-3-5-9\n test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n \n create_merge_msgs () {\n@@ -675,6 +679,106 @@ test_expect_success 'refresh the index before merging' '\n \tgit merge c3\n '\n \n+test_expect_success 'merge with --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit merge --autostash c2 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'merge with merge.autoStash' '\n+\ttest_config merge.autoStash true &&\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit merge c2 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'fast-forward merge with --autostash' '\n+\tgit reset --hard c0 &&\n+\tgit merge-file file file.orig file.5 &&\n+\tgit merge --autostash c1 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\ttest_cmp result.1-5 file\n+'\n+\n+test_expect_success 'octopus merge with --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.3 &&\n+\tgit merge --autostash c2 c3 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5-9 merge-result &&\n+\ttest_cmp result.1-3-5-9 file\n+'\n+\n+test_expect_success 'conflicted merge with --autostash, --abort restores stash' '\n+\tgit reset --hard c3 &&\n+\tcp file.1 file &&\n+\ttest_must_fail git merge --autostash c7 &&\n+\tgit merge --abort 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\ttest_cmp file.1 file\n+'\n+\n+test_expect_success 'completed merge with --no-commit and --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit diff >expect &&\n+\tgit merge --no-commit --autostash c2 &&\n+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n+\ttest_cmp expect actual &&\n+\tgit commit 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit diff >expect &&\n+\tgit merge --no-commit --autostash c2 &&\n+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n+\ttest_cmp expect actual &&\n+\tgit merge --abort 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit diff >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit diff >expect &&\n+\tgit merge --no-commit --autostash c2 &&\n+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n+\ttest_cmp expect actual &&\n+\tgit reset --hard 2>err &&\n+\ttest_i18ngrep \"Autostash exists; creating a new stash entry.\" err &&\n+\tgit diff --exit-code\n+'\n+\n+test_expect_success 'merge with conflicted --autostash changes' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9y &&\n+\tgit diff >expect &&\n+\ttest_when_finished \"test_might_fail git stash drop\" &&\n+\tgit merge --autostash c3 2>err &&\n+\ttest_i18ngrep \"Applying autostash resulted in conflicts.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-9 merge-result &&\n+\tgit stash show -p >actual &&\n+\ttest_cmp expect actual\n+'\n+\n cat >expected.branch <<\\EOF\n Merge branch 'c5-branch' (early part)\n EOF\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"393606","messageId":"17caf6d66f5ee33e6aef7265ab33dee83f24f05c.1584782450.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v3 19/19] pull: pass --autostash to merge","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-03-21T09:21:38Z","receivedAt":"2020-03-21T09:22:47Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Before, `--autostash` only worked with `git pull --rebase`. However, in\nthe last patch, merge learned `--autostash` as well so there's no reason\nwhy we should have this restriction anymore. Teach pull to pass\n`--autostash` to merge, just like it did for rebase.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-pull.txt      |  9 -------\n Documentation/merge-options.txt |  4 +--\n builtin/pull.c                  |  9 ++++---\n t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------\n 4 files changed, 42 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt\nindex dfb901f8b8..ba3772de9f 100644\n--- a/Documentation/git-pull.txt\n+++ b/Documentation/git-pull.txt\n@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.\n --no-rebase::\n \tOverride earlier --rebase.\n \n---autostash::\n---no-autostash::\n-\tBefore starting rebase, stash local modifications away (see\n-\tlinkgit:git-stash[1]) if needed, and apply the stash entry when\n-\tdone. `--no-autostash` is useful to override the `rebase.autoStash`\n-\tconfiguration variable (see linkgit:git-config[1]).\n-+\n-This option is only valid when \"--rebase\" is used.\n-\n Options related to fetching\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n \ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 34493eb58b..ae56cca826 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -155,6 +155,8 @@ ifndef::git-pull[]\n \tNote that not all merge strategies may support progress\n \treporting.\n \n+endif::git-pull[]\n+\n --autostash::\n --no-autostash::\n \tAutomatically create a temporary stash entry before the operation\n@@ -163,8 +165,6 @@ ifndef::git-pull[]\n \twith care: the final stash application after a successful\n \trebase might result in non-trivial conflicts.\n \n-endif::git-pull[]\n-\n --allow-unrelated-histories::\n \tBy default, `git merge` command refuses to merge histories\n \tthat do not share a common ancestor.  This option can be\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 3e624d1e00..9beb4841d1 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -163,7 +163,7 @@ static struct option pull_options[] = {\n \t\tN_(\"verify that the named commit has a valid GPG signature\"),\n \t\tPARSE_OPT_NOARG),\n \tOPT_BOOL(0, \"autostash\", &opt_autostash,\n-\t\tN_(\"automatically stash/stash pop before and after rebase\")),\n+\t\tN_(\"automatically stash/stash pop before and after\")),\n \tOPT_PASSTHRU_ARGV('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n \t\tN_(\"merge strategy to use\"),\n \t\t0),\n@@ -661,6 +661,10 @@ static int run_merge(void)\n \targv_array_pushv(&args, opt_strategy_opts.argv);\n \tif (opt_gpg_sign)\n \t\targv_array_push(&args, opt_gpg_sign);\n+\tif (opt_autostash == 0)\n+\t\targv_array_push(&args, \"--no-autostash\");\n+\telse if (opt_autostash == 1)\n+\t\targv_array_push(&args, \"--autostash\");\n \tif (opt_allow_unrelated_histories > 0)\n \t\targv_array_push(&args, \"--allow-unrelated-histories\");\n \n@@ -908,9 +912,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (get_oid(\"HEAD\", &orig_head))\n \t\toidclr(&orig_head);\n \n-\tif (!opt_rebase && opt_autostash != -1)\n-\t\tdie(_(\"--[no-]autostash option is only valid with --rebase.\"));\n-\n \tautostash = config_autostash;\n \tif (opt_rebase) {\n \t\tif (opt_autostash != -1)\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex f610dc14de..37535d63a9 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -28,7 +28,7 @@ test_pull_autostash_fail () {\n \techo dirty >new_file &&\n \tgit add new_file &&\n \ttest_must_fail git pull \"$@\" . copy 2>err &&\n-\ttest_i18ngrep \"uncommitted changes.\" err\n+\ttest_i18ngrep \"\\(uncommitted changes.\\)\\|\\(overwritten by merge:\\)\" err\n }\n \n test_expect_success setup '\n@@ -404,13 +404,40 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '\n \ttest_pull_autostash_fail --rebase --no-autostash\n '\n \n-for i in --autostash --no-autostash\n-do\n-\ttest_expect_success \"pull $i (without --rebase) is illegal\" '\n-\t\ttest_must_fail git pull $i . copy 2>err &&\n-\t\ttest_i18ngrep \"only valid with --rebase\" err\n-\t'\n-done\n+test_expect_success 'pull succeeds with dirty working directory and merge.autostash set' '\n+\ttest_config merge.autostash true &&\n+\ttest_pull_autostash 2\n+'\n+\n+test_expect_success 'pull --autostash & merge.autostash=true' '\n+\ttest_config merge.autostash true &&\n+\ttest_pull_autostash 2 --autostash\n+'\n+\n+test_expect_success 'pull --autostash & merge.autostash=false' '\n+\ttest_config merge.autostash false &&\n+\ttest_pull_autostash 2 --autostash\n+'\n+\n+test_expect_success 'pull --autostash & merge.autostash unset' '\n+\ttest_unconfig merge.autostash &&\n+\ttest_pull_autostash 2 --autostash\n+'\n+\n+test_expect_success 'pull --no-autostash & merge.autostash=true' '\n+\ttest_config merge.autostash true &&\n+\ttest_pull_autostash_fail --no-autostash\n+'\n+\n+test_expect_success 'pull --no-autostash & merge.autostash=false' '\n+\ttest_config merge.autostash false &&\n+\ttest_pull_autostash_fail --no-autostash\n+'\n+\n+test_expect_success 'pull --no-autostash & merge.autostash unset' '\n+\ttest_unconfig merge.autostash &&\n+\ttest_pull_autostash_fail --no-autostash\n+'\n \n test_expect_success 'pull.rebase' '\n \tgit reset --hard before-rebase &&\n-- \n2.25.0.114.g5b0ca878e0\n\n"},{"id":"394581","messageId":"0b237d3c-5b2d-1714-9243-8f32621a332d@gmail.com","threadId":"52048","inReplyTo":"7c37777f07191c8ac1d26300f9465b90758550b2.1584782450.git.liu.denton@gmail.com","subject":"Re: [PATCH v3 03/19] sequencer: use file strbuf for read_oneliner()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-02T13:34:57Z","receivedAt":"2020-04-02T13:35:03Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 21/03/2020 09:21, Denton Liu wrote:\n> In the original read_oneliner logic, we duplicated the logic for\n> strbuf_trim_trailing_newline() with one exception: instead of preventing\n> buffer accesses below index 0, it would prevent buffer accesses below\n> index `orig_len`. Although this is correct, it isn't worth having the\n> duplicated logic around.\n> \n> Add a second strbuf to which files are read and run\n> strbuf_trim_trailing_newline() directly on this strbuf then concatenate\n> this strbuf with the argument strbuf at the end of the function. The\n> function's external behaviour is unchanged.\n\nread_oneliner() is typically called with an strbuf that is used multiple \ntimes with the caller resetting it in-between calls. This saves us from \nhaving to allocate a new buffer for each call. The change here negates \nthat as we end up allocating a new buffer each time and then copying the \nresult. I'd be surprised if any of the callers actually wanted to append \nto the buffer, I suspect they all call strbuf_reset() before passing the \nbuffer to read_oneliner(). If that is the case maybe we should think \nabout adding the call to strbuf_reset() inside read_oneliner() and \ndocumenting it as resetting the buffer before reading. Then we can just \nuse strbuf_trim() on the 'real' buffer.\n\nBest Wishes\n\nPhillip\n\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   sequencer.c | 24 +++++++++++++-----------\n>   1 file changed, 13 insertions(+), 11 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index e528225e78..c49fe76fe6 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -431,26 +431,28 @@ static int write_message(const void *buf, size_t len, const char *filename,\n>   static int read_oneliner(struct strbuf *buf,\n>   \tconst char *path, int skip_if_empty)\n>   {\n> -\tint orig_len = buf->len;\n> +\tint ret = 0;\n> +\tstruct strbuf file_buf = STRBUF_INIT;\n>   \n>   \tif (!file_exists(path))\n>   \t\treturn 0;\n>   \n> -\tif (strbuf_read_file(buf, path, 0) < 0) {\n> +\tif (strbuf_read_file(&file_buf, path, 0) < 0) {\n>   \t\twarning_errno(_(\"could not read '%s'\"), path);\n> -\t\treturn 0;\n> +\t\tgoto done;\n>   \t}\n>   \n> -\tif (buf->len > orig_len && buf->buf[buf->len - 1] == '\\n') {\n> -\t\tif (--buf->len > orig_len && buf->buf[buf->len - 1] == '\\r')\n> -\t\t\t--buf->len;\n> -\t\tbuf->buf[buf->len] = '\\0';\n> -\t}\n> +\tstrbuf_trim_trailing_newline(&file_buf);\n>   \n> -\tif (skip_if_empty && buf->len == orig_len)\n> -\t\treturn 0;\n> +\tif (skip_if_empty && !file_buf.len)\n> +\t\tgoto done;\n>   \n> -\treturn 1;\n> +\tstrbuf_addbuf(buf, &file_buf);\n> +\tret = 1;\n> +\n> +done:\n> +\tstrbuf_release(&file_buf);\n> +\treturn ret;\n>   }\n>   \n>   static struct tree *empty_tree(struct repository *r)\n> \n"},{"id":"394583","messageId":"5c81eca7-0990-1c60-7f9f-04bce05be3ba@gmail.com","threadId":"52048","inReplyTo":"e42f4bdd4d763be4062f12fa6f1481849e87a0ad.1584782450.git.liu.denton@gmail.com","subject":"Re: [PATCH v3 05/19] sequencer: configurably warn on non-existent files","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-02T13:39:26Z","receivedAt":"2020-04-02T13:39:38Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 21/03/2020 09:21, Denton Liu wrote:\n> In the future, we plan on externing read_oneliner(). Future users of\n> read_oneliner() will want the ability to output warnings in the event\n> that the `path` doesn't exist. Introduce the\n> `READ_ONELINER_WARN_NON_EXISTENCE`\n\nThis is a rather cumbersome name, I think Junio's original suggestion of \nREAD_ONELINER_WARN_MISSING is nicer\n\n> flag which, if active, would issue a\n> warning when a file doesn't exist by skipping the `!file_exists()` check\n> and letting `strbuf_read_file()` handle that case.\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   sequencer.c | 3 ++-\n>   1 file changed, 2 insertions(+), 1 deletion(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index abb2a21e9d..92e8d38290 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename,\n>   }\n>   \n>   #define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n> +#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)\n>   \n>   /*\n>    * Reads a file that was presumably written by a shell script, i.e. with an\n> @@ -436,7 +437,7 @@ static int read_oneliner(struct strbuf *buf,\n>   \tint ret = 0;\n>   \tstruct strbuf file_buf = STRBUF_INIT;\n>   \n> -\tif (!file_exists(path))\n> +\tif (!(flags & READ_ONELINER_WARN_NON_EXISTENCE) && !file_exists(path))\n>   \t\treturn 0;\n\nThis isn't the fault of you patch but it would be more efficient to do \nthe read and then check for ENOENT/ENOTDIR rather than calling \nfile_exists() first\n\nBest Wishes\n\nPhillip\n\n>   \tif (strbuf_read_file(&file_buf, path, 0) < 0) {\n> \n"},{"id":"394585","messageId":"51bbff1e-ccca-5d13-24f1-942dcfe6174d@gmail.com","threadId":"52048","inReplyTo":"d07a50c5b22128263aaa8eef94546fe8bc8a9ec8.1584782450.git.liu.denton@gmail.com","subject":"Re: [PATCH v3 07/19] rebase: use read_oneliner()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-02T13:41:27Z","receivedAt":"2020-04-02T13:41:33Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 21/03/2020 09:21, Denton Liu wrote:\n> Since in sequencer.c, read_one() basically duplicates the functionality\n> of read_oneliner(), reduce code duplication by replacing read_one() with\n> read_oneliner().\n> \n> This was done with the following Coccinelle script\n> \n> \t@@\n> \texpression a, b;\n> \t@@\n> \t- read_one(a, b)\n> \t+ !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)\n> \n> and long lines were manually broken up.\n\nIt's great to see read_one() going away\n\nThanks\n\nPhillip\n\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   builtin/rebase.c | 37 +++++++++++++++++--------------------\n>   1 file changed, 17 insertions(+), 20 deletions(-)\n> \n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index bff53d5d16..1146463099 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -586,15 +586,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o\n>   \treturn path.buf;\n>   }\n>   \n> -/* Read one file, then strip line endings */\n> -static int read_one(const char *path, struct strbuf *buf)\n> -{\n> -\tif (strbuf_read_file(buf, path, 0) < 0)\n> -\t\treturn error_errno(_(\"could not read '%s'\"), path);\n> -\tstrbuf_trim_trailing_newline(buf);\n> -\treturn 0;\n> -}\n> -\n>   /* Initialize the rebase options from the state directory. */\n>   static int read_basic_state(struct rebase_options *opts)\n>   {\n> @@ -602,8 +593,10 @@ static int read_basic_state(struct rebase_options *opts)\n>   \tstruct strbuf buf = STRBUF_INIT;\n>   \tstruct object_id oid;\n>   \n> -\tif (read_one(state_dir_path(\"head-name\", opts), &head_name) ||\n> -\t    read_one(state_dir_path(\"onto\", opts), &buf))\n> +\tif (!read_oneliner(&head_name, state_dir_path(\"head-name\", opts),\n> +\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE) ||\n> +\t    !read_oneliner(&buf, state_dir_path(\"onto\", opts),\n> +\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n>   \t\treturn -1;\n>   \topts->head_name = starts_with(head_name.buf, \"refs/\") ?\n>   \t\txstrdup(head_name.buf) : NULL;\n> @@ -619,9 +612,11 @@ static int read_basic_state(struct rebase_options *opts)\n>   \t */\n>   \tstrbuf_reset(&buf);\n>   \tif (file_exists(state_dir_path(\"orig-head\", opts))) {\n> -\t\tif (read_one(state_dir_path(\"orig-head\", opts), &buf))\n> +\t\tif (!read_oneliner(&buf, state_dir_path(\"orig-head\", opts),\n> +\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n>   \t\t\treturn -1;\n> -\t} else if (read_one(state_dir_path(\"head\", opts), &buf))\n> +\t} else if (!read_oneliner(&buf, state_dir_path(\"head\", opts),\n> +\t\t\t\t  READ_ONELINER_WARN_NON_EXISTENCE))\n>   \t\treturn -1;\n>   \tif (get_oid(buf.buf, &opts->orig_head))\n>   \t\treturn error(_(\"invalid orig-head: '%s'\"), buf.buf);\n> @@ -641,8 +636,8 @@ static int read_basic_state(struct rebase_options *opts)\n>   \n>   \tif (file_exists(state_dir_path(\"allow_rerere_autoupdate\", opts))) {\n>   \t\tstrbuf_reset(&buf);\n> -\t\tif (read_one(state_dir_path(\"allow_rerere_autoupdate\", opts),\n> -\t\t\t    &buf))\n> +\t\tif (!read_oneliner(&buf, state_dir_path(\"allow_rerere_autoupdate\", opts),\n> +\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n>   \t\t\treturn -1;\n>   \t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n>   \t\t\topts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;\n> @@ -655,8 +650,8 @@ static int read_basic_state(struct rebase_options *opts)\n>   \n>   \tif (file_exists(state_dir_path(\"gpg_sign_opt\", opts))) {\n>   \t\tstrbuf_reset(&buf);\n> -\t\tif (read_one(state_dir_path(\"gpg_sign_opt\", opts),\n> -\t\t\t    &buf))\n> +\t\tif (!read_oneliner(&buf, state_dir_path(\"gpg_sign_opt\", opts),\n> +\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n>   \t\t\treturn -1;\n>   \t\tfree(opts->gpg_sign_opt);\n>   \t\topts->gpg_sign_opt = xstrdup(buf.buf);\n> @@ -664,7 +659,8 @@ static int read_basic_state(struct rebase_options *opts)\n>   \n>   \tif (file_exists(state_dir_path(\"strategy\", opts))) {\n>   \t\tstrbuf_reset(&buf);\n> -\t\tif (read_one(state_dir_path(\"strategy\", opts), &buf))\n> +\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy\", opts),\n> +\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n>   \t\t\treturn -1;\n>   \t\tfree(opts->strategy);\n>   \t\topts->strategy = xstrdup(buf.buf);\n> @@ -672,7 +668,8 @@ static int read_basic_state(struct rebase_options *opts)\n>   \n>   \tif (file_exists(state_dir_path(\"strategy_opts\", opts))) {\n>   \t\tstrbuf_reset(&buf);\n> -\t\tif (read_one(state_dir_path(\"strategy_opts\", opts), &buf))\n> +\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy_opts\", opts),\n> +\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n>   \t\t\treturn -1;\n>   \t\tfree(opts->strategy_opts);\n>   \t\topts->strategy_opts = xstrdup(buf.buf);\n> @@ -724,7 +721,7 @@ static int apply_autostash(struct rebase_options *opts)\n>   \tif (!file_exists(path))\n>   \t\treturn 0;\n>   \n> -\tif (read_one(path, &autostash))\n> +\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))\n>   \t\treturn error(_(\"Could not read '%s'\"), path);\n>   \t/* Ensure that the hash is not mistaken for a number */\n>   \tstrbuf_addstr(&autostash, \"^0\");\n> \n"},{"id":"394586","messageId":"66697cd4-ca4e-804c-311d-8132bb1c0a8b@gmail.com","threadId":"52048","inReplyTo":"03bdaeebc993058bf14a0d196f5a1a68800e538e.1584782450.git.liu.denton@gmail.com","subject":"Re: [PATCH v3 09/19] rebase: use apply_autostash() from sequencer.c","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-02T14:59:34Z","receivedAt":"2020-04-02T14:59:40Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 21/03/2020 09:21, Denton Liu wrote:\n> The apply_autostash() function in builtin/rebase.c is similar enough to\n> the apply_autostash() function in sequencer.c that they are almost\n> interchangeable.\n\nYou say they are almost interchangeable and then use them as if they are \ncompletely interchangeable. I think they are interchangeable, the only \ndifference I could see is that the code in rebase.c redirects stdin when \nforking 'git stash apply' but the sequencer does not, I don't think this \nmatters.\n\nBest Wishes\n\nPhillip\n\n  Make the sequencer.c version extern and use it in\n> rebase.\n> \n> The rebase version was introduced in 6defce2b02 (builtin rebase: support\n> `--autostash` option, 2018-09-04) as part of the shell to C conversion.\n> It opted to duplicate the function because, at the time, there was\n> another in-progress project converting interactive rebase from shell to\n> C as well and they did not want to clash with them by refactoring\n> sequencer.c version of apply_autostash(). Since both efforts are long\n> done, we can freely combine them together now.\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   builtin/rebase.c | 49 ++----------------------------------------------\n>   sequencer.c      |  2 +-\n>   sequencer.h      |  2 ++\n>   3 files changed, 5 insertions(+), 48 deletions(-)\n> \n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index 1146463099..ceb115247a 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -712,51 +712,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)\n>   \treturn 0;\n>   }\n>   \n> -static int apply_autostash(struct rebase_options *opts)\n> -{\n> -\tconst char *path = state_dir_path(\"autostash\", opts);\n> -\tstruct strbuf autostash = STRBUF_INIT;\n> -\tstruct child_process stash_apply = CHILD_PROCESS_INIT;\n> -\n> -\tif (!file_exists(path))\n> -\t\treturn 0;\n> -\n> -\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))\n> -\t\treturn error(_(\"Could not read '%s'\"), path);\n> -\t/* Ensure that the hash is not mistaken for a number */\n> -\tstrbuf_addstr(&autostash, \"^0\");\n> -\targv_array_pushl(&stash_apply.args,\n> -\t\t\t \"stash\", \"apply\", autostash.buf, NULL);\n> -\tstash_apply.git_cmd = 1;\n> -\tstash_apply.no_stderr = stash_apply.no_stdout =\n> -\t\tstash_apply.no_stdin = 1;\n> -\tif (!run_command(&stash_apply))\n> -\t\tprintf(_(\"Applied autostash.\\n\"));\n> -\telse {\n> -\t\tstruct argv_array args = ARGV_ARRAY_INIT;\n> -\t\tint res = 0;\n> -\n> -\t\targv_array_pushl(&args,\n> -\t\t\t\t \"stash\", \"store\", \"-m\", \"autostash\", \"-q\",\n> -\t\t\t\t autostash.buf, NULL);\n> -\t\tif (run_command_v_opt(args.argv, RUN_GIT_CMD))\n> -\t\t\tres = error(_(\"Cannot store %s\"), autostash.buf);\n> -\t\targv_array_clear(&args);\n> -\t\tstrbuf_release(&autostash);\n> -\t\tif (res)\n> -\t\t\treturn res;\n> -\n> -\t\tfprintf(stderr,\n> -\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n> -\t\t\t  \"Your changes are safe in the stash.\\n\"\n> -\t\t\t  \"You can run \\\"git stash pop\\\" or \\\"git stash drop\\\" \"\n> -\t\t\t  \"at any time.\\n\"));\n> -\t}\n> -\n> -\tstrbuf_release(&autostash);\n> -\treturn 0;\n> -}\n> -\n>   static int finish_rebase(struct rebase_options *opts)\n>   {\n>   \tstruct strbuf dir = STRBUF_INIT;\n> @@ -764,7 +719,7 @@ static int finish_rebase(struct rebase_options *opts)\n>   \tint ret = 0;\n>   \n>   \tdelete_ref(NULL, \"REBASE_HEAD\", NULL, REF_NO_DEREF);\n> -\tapply_autostash(opts);\n> +\tapply_autostash(state_dir_path(\"autostash\", opts));\n>   \tclose_object_store(the_repository->objects);\n>   \t/*\n>   \t * We ignore errors in 'gc --auto', since the\n> @@ -1209,7 +1164,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)\n>   \t} else if (status == 2) {\n>   \t\tstruct strbuf dir = STRBUF_INIT;\n>   \n> -\t\tapply_autostash(opts);\n> +\t\tapply_autostash(state_dir_path(\"autostash\", opts));\n>   \t\tstrbuf_addstr(&dir, opts->state_dir);\n>   \t\tremove_dir_recursively(&dir, 0);\n>   \t\tstrbuf_release(&dir);\n> diff --git a/sequencer.c b/sequencer.c\n> index ad40a8b3fc..b52668f8de 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -3655,7 +3655,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n>   \treturn -1;\n>   }\n>   \n> -static int apply_autostash(const char *path)\n> +int apply_autostash(const char *path)\n>   {\n>   \tstruct strbuf stash_sha1 = STRBUF_INIT;\n>   \tstruct child_process child = CHILD_PROCESS_INIT;\n> diff --git a/sequencer.h b/sequencer.h\n> index 5c9662a60a..bab910f012 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,\n>   \t\t\t const struct commit *current_head,\n>   \t\t\t const struct object_id *new_head);\n>   \n> +int apply_autostash(const char *path);\n> +\n>   #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n>   #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n>   void print_commit_summary(struct repository *repo,\n> \n"},{"id":"394587","messageId":"47714403-0537-176d-ffc3-86d914d0cf8b@gmail.com","threadId":"52048","inReplyTo":"a14a2e0849fea91c6c41cf71b8d049123bea0c6d.1584782450.git.liu.denton@gmail.com","subject":"Re: [PATCH v3 11/19] reset: extract reset_head() from rebase","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-02T15:04:43Z","receivedAt":"2020-04-02T15:04:50Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 21/03/2020 09:21, Denton Liu wrote:\n> Continue the process of lib-ifying the autostash code. In a future\n> commit, this will be used to implement `--autostash` in other builtins.\n> \n> This patch is best viewed with `--color-moved`.\n\nIndeed! It looks good. Looking ahead I hope we can use this to avoid \nforking 'git checkout' in sequencer.c\n\nBest Wishes\n\nPhillip\n\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   Makefile         |   1 +\n>   builtin/rebase.c | 139 +---------------------------------------------\n>   reset.c          | 140 +++++++++++++++++++++++++++++++++++++++++++++++\n>   reset.h          |  20 +++++++\n>   4 files changed, 162 insertions(+), 138 deletions(-)\n>   create mode 100644 reset.c\n>   create mode 100644 reset.h\n> \n> diff --git a/Makefile b/Makefile\n> index f709fc3f77..338fb55b73 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -970,6 +970,7 @@ LIB_OBJS += replace-object.o\n>   LIB_OBJS += repo-settings.o\n>   LIB_OBJS += repository.o\n>   LIB_OBJS += rerere.o\n> +LIB_OBJS += reset.o\n>   LIB_OBJS += resolve-undo.o\n>   LIB_OBJS += revision.o\n>   LIB_OBJS += run-command.o\n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index 52eb45b6c4..caff67b00d 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -27,6 +27,7 @@\n>   #include \"branch.h\"\n>   #include \"sequencer.h\"\n>   #include \"rebase-interactive.h\"\n> +#include \"reset.h\"\n>   \n>   #define DEFAULT_REFLOG_ACTION \"rebase\"\n>   \n> @@ -766,144 +767,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)\n>   \t}\n>   }\n>   \n> -#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n> -\n> -#define RESET_HEAD_DETACH (1<<0)\n> -#define RESET_HEAD_HARD (1<<1)\n> -#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n> -#define RESET_HEAD_REFS_ONLY (1<<3)\n> -#define RESET_ORIG_HEAD (1<<4)\n> -\n> -static int reset_head(struct repository *r, struct object_id *oid, const char *action,\n> -\t\t      const char *switch_to_branch, unsigned flags,\n> -\t\t      const char *reflog_orig_head, const char *reflog_head,\n> -\t\t      const char *default_reflog_action)\n> -{\n> -\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n> -\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n> -\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n> -\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n> -\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n> -\tstruct object_id head_oid;\n> -\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n> -\tstruct lock_file lock = LOCK_INIT;\n> -\tstruct unpack_trees_options unpack_tree_opts;\n> -\tstruct tree *tree;\n> -\tconst char *reflog_action;\n> -\tstruct strbuf msg = STRBUF_INIT;\n> -\tsize_t prefix_len;\n> -\tstruct object_id *orig = NULL, oid_orig,\n> -\t\t*old_orig = NULL, oid_old_orig;\n> -\tint ret = 0, nr = 0;\n> -\n> -\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n> -\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n> -\n> -\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n> -\t\tret = -1;\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n> -\t\tret = error(_(\"could not determine HEAD revision\"));\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -\tif (!oid)\n> -\t\toid = &head_oid;\n> -\n> -\tif (refs_only)\n> -\t\tgoto reset_head_refs;\n> -\n> -\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n> -\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n> -\tunpack_tree_opts.head_idx = 1;\n> -\tunpack_tree_opts.src_index = r->index;\n> -\tunpack_tree_opts.dst_index = r->index;\n> -\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n> -\tunpack_tree_opts.update = 1;\n> -\tunpack_tree_opts.merge = 1;\n> -\tif (!detach_head)\n> -\t\tunpack_tree_opts.reset = 1;\n> -\n> -\tif (repo_read_index_unmerged(r) < 0) {\n> -\t\tret = error(_(\"could not read index\"));\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n> -\t\tret = error(_(\"failed to find tree of %s\"),\n> -\t\t\t    oid_to_hex(&head_oid));\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n> -\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n> -\t\tret = -1;\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -\ttree = parse_tree_indirect(oid);\n> -\tprime_cache_tree(r, r->index, tree);\n> -\n> -\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n> -\t\tret = error(_(\"could not write index\"));\n> -\t\tgoto leave_reset_head;\n> -\t}\n> -\n> -reset_head_refs:\n> -\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n> -\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n> -\tprefix_len = msg.len;\n> -\n> -\tif (update_orig_head) {\n> -\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n> -\t\t\told_orig = &oid_old_orig;\n> -\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n> -\t\t\torig = &oid_orig;\n> -\t\t\tif (!reflog_orig_head) {\n> -\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n> -\t\t\t\treflog_orig_head = msg.buf;\n> -\t\t\t}\n> -\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n> -\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n> -\t\t} else if (old_orig)\n> -\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n> -\t}\n> -\n> -\tif (!reflog_head) {\n> -\t\tstrbuf_setlen(&msg, prefix_len);\n> -\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n> -\t\treflog_head = msg.buf;\n> -\t}\n> -\tif (!switch_to_branch)\n> -\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n> -\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n> -\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n> -\telse {\n> -\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n> -\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n> -\t\tif (!ret)\n> -\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n> -\t\t\t\t\t    reflog_head);\n> -\t}\n> -\tif (run_hook)\n> -\t\trun_hook_le(NULL, \"post-checkout\",\n> -\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n> -\t\t\t    oid_to_hex(oid), \"1\", NULL);\n> -\n> -leave_reset_head:\n> -\tstrbuf_release(&msg);\n> -\trollback_lock_file(&lock);\n> -\twhile (nr)\n> -\t\tfree((void *)desc[--nr].buffer);\n> -\treturn ret;\n> -}\n> -\n>   static int move_to_original_branch(struct rebase_options *opts)\n>   {\n>   \tstruct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;\n> diff --git a/reset.c b/reset.c\n> new file mode 100644\n> index 0000000000..79b683bffa\n> --- /dev/null\n> +++ b/reset.c\n> @@ -0,0 +1,140 @@\n> +#include \"git-compat-util.h\"\n> +#include \"cache-tree.h\"\n> +#include \"lockfile.h\"\n> +#include \"refs.h\"\n> +#include \"reset.h\"\n> +#include \"run-command.h\"\n> +#include \"tree-walk.h\"\n> +#include \"tree.h\"\n> +#include \"unpack-trees.h\"\n> +\n> +int reset_head(struct repository *r, struct object_id *oid, const char *action,\n> +\t       const char *switch_to_branch, unsigned flags,\n> +\t       const char *reflog_orig_head, const char *reflog_head,\n> +\t       const char *default_reflog_action)\n> +{\n> +\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n> +\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n> +\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n> +\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n> +\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n> +\tstruct object_id head_oid;\n> +\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n> +\tstruct lock_file lock = LOCK_INIT;\n> +\tstruct unpack_trees_options unpack_tree_opts;\n> +\tstruct tree *tree;\n> +\tconst char *reflog_action;\n> +\tstruct strbuf msg = STRBUF_INIT;\n> +\tsize_t prefix_len;\n> +\tstruct object_id *orig = NULL, oid_orig,\n> +\t\t*old_orig = NULL, oid_old_orig;\n> +\tint ret = 0, nr = 0;\n> +\n> +\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n> +\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n> +\n> +\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n> +\t\tret = -1;\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n> +\t\tret = error(_(\"could not determine HEAD revision\"));\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +\tif (!oid)\n> +\t\toid = &head_oid;\n> +\n> +\tif (refs_only)\n> +\t\tgoto reset_head_refs;\n> +\n> +\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n> +\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n> +\tunpack_tree_opts.head_idx = 1;\n> +\tunpack_tree_opts.src_index = r->index;\n> +\tunpack_tree_opts.dst_index = r->index;\n> +\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n> +\tunpack_tree_opts.update = 1;\n> +\tunpack_tree_opts.merge = 1;\n> +\tif (!detach_head)\n> +\t\tunpack_tree_opts.reset = 1;\n> +\n> +\tif (repo_read_index_unmerged(r) < 0) {\n> +\t\tret = error(_(\"could not read index\"));\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n> +\t\tret = error(_(\"failed to find tree of %s\"),\n> +\t\t\t    oid_to_hex(&head_oid));\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n> +\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n> +\t\tret = -1;\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +\ttree = parse_tree_indirect(oid);\n> +\tprime_cache_tree(r, r->index, tree);\n> +\n> +\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n> +\t\tret = error(_(\"could not write index\"));\n> +\t\tgoto leave_reset_head;\n> +\t}\n> +\n> +reset_head_refs:\n> +\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n> +\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n> +\tprefix_len = msg.len;\n> +\n> +\tif (update_orig_head) {\n> +\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n> +\t\t\told_orig = &oid_old_orig;\n> +\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n> +\t\t\torig = &oid_orig;\n> +\t\t\tif (!reflog_orig_head) {\n> +\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n> +\t\t\t\treflog_orig_head = msg.buf;\n> +\t\t\t}\n> +\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n> +\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n> +\t\t} else if (old_orig)\n> +\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n> +\t}\n> +\n> +\tif (!reflog_head) {\n> +\t\tstrbuf_setlen(&msg, prefix_len);\n> +\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n> +\t\treflog_head = msg.buf;\n> +\t}\n> +\tif (!switch_to_branch)\n> +\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n> +\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n> +\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n> +\telse {\n> +\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n> +\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n> +\t\tif (!ret)\n> +\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n> +\t\t\t\t\t    reflog_head);\n> +\t}\n> +\tif (run_hook)\n> +\t\trun_hook_le(NULL, \"post-checkout\",\n> +\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n> +\t\t\t    oid_to_hex(oid), \"1\", NULL);\n> +\n> +leave_reset_head:\n> +\tstrbuf_release(&msg);\n> +\trollback_lock_file(&lock);\n> +\twhile (nr)\n> +\t\tfree((void *)desc[--nr].buffer);\n> +\treturn ret;\n> +\n> +}\n> diff --git a/reset.h b/reset.h\n> new file mode 100644\n> index 0000000000..12f83c78e2\n> --- /dev/null\n> +++ b/reset.h\n> @@ -0,0 +1,20 @@\n> +#ifndef RESET_H\n> +#define RESET_H\n> +\n> +#include \"hash.h\"\n> +#include \"repository.h\"\n> +\n> +#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n> +\n> +#define RESET_HEAD_DETACH (1<<0)\n> +#define RESET_HEAD_HARD (1<<1)\n> +#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n> +#define RESET_HEAD_REFS_ONLY (1<<3)\n> +#define RESET_ORIG_HEAD (1<<4)\n> +\n> +int reset_head(struct repository *r, struct object_id *oid, const char *action,\n> +\t       const char *switch_to_branch, unsigned flags,\n> +\t       const char *reflog_orig_head, const char *reflog_head,\n> +\t       const char *default_reflog_action);\n> +\n> +#endif\n> \n"},{"id":"394588","messageId":"eb4ea9a5-a076-fb63-e587-1851f14218db@gmail.com","threadId":"52048","inReplyTo":"e21e2c6416766234114ba94cbaf5b47f0595ee69.1584782450.git.liu.denton@gmail.com","subject":"Re: [PATCH v3 16/19] sequencer: implement save_autostash()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-02T15:10:58Z","receivedAt":"2020-04-02T15:11:04Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 21/03/2020 09:21, Denton Liu wrote:\n> Extract common functionality of apply_autostash() into\n> apply_save_autostash() and use it to implement save_autostash(). This\n> function will be used in a future commit.\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   sequencer.c | 37 +++++++++++++++++++++++++++----------\n>   sequencer.h |  1 +\n>   2 files changed, 28 insertions(+), 10 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index 81b6c700d1..d67d47b332 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -3705,7 +3705,7 @@ void create_autostash(struct repository *r, const char *path,\n>   \tstrbuf_release(&buf);\n>   }\n>   \n> -int apply_autostash(const char *path)\n> +static int apply_save_autostash(const char *path, int attempt_apply)\n>   {\n>   \tstruct strbuf stash_sha1 = STRBUF_INIT;\n>   \tstruct child_process child = CHILD_PROCESS_INIT;\n> @@ -3718,13 +3718,17 @@ int apply_autostash(const char *path)\n>   \t}\n>   \tstrbuf_trim(&stash_sha1);\n>   \n> -\tchild.git_cmd = 1;\n> -\tchild.no_stdout = 1;\n> -\tchild.no_stderr = 1;\n> -\targv_array_push(&child.args, \"stash\");\n> -\targv_array_push(&child.args, \"apply\");\n> -\targv_array_push(&child.args, stash_sha1.buf);\n> -\tif (!run_command(&child))\n> +\tif (attempt_apply) {\n> +\t\tchild.git_cmd = 1;\n> +\t\tchild.no_stdout = 1;\n> +\t\tchild.no_stderr = 1;\n> +\t\targv_array_push(&child.args, \"stash\");\n> +\t\targv_array_push(&child.args, \"apply\");\n> +\t\targv_array_push(&child.args, stash_sha1.buf);\n> +\t\tret = run_command(&child);\n> +\t}\n> +\n> +\tif (attempt_apply && !ret)\n\nIf attempt_only is not set then we wont get any message - is this \nintended? Previously we always showed the message when applying was \nsuccessful\n\n>   \t\tfprintf(stderr, _(\"Applied autostash.\\n\"));\n>   \telse {\n>   \t\tstruct child_process store = CHILD_PROCESS_INIT;\n> @@ -3740,10 +3744,13 @@ int apply_autostash(const char *path)\n>   \t\t\tret = error(_(\"cannot store %s\"), stash_sha1.buf);\n>   \t\telse\n>   \t\t\tfprintf(stderr,\n> -\t\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n> +\t\t\t\t_(\"%s\\n\"\n>   \t\t\t\t  \"Your changes are safe in the stash.\\n\"\n>   \t\t\t\t  \"You can run \\\"git stash pop\\\" or\"\n> -\t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"));\n> +\t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"),\n> +\t\t\t\tattempt_apply ?\n> +\t\t\t\t_(\"Applying autostash resulted in conflicts.\") :\n> +\t\t\t\t_(\"Autostash exists; creating a new stash entry.\"));\n\nI'm a bit confused by the new message, perhaps the commit message could \nexplain what attempt_only is used for and then I suspect the reason for \nthis change will become clear\n\nBest Wishes\n\nPhillip\n\n>   \t}\n>   \n>   \tunlink(path);\n> @@ -3751,6 +3758,16 @@ int apply_autostash(const char *path)\n>   \treturn ret;\n>   }\n>   \n> +int save_autostash(const char *path)\n> +{\n> +\treturn apply_save_autostash(path, 0);\n> +}\n> +\n> +int apply_autostash(const char *path)\n> +{\n> +\treturn apply_save_autostash(path, 1);\n> +}\n> +\n>   static const char *reflog_message(struct replay_opts *opts,\n>   \tconst char *sub_action, const char *fmt, ...)\n>   {\n> diff --git a/sequencer.h b/sequencer.h\n> index 44ecfee7f3..1cb0e4e4b7 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -193,6 +193,7 @@ void commit_post_rewrite(struct repository *r,\n>   \n>   void create_autostash(struct repository *r, const char *path,\n>   \t\t      const char *default_reflog_action);\n> +int save_autostash(const char *path);\n>   int apply_autostash(const char *path);\n>   \n>   #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n> \n"},{"id":"394590","messageId":"cd7cb5aa-0a08-29dc-e1cc-f65a4010622f@gmail.com","threadId":"52048","inReplyTo":"9e3d4393cae8813cc4718c6ffcc28231b1344fbe.1584782450.git.liu.denton@gmail.com","subject":"Re: [PATCH v3 17/19] merge: teach --autostash option","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-02T15:24:54Z","receivedAt":"2020-04-02T15:24:59Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 21/03/2020 09:21, Denton Liu wrote:\n> In rebase, one can pass the `--autostash` option to cause the worktree\n> to be automatically stashed before continuing with the rebase. This\n> option is missing in merge, however.\n> \n> Implement the `--autostash` option and corresponding `merge.autoStash`\n> option in merge which stashes before merging and then pops after.\n> \n> This option is useful when a developer has some local changes on a topic\n> branch but they realize that their work depends on another branch.\n> Previously, they had to run something like\n> \n> \tgit fetch ...\n> \tgit stash push\n> \tgit merge FETCH_HEAD\n> \tgit stash pop\n> \n> but now, that is reduced to\n> \n> \tgit fetch ...\n> \tgit merge --autostash FETCH_HEAD\n> \n> When `git reset --hard` is run to abort a merge, the working tree will\n> be left in a clean state, as expected, with the autostash pushed onto\n> the stash stack.\n> \n> Suggested-by: Alban Gruin <alban.gruin@gmail.com>\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   Documentation/config/merge.txt  |  10 +++\n>   Documentation/merge-options.txt |   8 +++\n>   branch.c                        |   1 +\n>   builtin/commit.c                |   2 +\n>   builtin/merge.c                 |  17 ++++++\n>   builtin/reset.c                 |   7 ++-\n>   path.c                          |   1 +\n>   path.h                          |   4 +-\n>   t/t3033-merge-toplevel.sh       |  22 +++++++\n>   t/t7600-merge.sh                | 104 ++++++++++++++++++++++++++++++++\n>   10 files changed, 174 insertions(+), 2 deletions(-)\n> \n> diff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\n> index 6a313937f8..88b29127bf 100644\n> --- a/Documentation/config/merge.txt\n> +++ b/Documentation/config/merge.txt\n> @@ -70,6 +70,16 @@ merge.stat::\n>   \tWhether to print the diffstat between ORIG_HEAD and the merge result\n>   \tat the end of the merge.  True by default.\n>   \n> +merge.autoStash::\n> +\tWhen set to true, automatically create a temporary stash entry\n> +\tbefore the operation begins, and apply it after the operation\n> +\tends.  This means that you can run rebase\n\ncopy and paste error - s/rebase/merge/\n\n> on a dirty worktree.\n> +\tHowever, use with care: the final stash application after a\n> +\tsuccessful rebase might result in non-trivial conflicts.\n> +\tThis option can be overridden by the `--no-autostash` and\n> +\t`--autostash` options of linkgit:git-merge[1].\n> +\tDefaults to false.\n> +\n>   merge.tool::\n>   \tControls which merge tool is used by linkgit:git-mergetool[1].\n>   \tThe list below shows the valid built-in values.\n> diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\n> index 40dc4f5e8c..34493eb58b 100644\n> --- a/Documentation/merge-options.txt\n> +++ b/Documentation/merge-options.txt\n> @@ -155,6 +155,14 @@ ifndef::git-pull[]\n>   \tNote that not all merge strategies may support progress\n>   \treporting.\n>   \n> +--autostash::\n> +--no-autostash::\n> +\tAutomatically create a temporary stash entry before the operation\n> +\tbegins, and apply it after the operation ends.  This means\n> +\tthat you can run rebase on a dirty worktree.  However, use > +\twith care: the final stash application after a successful\n> +\trebase might result in non-trivial conflicts.\n\ns/rebase/merge/\n\n> +\n>   endif::git-pull[]\n>   \n>   --allow-unrelated-histories::\n> diff --git a/branch.c b/branch.c\n> index 579494738a..bf2536c70d 100644\n> --- a/branch.c\n> +++ b/branch.c\n> @@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)\n>   \tunlink(git_path_merge_rr(r));\n>   \tunlink(git_path_merge_msg(r));\n>   \tunlink(git_path_merge_mode(r));\n> +\tapply_autostash(git_path_merge_autostash(r));\n>   }\n>   \n>   void remove_branch_state(struct repository *r, int verbose)\n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index 7ba33a3bec..c11894423a 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>   \tunlink(git_path_merge_mode(the_repository));\n>   \tunlink(git_path_squash_msg(the_repository));\n>   \n> +\tapply_autostash(git_path_merge_autostash(the_repository));\n> +\n\nIt's hard to tell from the limited context but do we want to run \ncommit_index_files() before applying the autostash? I wonder if this \nshould be using remove_merge_branch_state() instead of duplicating it as \nwell.\n\n>   \tif (commit_index_files())\n>   \t\tdie(_(\"repository has been updated, but unable to write\\n\"\n>   \t\t      \"new_index file. Check that disk is not full and quota is\\n\"\n> diff --git a/builtin/merge.c b/builtin/merge.c\n> index d127d2225f..e038bef5ad 100644\n> --- a/builtin/merge.c\n> +++ b/builtin/merge.c\n> @@ -82,6 +82,7 @@ static int show_progress = -1;\n>   static int default_to_upstream = 1;\n>   static int signoff;\n>   static const char *sign_commit;\n> +static int autostash;\n>   static int no_verify;\n>   \n>   static struct strategy all_strategy[] = {\n> @@ -286,6 +287,8 @@ static struct option builtin_merge_options[] = {\n>   \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n>   \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n>   \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n> +\tOPT_BOOL(0, \"autostash\", &autostash,\n> +\t      N_(\"automatically stash/stash pop before and after\")),\n\nIf we're adding this option to several commands (rebase, merge, pull) \nthen it might be worth defining it in parse-options.h\n\n>   \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n>   \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n>   \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n> @@ -441,6 +444,7 @@ static void finish(struct commit *head_commit,\n>   \t\tstrbuf_addf(&reflog_message, \"%s: %s\",\n>   \t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n>   \t}\n> +\tapply_autostash(git_path_merge_autostash(the_repository));\n>   \tif (squash) {\n>   \t\tsquash_message(head_commit, remoteheads);\n>   \t} else {\n> @@ -634,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n>   \t\treturn 0;\n>   \t} else if (!strcmp(k, \"gpg.mintrustlevel\")) {\n>   \t\tcheck_trust_level = 0;\n> +\t} else if (!strcmp(k, \"merge.autostash\")) {\n> +\t\tautostash = git_config_bool(k, v);\n> +\t\treturn 0;\n>   \t}\n>   \n>   \tstatus = fmt_merge_msg_config(k, v, cb);\n> @@ -1291,6 +1298,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>   \n>   \t\t/* Invoke 'git reset --merge' */\n>   \t\tret = cmd_reset(nargc, nargv, prefix);\n> +\t\tapply_autostash(git_path_merge_autostash(the_repository));\n>   \t\tgoto done;\n>   \t}\n>   \n> @@ -1513,6 +1521,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>   \t\t\tgoto done;\n>   \t\t}\n>   \n> +\t\tif (autostash)\n> +\t\t\tcreate_autostash(the_repository,\n> +\t\t\t\t\t git_path_merge_autostash(the_repository),\n> +\t\t\t\t\t \"merge\");\n>   \t\tif (checkout_fast_forward(the_repository,\n>   \t\t\t\t\t  &head_commit->object.oid,\n>   \t\t\t\t\t  &commit->object.oid,\n> @@ -1579,6 +1591,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>   \tif (fast_forward == FF_ONLY)\n>   \t\tdie(_(\"Not possible to fast-forward, aborting.\"));\n>   \n> +\tif (autostash)\n> +\t\tcreate_autostash(the_repository,\n> +\t\t\t\t git_path_merge_autostash(the_repository),\n> +\t\t\t\t \"merge\");\n> +\n>   \t/* We are going to make a new commit. */\n>   \tgit_committer_info(IDENT_STRICT);\n>   \n> diff --git a/builtin/reset.c b/builtin/reset.c\n> index 18228c312e..038c8532eb 100644\n> --- a/builtin/reset.c\n> +++ b/builtin/reset.c\n> @@ -25,6 +25,7 @@\n>   #include \"cache-tree.h\"\n>   #include \"submodule.h\"\n>   #include \"submodule-config.h\"\n> +#include \"sequencer.h\"\n>   \n>   #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n>   \n> @@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n>   \t\tif (reset_type == HARD && !update_ref_status && !quiet)\n>   \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n>   \t}\n> -\tif (!pathspec.nr)\n> +\tif (!pathspec.nr) {\n> +\t\tif (reset_type == HARD)\n> +\t\t\tsave_autostash(git_path_merge_autostash(the_repository));\n> +\n>   \t\tremove_branch_state(the_repository, 0);\n\nThis removes the autostash file for all reset types but we only keep the \nstash in the case of 'reset --hard' which is confusing.\n\nBest Wishes\n\nPhillip\n\n> +\t}\n>   \n>   \treturn update_ref_status;\n>   }\n> diff --git a/path.c b/path.c\n> index 88cf593007..d764738146 100644\n> --- a/path.c\n> +++ b/path.c\n> @@ -1535,5 +1535,6 @@ REPO_GIT_PATH_FUNC(merge_msg, \"MERGE_MSG\")\n>   REPO_GIT_PATH_FUNC(merge_rr, \"MERGE_RR\")\n>   REPO_GIT_PATH_FUNC(merge_mode, \"MERGE_MODE\")\n>   REPO_GIT_PATH_FUNC(merge_head, \"MERGE_HEAD\")\n> +REPO_GIT_PATH_FUNC(merge_autostash, \"MERGE_AUTOSTASH\")\n>   REPO_GIT_PATH_FUNC(fetch_head, \"FETCH_HEAD\")\n>   REPO_GIT_PATH_FUNC(shallow, \"shallow\")\n> diff --git a/path.h b/path.h\n> index 14d6dcad16..1f1bf8f87a 100644\n> --- a/path.h\n> +++ b/path.h\n> @@ -177,11 +177,12 @@ struct path_cache {\n>   \tconst char *merge_rr;\n>   \tconst char *merge_mode;\n>   \tconst char *merge_head;\n> +\tconst char *merge_autostash;\n>   \tconst char *fetch_head;\n>   \tconst char *shallow;\n>   };\n>   \n> -#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n> +#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n>   \n>   const char *git_path_cherry_pick_head(struct repository *r);\n>   const char *git_path_revert_head(struct repository *r);\n> @@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);\n>   const char *git_path_merge_rr(struct repository *r);\n>   const char *git_path_merge_mode(struct repository *r);\n>   const char *git_path_merge_head(struct repository *r);\n> +const char *git_path_merge_autostash(struct repository *r);\n>   const char *git_path_fetch_head(struct repository *r);\n>   const char *git_path_shallow(struct repository *r);\n>   \n> diff --git a/t/t3033-merge-toplevel.sh b/t/t3033-merge-toplevel.sh\n> index d314599428..e29c284b9b 100755\n> --- a/t/t3033-merge-toplevel.sh\n> +++ b/t/t3033-merge-toplevel.sh\n> @@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' '\n>   \ttest_must_fail git merge five\n>   '\n>   \n> +test_expect_success 'refuse two-project merge by default, quit before --autostash happens' '\n> +\tt3033_reset &&\n> +\tgit reset --hard four &&\n> +\techo change >>one.t &&\n> +\tgit diff >expect &&\n> +\ttest_must_fail git merge --autostash five 2>err &&\n> +\ttest_i18ngrep ! \"stash\" err &&\n> +\tgit diff >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n>   test_expect_success 'two-project merge with --allow-unrelated-histories' '\n>   \tt3033_reset &&\n>   \tgit reset --hard four &&\n> @@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' '\n>   \tgit diff --exit-code five\n>   '\n>   \n> +test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' '\n> +\tt3033_reset &&\n> +\tgit reset --hard four &&\n> +\techo change >>one.t &&\n> +\tgit diff one.t >expect &&\n> +\tgit merge --allow-unrelated-histories --autostash five 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit diff one.t >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n>   test_done\n> diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\n> index 4fa0ef8e3b..c08e4315f4 100755\n> --- a/t/t7600-merge.sh\n> +++ b/t/t7600-merge.sh\n> @@ -30,13 +30,17 @@ Testing basic merge operations/option parsing.\n>   . \"$TEST_DIRECTORY\"/lib-gpg.sh\n>   \n>   test_write_lines 1 2 3 4 5 6 7 8 9 >file\n> +cp file file.orig\n>   test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1\n> +test_write_lines 1 2 '3 X' 4 5 6 7 8 9 >file.3\n>   test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5\n>   test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9\n>   test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n>   test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1\n>   test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n> +test_write_lines '1 X' 2 3 4 5 6 7 8 '9 X' >result.1-9\n>   test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n> +test_write_lines '1 X' 2 '3 X' 4 '5 X' 6 7 8 '9 X' >result.1-3-5-9\n>   test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n>   \n>   create_merge_msgs () {\n> @@ -675,6 +679,106 @@ test_expect_success 'refresh the index before merging' '\n>   \tgit merge c3\n>   '\n>   \n> +test_expect_success 'merge with --autostash' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9 &&\n> +\tgit merge --autostash c2 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit show HEAD:file >merge-result &&\n> +\ttest_cmp result.1-5 merge-result &&\n> +\ttest_cmp result.1-5-9 file\n> +'\n> +\n> +test_expect_success 'merge with merge.autoStash' '\n> +\ttest_config merge.autoStash true &&\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9 &&\n> +\tgit merge c2 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit show HEAD:file >merge-result &&\n> +\ttest_cmp result.1-5 merge-result &&\n> +\ttest_cmp result.1-5-9 file\n> +'\n> +\n> +test_expect_success 'fast-forward merge with --autostash' '\n> +\tgit reset --hard c0 &&\n> +\tgit merge-file file file.orig file.5 &&\n> +\tgit merge --autostash c1 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\ttest_cmp result.1-5 file\n> +'\n> +\n> +test_expect_success 'octopus merge with --autostash' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.3 &&\n> +\tgit merge --autostash c2 c3 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit show HEAD:file >merge-result &&\n> +\ttest_cmp result.1-5-9 merge-result &&\n> +\ttest_cmp result.1-3-5-9 file\n> +'\n> +\n> +test_expect_success 'conflicted merge with --autostash, --abort restores stash' '\n> +\tgit reset --hard c3 &&\n> +\tcp file.1 file &&\n> +\ttest_must_fail git merge --autostash c7 &&\n> +\tgit merge --abort 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\ttest_cmp file.1 file\n> +'\n> +\n> +test_expect_success 'completed merge with --no-commit and --autostash' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9 &&\n> +\tgit diff >expect &&\n> +\tgit merge --no-commit --autostash c2 &&\n> +\tgit stash show -p MERGE_AUTOSTASH >actual &&\n> +\ttest_cmp expect actual &&\n> +\tgit commit 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit show HEAD:file >merge-result &&\n> +\ttest_cmp result.1-5 merge-result &&\n> +\ttest_cmp result.1-5-9 file\n> +'\n> +\n> +test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9 &&\n> +\tgit diff >expect &&\n> +\tgit merge --no-commit --autostash c2 &&\n> +\tgit stash show -p MERGE_AUTOSTASH >actual &&\n> +\ttest_cmp expect actual &&\n> +\tgit merge --abort 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit diff >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9 &&\n> +\tgit diff >expect &&\n> +\tgit merge --no-commit --autostash c2 &&\n> +\tgit stash show -p MERGE_AUTOSTASH >actual &&\n> +\ttest_cmp expect actual &&\n> +\tgit reset --hard 2>err &&\n> +\ttest_i18ngrep \"Autostash exists; creating a new stash entry.\" err &&\n> +\tgit diff --exit-code\n> +'\n> +\n> +test_expect_success 'merge with conflicted --autostash changes' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9y &&\n> +\tgit diff >expect &&\n> +\ttest_when_finished \"test_might_fail git stash drop\" &&\n> +\tgit merge --autostash c3 2>err &&\n> +\ttest_i18ngrep \"Applying autostash resulted in conflicts.\" err &&\n> +\tgit show HEAD:file >merge-result &&\n> +\ttest_cmp result.1-9 merge-result &&\n> +\tgit stash show -p >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n>   cat >expected.branch <<\\EOF\n>   Merge branch 'c5-branch' (early part)\n>   EOF\n> \n"},{"id":"394593","messageId":"1e0f5aa4-b688-0e77-6646-defb42e7672e@gmail.com","threadId":"52048","inReplyTo":"17caf6d66f5ee33e6aef7265ab33dee83f24f05c.1584782450.git.liu.denton@gmail.com","subject":"Re: [PATCH v3 19/19] pull: pass --autostash to merge","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-02T16:07:00Z","receivedAt":"2020-04-02T16:07:05Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 21/03/2020 09:21, Denton Liu wrote:\n> Before, `--autostash` only worked with `git pull --rebase`. However, in\n> the last patch, merge learned `--autostash` as well so there's no reason\n> why we should have this restriction anymore. Teach pull to pass\n> `--autostash` to merge, just like it did for rebase.\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   Documentation/git-pull.txt      |  9 -------\n>   Documentation/merge-options.txt |  4 +--\n>   builtin/pull.c                  |  9 ++++---\n>   t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------\n>   4 files changed, 42 insertions(+), 23 deletions(-)\n> \n> diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt\n> index dfb901f8b8..ba3772de9f 100644\n> --- a/Documentation/git-pull.txt\n> +++ b/Documentation/git-pull.txt\n> @@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.\n>   --no-rebase::\n>   \tOverride earlier --rebase.\n>   \n> ---autostash::\n> ---no-autostash::\n> -\tBefore starting rebase, stash local modifications away (see\n> -\tlinkgit:git-stash[1]) if needed, and apply the stash entry when\n> -\tdone. `--no-autostash` is useful to override the `rebase.autoStash`\n> -\tconfiguration variable (see linkgit:git-config[1]).\n> -+\n> -This option is only valid when \"--rebase\" is used.\n> -\n>   Options related to fetching\n>   ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n>   \n> diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\n> index 34493eb58b..ae56cca826 100644\n> --- a/Documentation/merge-options.txt\n> +++ b/Documentation/merge-options.txt\n> @@ -155,6 +155,8 @@ ifndef::git-pull[]\n>   \tNote that not all merge strategies may support progress\n>   \treporting.\n>   \n> +endif::git-pull[]\n> +\n>   --autostash::\n>   --no-autostash::\n>   \tAutomatically create a temporary stash entry before the operation\n> @@ -163,8 +165,6 @@ ifndef::git-pull[]\n>   \twith care: the final stash application after a successful\n>   \trebase might result in non-trivial conflicts.\n>   \n> -endif::git-pull[]\n> -\n\nThis change means we need a neutral wording for the --autostash \ndocumentation that works with rebase/merge/pull rather than mentioning a \nspecific command\n\nBest Wishes\n\nPhillip\n\n>   --allow-unrelated-histories::\n>   \tBy default, `git merge` command refuses to merge histories\n>   \tthat do not share a common ancestor.  This option can be\n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index 3e624d1e00..9beb4841d1 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -163,7 +163,7 @@ static struct option pull_options[] = {\n>   \t\tN_(\"verify that the named commit has a valid GPG signature\"),\n>   \t\tPARSE_OPT_NOARG),\n>   \tOPT_BOOL(0, \"autostash\", &opt_autostash,\n> -\t\tN_(\"automatically stash/stash pop before and after rebase\")),\n> +\t\tN_(\"automatically stash/stash pop before and after\")),\n>   \tOPT_PASSTHRU_ARGV('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n>   \t\tN_(\"merge strategy to use\"),\n>   \t\t0),\n> @@ -661,6 +661,10 @@ static int run_merge(void)\n>   \targv_array_pushv(&args, opt_strategy_opts.argv);\n>   \tif (opt_gpg_sign)\n>   \t\targv_array_push(&args, opt_gpg_sign);\n> +\tif (opt_autostash == 0)\n> +\t\targv_array_push(&args, \"--no-autostash\");\n> +\telse if (opt_autostash == 1)\n> +\t\targv_array_push(&args, \"--autostash\");\n>   \tif (opt_allow_unrelated_histories > 0)\n>   \t\targv_array_push(&args, \"--allow-unrelated-histories\");\n>   \n> @@ -908,9 +912,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n>   \tif (get_oid(\"HEAD\", &orig_head))\n>   \t\toidclr(&orig_head);\n>   \n> -\tif (!opt_rebase && opt_autostash != -1)\n> -\t\tdie(_(\"--[no-]autostash option is only valid with --rebase.\"));\n> -\n>   \tautostash = config_autostash;\n>   \tif (opt_rebase) {\n>   \t\tif (opt_autostash != -1)\n> diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\n> index f610dc14de..37535d63a9 100755\n> --- a/t/t5520-pull.sh\n> +++ b/t/t5520-pull.sh\n> @@ -28,7 +28,7 @@ test_pull_autostash_fail () {\n>   \techo dirty >new_file &&\n>   \tgit add new_file &&\n>   \ttest_must_fail git pull \"$@\" . copy 2>err &&\n> -\ttest_i18ngrep \"uncommitted changes.\" err\n> +\ttest_i18ngrep \"\\(uncommitted changes.\\)\\|\\(overwritten by merge:\\)\" err\n>   }\n>   \n>   test_expect_success setup '\n> @@ -404,13 +404,40 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '\n>   \ttest_pull_autostash_fail --rebase --no-autostash\n>   '\n>   \n> -for i in --autostash --no-autostash\n> -do\n> -\ttest_expect_success \"pull $i (without --rebase) is illegal\" '\n> -\t\ttest_must_fail git pull $i . copy 2>err &&\n> -\t\ttest_i18ngrep \"only valid with --rebase\" err\n> -\t'\n> -done\n> +test_expect_success 'pull succeeds with dirty working directory and merge.autostash set' '\n> +\ttest_config merge.autostash true &&\n> +\ttest_pull_autostash 2\n> +'\n> +\n> +test_expect_success 'pull --autostash & merge.autostash=true' '\n> +\ttest_config merge.autostash true &&\n> +\ttest_pull_autostash 2 --autostash\n> +'\n> +\n> +test_expect_success 'pull --autostash & merge.autostash=false' '\n> +\ttest_config merge.autostash false &&\n> +\ttest_pull_autostash 2 --autostash\n> +'\n> +\n> +test_expect_success 'pull --autostash & merge.autostash unset' '\n> +\ttest_unconfig merge.autostash &&\n> +\ttest_pull_autostash 2 --autostash\n> +'\n> +\n> +test_expect_success 'pull --no-autostash & merge.autostash=true' '\n> +\ttest_config merge.autostash true &&\n> +\ttest_pull_autostash_fail --no-autostash\n> +'\n> +\n> +test_expect_success 'pull --no-autostash & merge.autostash=false' '\n> +\ttest_config merge.autostash false &&\n> +\ttest_pull_autostash_fail --no-autostash\n> +'\n> +\n> +test_expect_success 'pull --no-autostash & merge.autostash unset' '\n> +\ttest_unconfig merge.autostash &&\n> +\ttest_pull_autostash_fail --no-autostash\n> +'\n>   \n>   test_expect_success 'pull.rebase' '\n>   \tgit reset --hard before-rebase &&\n> \n"},{"id":"394655","messageId":"20200403103126.GA3163329@generichostname","threadId":"52048","inReplyTo":"cd7cb5aa-0a08-29dc-e1cc-f65a4010622f@gmail.com","subject":"Re: [PATCH v3 17/19] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-03T10:31:26Z","receivedAt":"2020-04-03T10:31:34Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Phillip,\n\nThanks for your detailed review on the series. I've updated everything\naccording to your comments except for the parts below:\n\nOn Thu, Apr 02, 2020 at 04:24:54PM +0100, Phillip Wood wrote:\n> > diff --git a/branch.c b/branch.c\n> > index 579494738a..bf2536c70d 100644\n> > --- a/branch.c\n> > +++ b/branch.c\n> > @@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)\n> >   \tunlink(git_path_merge_rr(r));\n> >   \tunlink(git_path_merge_msg(r));\n> >   \tunlink(git_path_merge_mode(r));\n> > +\tapply_autostash(git_path_merge_autostash(r));\n> >   }\n> >   void remove_branch_state(struct repository *r, int verbose)\n> > diff --git a/builtin/commit.c b/builtin/commit.c\n> > index 7ba33a3bec..c11894423a 100644\n> > --- a/builtin/commit.c\n> > +++ b/builtin/commit.c\n> > @@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n> >   \tunlink(git_path_merge_mode(the_repository));\n> >   \tunlink(git_path_squash_msg(the_repository));\n> > +\tapply_autostash(git_path_merge_autostash(the_repository));\n> > +\n> \n> It's hard to tell from the limited context but do we want to run\n> commit_index_files() before applying the autostash?\n\nI don't think it really matters which order we run it in. When we run\napply_autostash(), we only ever touch the working tree, not the index so\nit doesn't matter if it's run before or after. I'd prefer to keep it\nhere because if we ever refactor this to use\nremove_merge_branch_state(), the person working on this will be able to\nperform the refactor more easily without having to worry about implicit\nordering dependencies.\n\n> I wonder if this should\n> be using remove_merge_branch_state() instead of duplicating it as well.\n\nWe can _almost_ use remove_branch_state() even! Unfortunately,\nremove_merge_branch_state() calls `unlink(git_path_merge_rr(r))` which\nwe cannot do since repo_rerere() relies on it later. Perhaps we can\ncan move repo_rerere() earlier?\n \n> > diff --git a/builtin/reset.c b/builtin/reset.c\n> > index 18228c312e..038c8532eb 100644\n> > --- a/builtin/reset.c\n> > +++ b/builtin/reset.c\n> > @@ -25,6 +25,7 @@\n> >   #include \"cache-tree.h\"\n> >   #include \"submodule.h\"\n> >   #include \"submodule-config.h\"\n> > +#include \"sequencer.h\"\n> >   #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n> > @@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n> >   \t\tif (reset_type == HARD && !update_ref_status && !quiet)\n> >   \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n> >   \t}\n> > -\tif (!pathspec.nr)\n> > +\tif (!pathspec.nr) {\n> > +\t\tif (reset_type == HARD)\n> > +\t\t\tsave_autostash(git_path_merge_autostash(the_repository));\n> > +\n> >   \t\tremove_branch_state(the_repository, 0);\n> \n> This removes the autostash file for all reset types but we only keep the\n> stash in the case of 'reset --hard' which is confusing.\n\nI was worried that this change would be controversial... The rationale\nbehind this change was that with `reset --hard`, we want to leave a\nclean working tree behind so we save it into the stash reflog. In all\nother cases, remove_branch_state() will apply the saved stash entry\nwhich should be fine since users don't expect a clean worktree with the\nother reset types.\n\nI considered saving the autostash in all cases of reset but\n`git merge --abort` invokes `git reset --merge` behind the scenes so\nwe'd have to consider that. Perhaps we can make all resets save the\nstash entry and, in the case of `merge --abort`, we can add some extra\nlogic to subvert this so that the stash entry is applied?\n\nI'm not sure about what the most intuitive behaviour is. I thought that\nthis implementation would be the best but I'm not entirely sure. I'd\nappreciate some more discussion on this.\n\nThanks,\n\nDenton\n\n> Best Wishes\n> \n> Phillip\n> \n> > +\t}\n> >   \treturn update_ref_status;\n> >   }\n"},{"id":"394656","messageId":"20200403105639.GA3193506@generichostname","threadId":"52048","inReplyTo":"20200403103126.GA3163329@generichostname","subject":"Re: [PATCH v3 17/19] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-03T10:56:39Z","receivedAt":"2020-04-03T10:56:46Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"On Fri, Apr 03, 2020 at 06:31:26AM -0400, Denton Liu wrote:\n> > > diff --git a/builtin/reset.c b/builtin/reset.c\n> > > index 18228c312e..038c8532eb 100644\n> > > --- a/builtin/reset.c\n> > > +++ b/builtin/reset.c\n> > > @@ -25,6 +25,7 @@\n> > >   #include \"cache-tree.h\"\n> > >   #include \"submodule.h\"\n> > >   #include \"submodule-config.h\"\n> > > +#include \"sequencer.h\"\n> > >   #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n> > > @@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n> > >   \t\tif (reset_type == HARD && !update_ref_status && !quiet)\n> > >   \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n> > >   \t}\n> > > -\tif (!pathspec.nr)\n> > > +\tif (!pathspec.nr) {\n> > > +\t\tif (reset_type == HARD)\n> > > +\t\t\tsave_autostash(git_path_merge_autostash(the_repository));\n> > > +\n> > >   \t\tremove_branch_state(the_repository, 0);\n> > \n> > This removes the autostash file for all reset types but we only keep the\n> > stash in the case of 'reset --hard' which is confusing.\n> \n> I was worried that this change would be controversial... The rationale\n> behind this change was that with `reset --hard`, we want to leave a\n> clean working tree behind so we save it into the stash reflog. In all\n> other cases, remove_branch_state() will apply the saved stash entry\n> which should be fine since users don't expect a clean worktree with the\n> other reset types.\n> \n> I considered saving the autostash in all cases of reset but\n> `git merge --abort` invokes `git reset --merge` behind the scenes so\n> we'd have to consider that. Perhaps we can make all resets save the\n> stash entry and, in the case of `merge --abort`, we can add some extra\n> logic to subvert this so that the stash entry is applied?\n\nPerhaps something like this?\n\n-- >8 --\ncommit 14d0b569cb7675f00d32d3d7fad7564fcaeca458\nAuthor: Denton Liu <liu.denton@gmail.com>\nDate:   Fri Apr 3 06:50:34 2020 -0400\n\n    squash! merge: teach --autostash option\n    \n    Stash is saved when any reset is run, when git merge --abort is run,\n    stash is applied.\n\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 9573d77096..31b82d614c 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -1242,6 +1242,8 @@ static int merging_a_throwaway_tag(struct commit *commit)\n \treturn is_throwaway_tag;\n }\n \n+static GIT_PATH_FUNC(git_path_merge_autostash_saved, \"MERGE_AUTOSTASH_SAVED\")\n+\n int cmd_merge(int argc, const char **argv, const char *prefix)\n {\n \tstruct object_id result_tree, stash, head_oid;\n@@ -1295,9 +1297,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\tif (!file_exists(git_path_merge_head(the_repository)))\n \t\t\tdie(_(\"There is no merge to abort (MERGE_HEAD missing).\"));\n \n+\t\tif (file_exists(git_path_merge_autostash(the_repository))) {\n+\t\t\tif (rename(git_path_merge_autostash(the_repository),\n+\t\t\t\t\t\tgit_path_merge_autostash_saved()))\n+\t\t\t\tdie_errno(_(\"failed to rename autostash\"));\n+\t\t}\n+\n \t\t/* Invoke 'git reset --merge' */\n \t\tret = cmd_reset(nargc, nargv, prefix);\n-\t\tapply_autostash(git_path_merge_autostash(the_repository));\n+\n+\t\tapply_autostash(git_path_merge_autostash_saved());\n \t\tgoto done;\n \t}\n \ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex 038c8532eb..060470c455 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -439,9 +439,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n \t}\n \tif (!pathspec.nr) {\n-\t\tif (reset_type == HARD)\n-\t\t\tsave_autostash(git_path_merge_autostash(the_repository));\n-\n+\t\tsave_autostash(git_path_merge_autostash(the_repository));\n \t\tremove_branch_state(the_repository, 0);\n \t}\n \n> \n> I'm not sure about what the most intuitive behaviour is. I thought that\n> this implementation would be the best but I'm not entirely sure. I'd\n> appreciate some more discussion on this.\n> \n> Thanks,\n> \n> Denton\n> \n> > Best Wishes\n> > \n> > Phillip\n> > \n> > > +\t}\n> > >   \treturn update_ref_status;\n> > >   }\n"},{"id":"394660","messageId":"faa275d4-ca5d-9ab9-cbcb-aebd5e53e20b@gmail.com","threadId":"52048","inReplyTo":"20200403105639.GA3193506@generichostname","subject":"Re: [PATCH v3 17/19] merge: teach --autostash option","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-03T13:09:26Z","receivedAt":"2020-04-03T13:09:36Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 03/04/2020 11:56, Denton Liu wrote:\n> On Fri, Apr 03, 2020 at 06:31:26AM -0400, Denton Liu wrote:\n>>>> diff --git a/builtin/reset.c b/builtin/reset.c\n>>>> index 18228c312e..038c8532eb 100644\n>>>> --- a/builtin/reset.c\n>>>> +++ b/builtin/reset.c\n>>>> @@ -25,6 +25,7 @@\n>>>>    #include \"cache-tree.h\"\n>>>>    #include \"submodule.h\"\n>>>>    #include \"submodule-config.h\"\n>>>> +#include \"sequencer.h\"\n>>>>    #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n>>>> @@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n>>>>    \t\tif (reset_type == HARD && !update_ref_status && !quiet)\n>>>>    \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n>>>>    \t}\n>>>> -\tif (!pathspec.nr)\n>>>> +\tif (!pathspec.nr) {\n>>>> +\t\tif (reset_type == HARD)\n>>>> +\t\t\tsave_autostash(git_path_merge_autostash(the_repository));\n>>>> +\n>>>>    \t\tremove_branch_state(the_repository, 0);\n>>>\n>>> This removes the autostash file for all reset types but we only keep the\n>>> stash in the case of 'reset --hard' which is confusing.\n>>\n>> I was worried that this change would be controversial... The rationale\n>> behind this change was that with `reset --hard`, we want to leave a\n>> clean working tree behind so we save it into the stash reflog. In all\n>> other cases, remove_branch_state() will apply the saved stash entry\n>> which should be fine since users don't expect a clean worktree with the\n>> other reset types.\n>>\n>> I considered saving the autostash in all cases of reset but\n>> `git merge --abort` invokes `git reset --merge` behind the scenes so\n>> we'd have to consider that. Perhaps we can make all resets save the\n>> stash entry and, in the case of `merge --abort`, we can add some extra\n>> logic to subvert this so that the stash entry is applied?\n> \n> Perhaps something like this?\n> \n> -- >8 --\n> commit 14d0b569cb7675f00d32d3d7fad7564fcaeca458\n> Author: Denton Liu <liu.denton@gmail.com>\n> Date:   Fri Apr 3 06:50:34 2020 -0400\n> \n>      squash! merge: teach --autostash option\n>      \n>      Stash is saved when any reset is run, when git merge --abort is run,\n>      stash is applied.\n\nI think this is the easiest behavior to understand, it avoids changing \nthe behavior of reset, in particular it stops 'reset --mixed/--soft' \nfrom suddenly starting to touch the working tree.\n\n> diff --git a/builtin/merge.c b/builtin/merge.c\n> index 9573d77096..31b82d614c 100644\n> --- a/builtin/merge.c\n> +++ b/builtin/merge.c\n> @@ -1242,6 +1242,8 @@ static int merging_a_throwaway_tag(struct commit *commit)\n>   \treturn is_throwaway_tag;\n>   }\n>   \n> +static GIT_PATH_FUNC(git_path_merge_autostash_saved, \"MERGE_AUTOSTASH_SAVED\")\n> +\n>   int cmd_merge(int argc, const char **argv, const char *prefix)\n>   {\n>   \tstruct object_id result_tree, stash, head_oid;\n> @@ -1295,9 +1297,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>   \t\tif (!file_exists(git_path_merge_head(the_repository)))\n>   \t\t\tdie(_(\"There is no merge to abort (MERGE_HEAD missing).\"));\n>   \n> +\t\tif (file_exists(git_path_merge_autostash(the_repository))) {\n> +\t\t\tif (rename(git_path_merge_autostash(the_repository),\n> +\t\t\t\t\t\tgit_path_merge_autostash_saved()))\n> +\t\t\t\tdie_errno(_(\"failed to rename autostash\"));\n\nThis is a bit of a performance, can't we just remember the stash oid in \na variable and tweak the apply code?\n\n> +\t\t}\n> +\n>   \t\t/* Invoke 'git reset --merge' */\n>   \t\tret = cmd_reset(nargc, nargv, prefix);\n> -\t\tapply_autostash(git_path_merge_autostash(the_repository));\n> +\n> +\t\tapply_autostash(git_path_merge_autostash_saved());\n\nCalling cmd_reset() was already a bit dodgy by our normal rules, now \nwe're calling other functions after it though I guess given the current \nautostash implementation it's mostly in a separate process.\n\nI think this is a good direction to go in\nBTW what message gets printed when the stash is saved?\n\nBest Wishes\n\nPhillip\n\n>   \t\tgoto done;\n>   \t}\n>   \n> diff --git a/builtin/reset.c b/builtin/reset.c\n> index 038c8532eb..060470c455 100644\n> --- a/builtin/reset.c\n> +++ b/builtin/reset.c\n> @@ -439,9 +439,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n>   \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n>   \t}\n>   \tif (!pathspec.nr) {\n> -\t\tif (reset_type == HARD)\n> -\t\t\tsave_autostash(git_path_merge_autostash(the_repository));\n> -\n> +\t\tsave_autostash(git_path_merge_autostash(the_repository));\n>   \t\tremove_branch_state(the_repository, 0);\n>   \t}\n>   \n>>\n>> I'm not sure about what the most intuitive behaviour is. I thought that\n>> this implementation would be the best but I'm not entirely sure. I'd\n>> appreciate some more discussion on this.\n>>\n>> Thanks,\n>>\n>> Denton\n>>\n>>> Best Wishes\n>>>\n>>> Phillip\n>>>\n>>>> +\t}\n>>>>    \treturn update_ref_status;\n>>>>    }\n"},{"id":"394662","messageId":"194df5e2-4e5d-3150-d8a4-bb55bdef75be@gmail.com","threadId":"52048","inReplyTo":"20200403103126.GA3163329@generichostname","subject":"Re: [PATCH v3 17/19] merge: teach --autostash option","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-03T13:34:30Z","receivedAt":"2020-04-03T13:34:37Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 03/04/2020 11:31, Denton Liu wrote:\n> Hi Phillip,\n> \n> Thanks for your detailed review on the series. I've updated everything\n> according to your comments except for the parts below:\n\nThat's great, I've added a few more comments below\n\n> On Thu, Apr 02, 2020 at 04:24:54PM +0100, Phillip Wood wrote:\n>>> diff --git a/branch.c b/branch.c\n>>> index 579494738a..bf2536c70d 100644\n>>> --- a/branch.c\n>>> +++ b/branch.c\n>>> @@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)\n>>>    \tunlink(git_path_merge_rr(r));\n>>>    \tunlink(git_path_merge_msg(r));\n>>>    \tunlink(git_path_merge_mode(r));\n>>> +\tapply_autostash(git_path_merge_autostash(r));\n>>>    }\n>>>    void remove_branch_state(struct repository *r, int verbose)\n>>> diff --git a/builtin/commit.c b/builtin/commit.c\n>>> index 7ba33a3bec..c11894423a 100644\n>>> --- a/builtin/commit.c\n>>> +++ b/builtin/commit.c\n>>> @@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>>>    \tunlink(git_path_merge_mode(the_repository));\n>>>    \tunlink(git_path_squash_msg(the_repository));\n>>> +\tapply_autostash(git_path_merge_autostash(the_repository));\n>>> +\n>>\n>> It's hard to tell from the limited context but do we want to run\n>> commit_index_files() before applying the autostash?\n> \n> I don't think it really matters which order we run it in. When we run\n> apply_autostash(), we only ever touch the working tree, not the index so\n> it doesn't matter if it's run before or after. I'd prefer to keep it\n> here because if we ever refactor this to use\n> remove_merge_branch_state(), the person working on this will be able to\n> perform the refactor more easily without having to worry about implicit\n> ordering dependencies.\n\nThinking a bit more about this we definitely want to commit the index \nfiles before applying the stash as that is done in a separate process so \nwe want our index written to disk first.\n\n'git stash apply' does touch the index while it's merging and then tries \nto reset it to the pre-merge state if the merge has no conflicts. If the \nuser runs\n     git add new-file\n     git merge --autostash branch\nand new-file is not in branch then 'git stash apply' will add new-file \nto the index\n\n\n>> I wonder if this should\n>> be using remove_merge_branch_state() instead of duplicating it as well.\n> \n> We can _almost_ use remove_branch_state() even! Unfortunately,\n> remove_merge_branch_state() calls `unlink(git_path_merge_rr(r))` which\n> we cannot do since repo_rerere() relies on it later. Perhaps we can\n> can move repo_rerere() earlier?\n\nI think we should move apply_autostash() down so that repo_rerere() is \ncalled with the index that we've committed before we apply the stash \nwhich might add conflicts or new files. We should probably run the \npost-commit and post-rewrite hooks before we apply the autostash as well \nto avoid changing the existing behaviour.\n\nIf we aim for something like\n   commit_index_files()\n   repo_rerere()\n   stash_oid = read_autostash_oid()\n   cleanup_branch_state()\n   run_post_commit_hook()\n   run_post_rewrite_hook()\n   print_commit_summary()\n   apply_autostash(stash_oid)\n\nThen we keep the existing behaviour for rerere and the hooks, and the \ncommit summary is printed before all the status junk that \napply_autostash() spews out\n\nWe probably need to check the where 'git merge' applies the autostash as \nwell to make sure it is writing the index to disk, calling the \npost-merge hook and printing any messages before applying the stash\n\nBest Wishes\n\nPhillip\n\n\n>>> diff --git a/builtin/reset.c b/builtin/reset.c\n>>> index 18228c312e..038c8532eb 100644\n>>> --- a/builtin/reset.c\n>>> +++ b/builtin/reset.c\n>>> @@ -25,6 +25,7 @@\n>>>    #include \"cache-tree.h\"\n>>>    #include \"submodule.h\"\n>>>    #include \"submodule-config.h\"\n>>> +#include \"sequencer.h\"\n>>>    #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n>>> @@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n>>>    \t\tif (reset_type == HARD && !update_ref_status && !quiet)\n>>>    \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n>>>    \t}\n>>> -\tif (!pathspec.nr)\n>>> +\tif (!pathspec.nr) {\n>>> +\t\tif (reset_type == HARD)\n>>> +\t\t\tsave_autostash(git_path_merge_autostash(the_repository));\n>>> +\n>>>    \t\tremove_branch_state(the_repository, 0);\n>>\n>> This removes the autostash file for all reset types but we only keep the\n>> stash in the case of 'reset --hard' which is confusing.\n> \n> I was worried that this change would be controversial... The rationale\n> behind this change was that with `reset --hard`, we want to leave a\n> clean working tree behind so we save it into the stash reflog. In all\n> other cases, remove_branch_state() will apply the saved stash entry\n> which should be fine since users don't expect a clean worktree with the\n> other reset types.\n> \n> I considered saving the autostash in all cases of reset but\n> `git merge --abort` invokes `git reset --merge` behind the scenes so\n> we'd have to consider that. Perhaps we can make all resets save the\n> stash entry and, in the case of `merge --abort`, we can add some extra\n> logic to subvert this so that the stash entry is applied?\n> \n> I'm not sure about what the most intuitive behaviour is. I thought that\n> this implementation would be the best but I'm not entirely sure. I'd\n> appreciate some more discussion on this.\n> \n> Thanks,\n> \n> Denton\n> \n>> Best Wishes\n>>\n>> Phillip\n>>\n>>> +\t}\n>>>    \treturn update_ref_status;\n>>>    }\n"},{"id":"394717","messageId":"20200403211458.GA14409@generichostname","threadId":"52048","inReplyTo":"faa275d4-ca5d-9ab9-cbcb-aebd5e53e20b@gmail.com","subject":"Re: [PATCH v3 17/19] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-03T21:14:58Z","receivedAt":"2020-04-03T21:15:06Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Phillip,\n\nOn Fri, Apr 03, 2020 at 02:09:26PM +0100, Phillip Wood wrote:\n> Hi Denton\n> \n> On 03/04/2020 11:56, Denton Liu wrote:\n> > On Fri, Apr 03, 2020 at 06:31:26AM -0400, Denton Liu wrote:\n> > > > > diff --git a/builtin/reset.c b/builtin/reset.c\n> > > > > index 18228c312e..038c8532eb 100644\n> > > > > --- a/builtin/reset.c\n> > > > > +++ b/builtin/reset.c\n> > > > > @@ -25,6 +25,7 @@\n> > > > >    #include \"cache-tree.h\"\n> > > > >    #include \"submodule.h\"\n> > > > >    #include \"submodule-config.h\"\n> > > > > +#include \"sequencer.h\"\n> > > > >    #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n> > > > > @@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n> > > > >    \t\tif (reset_type == HARD && !update_ref_status && !quiet)\n> > > > >    \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n> > > > >    \t}\n> > > > > -\tif (!pathspec.nr)\n> > > > > +\tif (!pathspec.nr) {\n> > > > > +\t\tif (reset_type == HARD)\n> > > > > +\t\t\tsave_autostash(git_path_merge_autostash(the_repository));\n> > > > > +\n> > > > >    \t\tremove_branch_state(the_repository, 0);\n> > > > \n> > > > This removes the autostash file for all reset types but we only keep the\n> > > > stash in the case of 'reset --hard' which is confusing.\n> > > \n> > > I was worried that this change would be controversial... The rationale\n> > > behind this change was that with `reset --hard`, we want to leave a\n> > > clean working tree behind so we save it into the stash reflog. In all\n> > > other cases, remove_branch_state() will apply the saved stash entry\n> > > which should be fine since users don't expect a clean worktree with the\n> > > other reset types.\n> > > \n> > > I considered saving the autostash in all cases of reset but\n> > > `git merge --abort` invokes `git reset --merge` behind the scenes so\n> > > we'd have to consider that. Perhaps we can make all resets save the\n> > > stash entry and, in the case of `merge --abort`, we can add some extra\n> > > logic to subvert this so that the stash entry is applied?\n> > \n> > Perhaps something like this?\n> > \n> > -- >8 --\n> > commit 14d0b569cb7675f00d32d3d7fad7564fcaeca458\n> > Author: Denton Liu <liu.denton@gmail.com>\n> > Date:   Fri Apr 3 06:50:34 2020 -0400\n> > \n> >      squash! merge: teach --autostash option\n> >      Stash is saved when any reset is run, when git merge --abort is run,\n> >      stash is applied.\n> \n> I think this is the easiest behavior to understand, it avoids changing the\n> behavior of reset, in particular it stops 'reset --mixed/--soft' from\n> suddenly starting to touch the working tree.\n\nGreat, I'll use this approach then.\n\n> > diff --git a/builtin/merge.c b/builtin/merge.c\n> > index 9573d77096..31b82d614c 100644\n> > --- a/builtin/merge.c\n> > +++ b/builtin/merge.c\n> > @@ -1242,6 +1242,8 @@ static int merging_a_throwaway_tag(struct commit *commit)\n> >   \treturn is_throwaway_tag;\n> >   }\n> > +static GIT_PATH_FUNC(git_path_merge_autostash_saved, \"MERGE_AUTOSTASH_SAVED\")\n> > +\n> >   int cmd_merge(int argc, const char **argv, const char *prefix)\n> >   {\n> >   \tstruct object_id result_tree, stash, head_oid;\n> > @@ -1295,9 +1297,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n> >   \t\tif (!file_exists(git_path_merge_head(the_repository)))\n> >   \t\t\tdie(_(\"There is no merge to abort (MERGE_HEAD missing).\"));\n> > +\t\tif (file_exists(git_path_merge_autostash(the_repository))) {\n> > +\t\t\tif (rename(git_path_merge_autostash(the_repository),\n> > +\t\t\t\t\t\tgit_path_merge_autostash_saved()))\n> > +\t\t\t\tdie_errno(_(\"failed to rename autostash\"));\n> \n> This is a bit of a performance, can't we just remember the stash oid in a\n> variable and tweak the apply code?\n\nThis makes me a little bit sad because we basically have to duplicate\nthe file-handling portion of apply_save_autostash() but I agree that\ndoing your way should be better.\n\n> > +\t\t}\n> > +\n> >   \t\t/* Invoke 'git reset --merge' */\n> >   \t\tret = cmd_reset(nargc, nargv, prefix);\n> > -\t\tapply_autostash(git_path_merge_autostash(the_repository));\n> > +\n> > +\t\tapply_autostash(git_path_merge_autostash_saved());\n> \n> Calling cmd_reset() was already a bit dodgy by our normal rules, now we're\n> calling other functions after it though I guess given the current autostash\n> implementation it's mostly in a separate process.\n> \n> I think this is a good direction to go in\n> BTW what message gets printed when the stash is saved?\n\nThis is the message that is printed:\n\n\t$ git reset --hard\n\tHEAD is now at c4c4222 commit 1\n\tAutostash exists; creating a new stash entry.\n\tYour changes are safe in the stash.\n\tYou can run \"git stash pop\" or \"git stash drop\" at any time.\n\nThanks,\n\nDenton\n\n> Best Wishes\n> \n> Phillip\n> \n> >   \t\tgoto done;\n> >   \t}\n> > diff --git a/builtin/reset.c b/builtin/reset.c\n> > index 038c8532eb..060470c455 100644\n> > --- a/builtin/reset.c\n> > +++ b/builtin/reset.c\n> > @@ -439,9 +439,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n> >   \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n> >   \t}\n> >   \tif (!pathspec.nr) {\n> > -\t\tif (reset_type == HARD)\n> > -\t\t\tsave_autostash(git_path_merge_autostash(the_repository));\n> > -\n> > +\t\tsave_autostash(git_path_merge_autostash(the_repository));\n> >   \t\tremove_branch_state(the_repository, 0);\n> >   \t}\n> > > \n> > > I'm not sure about what the most intuitive behaviour is. I thought that\n> > > this implementation would be the best but I'm not entirely sure. I'd\n> > > appreciate some more discussion on this.\n> > > \n> > > Thanks,\n> > > \n> > > Denton\n> > > \n> > > > Best Wishes\n> > > > \n> > > > Phillip\n> > > > \n> > > > > +\t}\n> > > > >    \treturn update_ref_status;\n> > > > >    }\n"},{"id":"394725","messageId":"20200403222552.GA35842@generichostname","threadId":"52048","inReplyTo":"194df5e2-4e5d-3150-d8a4-bb55bdef75be@gmail.com","subject":"Re: [PATCH v3 17/19] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-03T22:25:52Z","receivedAt":"2020-04-03T22:25:59Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Phillip,\n\nOn Fri, Apr 03, 2020 at 02:34:30PM +0100, Phillip Wood wrote:\n> > On Thu, Apr 02, 2020 at 04:24:54PM +0100, Phillip Wood wrote:\n> > > > diff --git a/branch.c b/branch.c\n> > > > index 579494738a..bf2536c70d 100644\n> > > > --- a/branch.c\n> > > > +++ b/branch.c\n> > > > @@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)\n> > > >    \tunlink(git_path_merge_rr(r));\n> > > >    \tunlink(git_path_merge_msg(r));\n> > > >    \tunlink(git_path_merge_mode(r));\n> > > > +\tapply_autostash(git_path_merge_autostash(r));\n> > > >    }\n> > > >    void remove_branch_state(struct repository *r, int verbose)\n> > > > diff --git a/builtin/commit.c b/builtin/commit.c\n> > > > index 7ba33a3bec..c11894423a 100644\n> > > > --- a/builtin/commit.c\n> > > > +++ b/builtin/commit.c\n> > > > @@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n> > > >    \tunlink(git_path_merge_mode(the_repository));\n> > > >    \tunlink(git_path_squash_msg(the_repository));\n> > > > +\tapply_autostash(git_path_merge_autostash(the_repository));\n> > > > +\n> > > \n> > > It's hard to tell from the limited context but do we want to run\n> > > commit_index_files() before applying the autostash?\n> > \n> > I don't think it really matters which order we run it in. When we run\n> > apply_autostash(), we only ever touch the working tree, not the index so\n> > it doesn't matter if it's run before or after. I'd prefer to keep it\n> > here because if we ever refactor this to use\n> > remove_merge_branch_state(), the person working on this will be able to\n> > perform the refactor more easily without having to worry about implicit\n> > ordering dependencies.\n> \n> Thinking a bit more about this we definitely want to commit the index files\n> before applying the stash as that is done in a separate process so we want\n> our index written to disk first.\n> \n> 'git stash apply' does touch the index while it's merging and then tries to\n> reset it to the pre-merge state if the merge has no conflicts. If the user\n> runs\n>     git add new-file\n>     git merge --autostash branch\n> and new-file is not in branch then 'git stash apply' will add new-file to\n> the index\n> \n> \n> > > I wonder if this should\n> > > be using remove_merge_branch_state() instead of duplicating it as well.\n> > \n> > We can _almost_ use remove_branch_state() even! Unfortunately,\n> > remove_merge_branch_state() calls `unlink(git_path_merge_rr(r))` which\n> > we cannot do since repo_rerere() relies on it later. Perhaps we can\n> > can move repo_rerere() earlier?\n> \n> I think we should move apply_autostash() down so that repo_rerere() is\n> called with the index that we've committed before we apply the stash which\n> might add conflicts or new files. We should probably run the post-commit and\n> post-rewrite hooks before we apply the autostash as well to avoid changing\n> the existing behaviour.\n> \n> If we aim for something like\n>   commit_index_files()\n>   repo_rerere()\n>   stash_oid = read_autostash_oid()\n>   cleanup_branch_state()\n>   run_post_commit_hook()\n>   run_post_rewrite_hook()\n>   print_commit_summary()\n>   apply_autostash(stash_oid)\n> \n> Then we keep the existing behaviour for rerere and the hooks, and the commit\n> summary is printed before all the status junk that apply_autostash() spews\n> out\n\nMakes sense, I'll make this change.\n\n> We probably need to check the where 'git merge' applies the autostash as\n> well to make sure it is writing the index to disk, calling the post-merge\n> hook and printing any messages before applying the stash\n\nIt currently applies the autostash in finish(). After reviewing it, I've\nmoved the apply_autostash() call to the end of that function.\n\nActually, going back to review some more callsites of apply_autostash(),\nthe one in remove_merge_branch_state() doesn't really make sense. We\nshould really be calling save_autostash() in there so that we don't\naccidentally dirty the worktree unexpectedly. The bonus to this is that\nI've removed the save_autostash() call in `git reset` so it should just\nbe able to call remove_branch_state() and have everything just work.\n\nAs a result of these changes, we now only introduce three callsites of\napply_autostash(), which are all explicit:\n\n\t1. At the end of `git commit`\n\t2. At the end of finish() in `git merge`\n\t3. At the end of `git merge --abort`\n\nI'll send up a new version later today.\n\nThanks for helping shape this topic up,\n\nDenton\n\n> Best Wishes\n> \n> Phillip\n"},{"id":"394736","messageId":"059e0e8e43f633086fc297f8328288047b45d0e0.1585962672.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 02/23] t7600: use test_write_lines()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:15Z","receivedAt":"2020-04-04T01:12:04Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In t7600, we were rewriting `printf '%s\\n' ...` to create files from\nparameters, one per line. However, we already have a function that wraps\nthis for us: test_write_lines(). Rewrite these instances to use that\nfunction instead of open coding it.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7600-merge.sh | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 132608879a..4fa0ef8e3b 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -29,15 +29,15 @@ Testing basic merge operations/option parsing.\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-gpg.sh\n \n-printf '%s\\n' 1 2 3 4 5 6 7 8 9 >file\n-printf '%s\\n' '1 X' 2 3 4 5 6 7 8 9 >file.1\n-printf '%s\\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5\n-printf '%s\\n' 1 2 3 4 5 6 7 8 '9 X' >file.9\n-printf '%s\\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n-printf '%s\\n' '1 X' 2 3 4 5 6 7 8 9 >result.1\n-printf '%s\\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n-printf '%s\\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n-printf '%s\\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n+test_write_lines 1 2 3 4 5 6 7 8 9 >file\n+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1\n+test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5\n+test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9\n+test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1\n+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n+test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n \n create_merge_msgs () {\n \techo \"Merge tag 'c2'\" >msg.1-5 &&\n@@ -81,7 +81,7 @@ verify_head () {\n }\n \n verify_parents () {\n-\tprintf '%s\\n' \"$@\" >parents.expected &&\n+\ttest_write_lines \"$@\" >parents.expected &&\n \t>parents.actual &&\n \ti=1 &&\n \twhile test $i -le $#\n@@ -95,7 +95,7 @@ verify_parents () {\n }\n \n verify_mergeheads () {\n-\tprintf '%s\\n' \"$@\" >mergehead.expected &&\n+\ttest_write_lines \"$@\" >mergehead.expected &&\n \twhile read sha1 rest\n \tdo\n \t\tgit rev-parse $sha1\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394737","messageId":"76585e5b1367a3adf18d761b2af9356ee64b46fd.1585962672.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 03/23] sequencer: stop leaking buf","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:16Z","receivedAt":"2020-04-04T01:12:05Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In read_populate_opts(), we call read_oneliner() _after_ calling\nstrbuf_release(). This means that `buf` is leaked at the end of the\nfunction.\n\nAlways clean up the strbuf by going to `done_rebase_i` whether or not\nwe return an error.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 13 +++++++++----\n 1 file changed, 9 insertions(+), 4 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex e528225e78..faab0b13e8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2485,6 +2485,7 @@ static int read_populate_opts(struct replay_opts *opts)\n {\n \tif (is_rebase_i(opts)) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tint ret = 0;\n \n \t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {\n \t\t\tif (!starts_with(buf.buf, \"-S\"))\n@@ -2525,7 +2526,7 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t\topts->keep_redundant_commits = 1;\n \n \t\tread_strategy_opts(opts, &buf);\n-\t\tstrbuf_release(&buf);\n+\t\tstrbuf_reset(&buf);\n \n \t\tif (read_oneliner(&opts->current_fixups,\n \t\t\t\t  rebase_path_current_fixups(), 1)) {\n@@ -2538,12 +2539,16 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t}\n \n \t\tif (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {\n-\t\t\tif (get_oid_hex(buf.buf, &opts->squash_onto) < 0)\n-\t\t\t\treturn error(_(\"unusable squash-onto\"));\n+\t\t\tif (get_oid_hex(buf.buf, &opts->squash_onto) < 0) {\n+\t\t\t\tret = error(_(\"unusable squash-onto\"));\n+\t\t\t\tgoto done_rebase_i;\n+\t\t\t}\n \t\t\topts->have_squash_onto = 1;\n \t\t}\n \n-\t\treturn 0;\n+done_rebase_i:\n+\t\tstrbuf_release(&buf);\n+\t\treturn ret;\n \t}\n \n \tif (!file_exists(git_path_opts_file()))\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394738","messageId":"cover.1585962672.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1584782450.git.liu.denton@gmail.com","subject":"[PATCH v4 00/23] merge: learn --autostash","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:13Z","receivedAt":"2020-04-04T01:12:05Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Phillip,\n\nI implemented all of your comments. Patches 3-5, 11 need careful review as\nthey're new. 'merge: teach --autostash option' also probably needs some\nmore review as I've changed the callsites of apply_autostash() and\nsave_autostash().\n\n\nAlban reported[1] that he expected merge to have an --autostash option,\njust like rebase. Since there's not really any reason why merge can't\nhave it, let's implement it in this patchset.\n\nThe majority of this patchset is spent refactoring. This is so that the\nactual implementation in merge is done as simply as possible.\n\nChanges since v3:\n\n* Replace 'sequencer: use file strbuf for read_oneliner()' with patches 3-5\n\n* Rename flag to READ_ONELINER_WARN_MISSING\n\n* Add 'sequencer: rename stash_sha1 to stash_oid'\n\n* Many changes to 'merge: teach --autostash option'\n\nChanges since v2:\n\n* Change patch 1 to refer to ASCII order[2]\n\n* Make read_oneliner() accept a collective flags arg instead of two\n  separate args[3]\n\n* `git reset --hard` now leaves the worktree clean[4]\n\n* Rebased against 'master' since the base is quite old and a new release is\n  coming out soon\n\nChanges since v1:\n\n* Completely changed how the refactoring was done\n\n* More tests and documentation\n\n[1]: https://github.com/gitgitgadget/git/issues/394\n[2]: https://lore.kernel.org/git/xmqqwoadfq8n.fsf@gitster-ct.c.googlers.com/\n[3]: https://lore.kernel.org/git/xmqqwoaikehz.fsf@gitster-ct.c.googlers.com/\n[4]: https://lore.kernel.org/git/20200123042906.GA29009@generichostname/\n\nDenton Liu (23):\n  Makefile: ASCII-sort += lists\n  t7600: use test_write_lines()\n  sequencer: stop leaking buf\n  sequencer: reuse strbuf_trim() in read_oneliner()\n  sequencer: make file exists check more efficient\n  sequencer: make read_oneliner() accept flags\n  sequencer: configurably warn on non-existent files\n  sequencer: make read_oneliner() extern\n  rebase: use read_oneliner()\n  sequencer: make apply_rebase() accept a path\n  sequencer: rename stash_sha1 to stash_oid\n  rebase: use apply_autostash() from sequencer.c\n  rebase: generify reset_head()\n  reset: extract reset_head() from rebase\n  rebase: extract create_autostash()\n  rebase: generify create_autostash()\n  sequencer: extract perform_autostash() from rebase\n  sequencer: unlink autostash in apply_autostash()\n  sequencer: implement save_autostash()\n  sequencer: implement apply_autostash_oid()\n  merge: teach --autostash option\n  t5520: make test_pull_autostash() accept expect_parent_num\n  pull: pass --autostash to merge\n\n Documentation/config/merge.txt  |  10 +\n Documentation/git-merge.txt     |   6 +-\n Documentation/git-pull.txt      |   9 -\n Documentation/merge-options.txt |   8 +\n Makefile                        |  78 ++++----\n branch.c                        |   1 +\n builtin/commit.c                |   2 +\n builtin/merge.c                 |  27 +++\n builtin/pull.c                  |   9 +-\n builtin/rebase.c                | 312 +++++---------------------------\n builtin/reset.c                 |   4 +-\n parse-options.h                 |   1 +\n path.c                          |   1 +\n path.h                          |   4 +-\n reset.c                         | 140 ++++++++++++++\n reset.h                         |  20 ++\n sequencer.c                     | 187 +++++++++++++------\n sequencer.h                     |  20 ++\n t/t3033-merge-toplevel.sh       |  22 +++\n t/t5520-pull.sh                 |  57 ++++--\n t/t7600-merge.sh                | 140 ++++++++++++--\n 21 files changed, 653 insertions(+), 405 deletions(-)\n create mode 100644 reset.c\n create mode 100644 reset.h\n\nRange-diff against v3:\n 1:  67511a47b2 =  1:  c26771e934 Makefile: ASCII-sort += lists\n 2:  6d76cea5b4 =  2:  059e0e8e43 t7600: use test_write_lines()\n 3:  7c254b34b0 <  -:  ---------- sequencer: use file strbuf for read_oneliner()\n -:  ---------- >  3:  76585e5b13 sequencer: stop leaking buf\n -:  ---------- >  4:  c7a3cfa200 sequencer: reuse strbuf_trim() in read_oneliner()\n -:  ---------- >  5:  58da8e9555 sequencer: make file exists check more efficient\n 4:  dc88da2ebf !  6:  6ead240957 sequencer: make read_oneliner() accept flags\n    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n     +#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n     +\n      /*\n    -  * Reads a file that was presumably written by a shell script, i.e. with an\n    -  * end-of-line marker that needs to be stripped.\n    +  * Resets a strbuf then reads a file that was presumably written by a shell\n    +  * script, i.e. with an end-of-line marker that needs to be stripped.\n     @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,\n       * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n       */\n    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n     -\tconst char *path, int skip_if_empty)\n     +\tconst char *path, unsigned flags)\n      {\n    - \tint ret = 0;\n    - \tstruct strbuf file_buf = STRBUF_INIT;\n    + \tstrbuf_reset(buf);\n    + \tif (strbuf_read_file(buf, path, 0) < 0) {\n     @@ sequencer.c: static int read_oneliner(struct strbuf *buf,\n      \n    - \tstrbuf_trim_trailing_newline(&file_buf);\n    + \tstrbuf_trim(buf);\n      \n    --\tif (skip_if_empty && !file_buf.len)\n    -+\tif ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !file_buf.len)\n    - \t\tgoto done;\n    +-\tif (skip_if_empty && !buf->len)\n    ++\tif ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !buf->len)\n    + \t\treturn 0;\n      \n    - \tstrbuf_addbuf(buf, &file_buf);\n    + \treturn 1;\n     @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n    - \tif (is_rebase_i(opts)) {\n      \t\tstruct strbuf buf = STRBUF_INIT;\n    + \t\tint ret = 0;\n      \n     -\t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {\n     +\t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(),\n    @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n      \t\t\t\tstrbuf_reset(&buf);\n      \t\t\telse {\n     @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n    - \t\t\tstrbuf_reset(&buf);\n    + \t\t\t}\n      \t\t}\n      \n     -\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {\n    @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n      \t\t\t\topts->allow_rerere_auto = RERERE_AUTOUPDATE;\n      \t\t\telse if (!strcmp(buf.buf, \"--no-rerere-autoupdate\"))\n     @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n    - \t\tstrbuf_release(&buf);\n    + \t\tread_strategy_opts(opts, &buf);\n      \n      \t\tif (read_oneliner(&opts->current_fixups,\n     -\t\t\t\t  rebase_path_current_fixups(), 1)) {\n 5:  7702543d91 <  -:  ---------- sequencer: configurably warn on non-existent files\n -:  ---------- >  7:  2e7922b259 sequencer: configurably warn on non-existent files\n 6:  361bc2e885 !  8:  6c3c37994b sequencer: make read_oneliner() extern\n    @@ Commit message\n         Instead of hiding it as a static function within sequencer.c, extern it\n         so that it can be reused by others.\n     \n    +    This patch is best viewed with --color-moved.\n    +\n      ## sequencer.c ##\n     @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,\n      \treturn 0;\n      }\n      \n     -#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n    --#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)\n    +-#define READ_ONELINER_WARN_MISSING (1 << 1)\n     -\n     -/*\n    -- * Reads a file that was presumably written by a shell script, i.e. with an\n    -- * end-of-line marker that needs to be stripped.\n    +- * Resets a strbuf then reads a file that was presumably written by a shell\n    +- * script, i.e. with an end-of-line marker that needs to be stripped.\n     - *\n     - * Note that only the last end-of-line marker is stripped, consistent with the\n     - * behavior of \"$(cat path)\" in a shell script.\n    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n     +int read_oneliner(struct strbuf *buf,\n      \tconst char *path, unsigned flags)\n      {\n    - \tint ret = 0;\n    + \tstrbuf_reset(buf);\n     \n      ## sequencer.h ##\n     @@ sequencer.h: void print_commit_summary(struct repository *repo,\n    @@ sequencer.h: void print_commit_summary(struct repository *repo,\n      \t\t\t  unsigned int flags);\n      \n     +#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n    -+#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)\n    ++#define READ_ONELINER_WARN_MISSING (1 << 1)\n     +\n     +/*\n    -+ * Reads a file that was presumably written by a shell script, i.e. with an\n    -+ * end-of-line marker that needs to be stripped.\n    ++ * Resets a strbuf then reads a file that was presumably written by a shell\n    ++ * script, i.e. with an end-of-line marker that needs to be stripped.\n     + *\n     + * Note that only the last end-of-line marker is stripped, consistent with the\n     + * behavior of \"$(cat path)\" in a shell script.\n     + *\n    -+ * Returns 1 if the file was read, 0 if it could not be read.\n    ++ * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n     + */\n     +int read_oneliner(struct strbuf *buf,\n     +\tconst char *path, unsigned flags);\n 7:  47816abff1 !  9:  689f34a2a5 rebase: use read_oneliner()\n    @@ Commit message\n     \n         Since in sequencer.c, read_one() basically duplicates the functionality\n         of read_oneliner(), reduce code duplication by replacing read_one() with\n    -    read_oneliner().\n    +    read_oneliner(). Also, delete strbuf_reset() calls prior to\n    +    read_oneliner() as read_oneliner() already resets the strbuf.\n     \n    -    This was done with the following Coccinelle script\n    +    This was mostly done with the following Coccinelle script\n     \n                 @@\n                 expression a, b;\n    @@ Commit message\n                 - read_one(a, b)\n                 + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)\n     \n    -    and long lines were manually broken up.\n    +    Long lines were broken up and strbuf_reset()s were deleted manually.\n     \n      ## builtin/rebase.c ##\n     @@ builtin/rebase.c: static const char *state_dir_path(const char *filename, struct rebase_options *o\n    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n     -\tif (read_one(state_dir_path(\"head-name\", opts), &head_name) ||\n     -\t    read_one(state_dir_path(\"onto\", opts), &buf))\n     +\tif (!read_oneliner(&head_name, state_dir_path(\"head-name\", opts),\n    -+\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE) ||\n    ++\t\t\t   READ_ONELINER_WARN_MISSING) ||\n     +\t    !read_oneliner(&buf, state_dir_path(\"onto\", opts),\n    -+\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n    ++\t\t\t   READ_ONELINER_WARN_MISSING))\n      \t\treturn -1;\n      \topts->head_name = starts_with(head_name.buf, \"refs/\") ?\n      \t\txstrdup(head_name.buf) : NULL;\n     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n    + \t * head. Fall back to reading from head to cover for the case that the\n    + \t * user upgraded git with an ongoing interactive rebase.\n      \t */\n    - \tstrbuf_reset(&buf);\n    +-\tstrbuf_reset(&buf);\n      \tif (file_exists(state_dir_path(\"orig-head\", opts))) {\n     -\t\tif (read_one(state_dir_path(\"orig-head\", opts), &buf))\n     +\t\tif (!read_oneliner(&buf, state_dir_path(\"orig-head\", opts),\n    -+\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n    ++\t\t\t\t   READ_ONELINER_WARN_MISSING))\n      \t\t\treturn -1;\n     -\t} else if (read_one(state_dir_path(\"head\", opts), &buf))\n     +\t} else if (!read_oneliner(&buf, state_dir_path(\"head\", opts),\n    -+\t\t\t\t  READ_ONELINER_WARN_NON_EXISTENCE))\n    ++\t\t\t\t  READ_ONELINER_WARN_MISSING))\n      \t\treturn -1;\n      \tif (get_oid(buf.buf, &opts->orig_head))\n      \t\treturn error(_(\"invalid orig-head: '%s'\"), buf.buf);\n     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n    + \t}\n      \n      \tif (file_exists(state_dir_path(\"allow_rerere_autoupdate\", opts))) {\n    - \t\tstrbuf_reset(&buf);\n    +-\t\tstrbuf_reset(&buf);\n     -\t\tif (read_one(state_dir_path(\"allow_rerere_autoupdate\", opts),\n     -\t\t\t    &buf))\n     +\t\tif (!read_oneliner(&buf, state_dir_path(\"allow_rerere_autoupdate\", opts),\n    -+\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n    ++\t\t\t\t   READ_ONELINER_WARN_MISSING))\n      \t\t\treturn -1;\n      \t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n      \t\t\topts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;\n     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n    + \t}\n      \n      \tif (file_exists(state_dir_path(\"gpg_sign_opt\", opts))) {\n    - \t\tstrbuf_reset(&buf);\n    +-\t\tstrbuf_reset(&buf);\n     -\t\tif (read_one(state_dir_path(\"gpg_sign_opt\", opts),\n     -\t\t\t    &buf))\n     +\t\tif (!read_oneliner(&buf, state_dir_path(\"gpg_sign_opt\", opts),\n    -+\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n    ++\t\t\t\t   READ_ONELINER_WARN_MISSING))\n      \t\t\treturn -1;\n      \t\tfree(opts->gpg_sign_opt);\n      \t\topts->gpg_sign_opt = xstrdup(buf.buf);\n    -@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n    + \t}\n      \n      \tif (file_exists(state_dir_path(\"strategy\", opts))) {\n    - \t\tstrbuf_reset(&buf);\n    +-\t\tstrbuf_reset(&buf);\n     -\t\tif (read_one(state_dir_path(\"strategy\", opts), &buf))\n     +\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy\", opts),\n    -+\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n    ++\t\t\t\t   READ_ONELINER_WARN_MISSING))\n      \t\t\treturn -1;\n      \t\tfree(opts->strategy);\n      \t\topts->strategy = xstrdup(buf.buf);\n    -@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n    + \t}\n      \n      \tif (file_exists(state_dir_path(\"strategy_opts\", opts))) {\n    - \t\tstrbuf_reset(&buf);\n    +-\t\tstrbuf_reset(&buf);\n     -\t\tif (read_one(state_dir_path(\"strategy_opts\", opts), &buf))\n     +\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy_opts\", opts),\n    -+\t\t\t\t   READ_ONELINER_WARN_NON_EXISTENCE))\n    ++\t\t\t\t   READ_ONELINER_WARN_MISSING))\n      \t\t\treturn -1;\n      \t\tfree(opts->strategy_opts);\n      \t\topts->strategy_opts = xstrdup(buf.buf);\n    @@ builtin/rebase.c: static int apply_autostash(struct rebase_options *opts)\n      \t\treturn 0;\n      \n     -\tif (read_one(path, &autostash))\n    -+\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))\n    ++\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))\n      \t\treturn error(_(\"Could not read '%s'\"), path);\n      \t/* Ensure that the hash is not mistaken for a number */\n      \tstrbuf_addstr(&autostash, \"^0\");\n 8:  7e9a86c337 = 10:  e0b8e409af sequencer: make apply_rebase() accept a path\n -:  ---------- > 11:  a52583beff sequencer: rename stash_sha1 to stash_oid\n 9:  76a486d8b0 ! 12:  389b17df33 rebase: use apply_autostash() from sequencer.c\n    @@ Commit message\n     \n         The apply_autostash() function in builtin/rebase.c is similar enough to\n         the apply_autostash() function in sequencer.c that they are almost\n    -    interchangeable. Make the sequencer.c version extern and use it in\n    -    rebase.\n    +    interchangeable, except for the type of arg they accept. Make the\n    +    sequencer.c version extern and use it in rebase.\n     \n         The rebase version was introduced in 6defce2b02 (builtin rebase: support\n         `--autostash` option, 2018-09-04) as part of the shell to C conversion.\n    @@ builtin/rebase.c: static int rebase_write_basic_state(struct rebase_options *opt\n     -\tif (!file_exists(path))\n     -\t\treturn 0;\n     -\n    --\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))\n    +-\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))\n     -\t\treturn error(_(\"Could not read '%s'\"), path);\n     -\t/* Ensure that the hash is not mistaken for a number */\n     -\tstrbuf_addstr(&autostash, \"^0\");\n    @@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,\n     -static int apply_autostash(const char *path)\n     +int apply_autostash(const char *path)\n      {\n    - \tstruct strbuf stash_sha1 = STRBUF_INIT;\n    + \tstruct strbuf stash_oid = STRBUF_INIT;\n      \tstruct child_process child = CHILD_PROCESS_INIT;\n     \n      ## sequencer.h ##\n10:  6d37e8049b = 13:  fe57e21dbc rebase: generify reset_head()\n11:  18e1900a16 = 14:  045668620d reset: extract reset_head() from rebase\n12:  21e228920e = 15:  d5f51cd80e rebase: extract create_autostash()\n13:  7e2eab5203 = 16:  9ab10d23d4 rebase: generify create_autostash()\n14:  fddf506115 ! 17:  26cca49be6 sequencer: extract perform_autostash() from rebase\n    @@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,\n     +\n      int apply_autostash(const char *path)\n      {\n    - \tstruct strbuf stash_sha1 = STRBUF_INIT;\n    + \tstruct strbuf stash_oid = STRBUF_INIT;\n     \n      ## sequencer.h ##\n     @@ sequencer.h: void commit_post_rewrite(struct repository *r,\n15:  9394c7ae30 ! 18:  e703022fda sequencer: unlink autostash in apply_autostash()\n    @@ sequencer.c: int apply_autostash(const char *path)\n      \t}\n      \n     +\tunlink(path);\n    - \tstrbuf_release(&stash_sha1);\n    + \tstrbuf_release(&stash_oid);\n      \treturn ret;\n      }\n16:  0035b84388 ! 19:  75dc3f10a1 sequencer: implement save_autostash()\n    @@ Commit message\n         apply_save_autostash() and use it to implement save_autostash(). This\n         function will be used in a future commit.\n     \n    +    The difference between save_autostash() and apply_autostash() is that\n    +    the latter does not try to apply the stash. It skips that step and\n    +    just stores the created entry in the stash reflog.\n    +\n      ## sequencer.c ##\n     @@ sequencer.c: void create_autostash(struct repository *r, const char *path,\n      \tstrbuf_release(&buf);\n    @@ sequencer.c: void create_autostash(struct repository *r, const char *path,\n     -int apply_autostash(const char *path)\n     +static int apply_save_autostash(const char *path, int attempt_apply)\n      {\n    - \tstruct strbuf stash_sha1 = STRBUF_INIT;\n    + \tstruct strbuf stash_oid = STRBUF_INIT;\n      \tstruct child_process child = CHILD_PROCESS_INIT;\n     @@ sequencer.c: int apply_autostash(const char *path)\n      \t}\n    - \tstrbuf_trim(&stash_sha1);\n    + \tstrbuf_trim(&stash_oid);\n      \n     -\tchild.git_cmd = 1;\n     -\tchild.no_stdout = 1;\n     -\tchild.no_stderr = 1;\n     -\targv_array_push(&child.args, \"stash\");\n     -\targv_array_push(&child.args, \"apply\");\n    --\targv_array_push(&child.args, stash_sha1.buf);\n    +-\targv_array_push(&child.args, stash_oid.buf);\n     -\tif (!run_command(&child))\n     +\tif (attempt_apply) {\n     +\t\tchild.git_cmd = 1;\n    @@ sequencer.c: int apply_autostash(const char *path)\n     +\t\tchild.no_stderr = 1;\n     +\t\targv_array_push(&child.args, \"stash\");\n     +\t\targv_array_push(&child.args, \"apply\");\n    -+\t\targv_array_push(&child.args, stash_sha1.buf);\n    ++\t\targv_array_push(&child.args, stash_oid.buf);\n     +\t\tret = run_command(&child);\n     +\t}\n     +\n    @@ sequencer.c: int apply_autostash(const char *path)\n      \telse {\n      \t\tstruct child_process store = CHILD_PROCESS_INIT;\n     @@ sequencer.c: int apply_autostash(const char *path)\n    - \t\t\tret = error(_(\"cannot store %s\"), stash_sha1.buf);\n    + \t\t\tret = error(_(\"cannot store %s\"), stash_oid.buf);\n      \t\telse\n      \t\t\tfprintf(stderr,\n     -\t\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n -:  ---------- > 20:  598ddea6c1 sequencer: implement apply_autostash_oid()\n17:  085f8e0dd2 ! 21:  7adf794192 merge: teach --autostash option\n    @@ Commit message\n                 git fetch ...\n                 git merge --autostash FETCH_HEAD\n     \n    -    When `git reset --hard` is run to abort a merge, the working tree will\n    -    be left in a clean state, as expected, with the autostash pushed onto\n    -    the stash stack.\n    +    When an autostash is generated, it is automatically reapplied to the\n    +    worktree only in three explicit situations:\n    +\n    +            1. An incomplete merge is commit using `git commit`.\n    +            2. A merge completes successfully.\n    +            3. A merge is aborted using `git merge --abort`.\n    +\n    +    In all other situations where the merge state is removed using\n    +    remove_merge_branch_state() such as aborting a merge via\n    +    `git reset --hard`, the autostash is saved into the stash reflog\n    +    instead keeping the worktree clean.\n     \n         Suggested-by: Alban Gruin <alban.gruin@gmail.com>\n     \n    @@ Documentation/config/merge.txt: merge.stat::\n     +merge.autoStash::\n     +\tWhen set to true, automatically create a temporary stash entry\n     +\tbefore the operation begins, and apply it after the operation\n    -+\tends.  This means that you can run rebase on a dirty worktree.\n    ++\tends.  This means that you can run merge on a dirty worktree.\n     +\tHowever, use with care: the final stash application after a\n    -+\tsuccessful rebase might result in non-trivial conflicts.\n    ++\tsuccessful merge might result in non-trivial conflicts.\n     +\tThis option can be overridden by the `--no-autostash` and\n     +\t`--autostash` options of linkgit:git-merge[1].\n     +\tDefaults to false.\n    @@ Documentation/config/merge.txt: merge.stat::\n      \tControls which merge tool is used by linkgit:git-mergetool[1].\n      \tThe list below shows the valid built-in values.\n     \n    + ## Documentation/git-merge.txt ##\n    +@@ Documentation/git-merge.txt: will be appended to the specified message.\n    + \n    + --abort::\n    + \tAbort the current conflict resolution process, and\n    +-\ttry to reconstruct the pre-merge state.\n    ++\ttry to reconstruct the pre-merge state. If an autostash entry is\n    ++\tpresent, apply it back to the worktree.\n    + +\n    + If there were uncommitted worktree changes present when the merge\n    + started, 'git merge --abort' will in some cases be unable to\n    +@@ Documentation/git-merge.txt: reconstruct these changes. It is therefore recommended to always\n    + commit or stash your changes before running 'git merge'.\n    + +\n    + 'git merge --abort' is equivalent to 'git reset --merge' when\n    +-`MERGE_HEAD` is present.\n    ++`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present where\n    ++the stash entry is applied to the worktree.\n    + \n    + --quit::\n    + \tForget about the current merge in progress. Leave the index\n    +\n      ## Documentation/merge-options.txt ##\n     @@ Documentation/merge-options.txt: ifndef::git-pull[]\n      \tNote that not all merge strategies may support progress\n    @@ Documentation/merge-options.txt: ifndef::git-pull[]\n     +--no-autostash::\n     +\tAutomatically create a temporary stash entry before the operation\n     +\tbegins, and apply it after the operation ends.  This means\n    -+\tthat you can run rebase on a dirty worktree.  However, use\n    ++\tthat you can run the operation on a dirty worktree.  However, use\n     +\twith care: the final stash application after a successful\n    -+\trebase might result in non-trivial conflicts.\n    ++\tmerge might result in non-trivial conflicts.\n     +\n      endif::git-pull[]\n      \n    @@ branch.c: void remove_merge_branch_state(struct repository *r)\n      \tunlink(git_path_merge_rr(r));\n      \tunlink(git_path_merge_msg(r));\n      \tunlink(git_path_merge_mode(r));\n    -+\tapply_autostash(git_path_merge_autostash(r));\n    ++\tsave_autostash(git_path_merge_autostash(r));\n      }\n      \n      void remove_branch_state(struct repository *r, int verbose)\n     \n      ## builtin/commit.c ##\n     @@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix)\n    - \tunlink(git_path_merge_mode(the_repository));\n    - \tunlink(git_path_squash_msg(the_repository));\n    + \t\t\t\t     &oid, flags);\n    + \t}\n      \n     +\tapply_autostash(git_path_merge_autostash(the_repository));\n     +\n    - \tif (commit_index_files())\n    - \t\tdie(_(\"repository has been updated, but unable to write\\n\"\n    - \t\t      \"new_index file. Check that disk is not full and quota is\\n\"\n    + \tUNLEAK(err);\n    + \tUNLEAK(sb);\n    + \treturn 0;\n     \n      ## builtin/merge.c ##\n     @@ builtin/merge.c: static int show_progress = -1;\n    @@ builtin/merge.c: static struct option builtin_merge_options[] = {\n      \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n      \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n      \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n    -+\tOPT_BOOL(0, \"autostash\", &autostash,\n    -+\t      N_(\"automatically stash/stash pop before and after\")),\n    ++\tOPT_AUTOSTASH(&autostash),\n      \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n      \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n      \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n     @@ builtin/merge.c: static void finish(struct commit *head_commit,\n    - \t\tstrbuf_addf(&reflog_message, \"%s: %s\",\n    - \t\t\tgetenv(\"GIT_REFLOG_ACTION\"), msg);\n    - \t}\n    + \t/* Run a post-merge hook */\n    + \trun_hook_le(NULL, \"post-merge\", squash ? \"1\" : \"0\", NULL);\n    + \n     +\tapply_autostash(git_path_merge_autostash(the_repository));\n    - \tif (squash) {\n    - \t\tsquash_message(head_commit, remoteheads);\n    - \t} else {\n    + \tstrbuf_release(&reflog_message);\n    + }\n    + \n     @@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void *cb)\n      \t\treturn 0;\n      \t} else if (!strcmp(k, \"gpg.mintrustlevel\")) {\n    @@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void\n      \n      \tstatus = fmt_merge_msg_config(k, v, cb);\n     @@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)\n    + \tif (abort_current_merge) {\n    + \t\tint nargc = 2;\n    + \t\tconst char *nargv[] = {\"reset\", \"--merge\", NULL};\n    ++\t\tstruct strbuf stash_oid = STRBUF_INIT;\n      \n    + \t\tif (orig_argc != 2)\n    + \t\t\tusage_msg_opt(_(\"--abort expects no arguments\"),\n    +@@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)\n    + \t\tif (!file_exists(git_path_merge_head(the_repository)))\n    + \t\t\tdie(_(\"There is no merge to abort (MERGE_HEAD missing).\"));\n    + \n    ++\t\tif (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),\n    ++\t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n    ++\t\t\tstrbuf_trim(&stash_oid);\n    ++\t\t\tunlink(git_path_merge_autostash(the_repository));\n    ++\t\t}\n    ++\n      \t\t/* Invoke 'git reset --merge' */\n      \t\tret = cmd_reset(nargc, nargv, prefix);\n    -+\t\tapply_autostash(git_path_merge_autostash(the_repository));\n    ++\n    ++\t\tif (stash_oid.len)\n    ++\t\t\tapply_autostash_oid(stash_oid.buf);\n    ++\n    ++\t\tstrbuf_release(&stash_oid);\n      \t\tgoto done;\n      \t}\n      \n    @@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)\n      \tgit_committer_info(IDENT_STRICT);\n      \n     \n    + ## builtin/rebase.c ##\n    +@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)\n    + \t\t{ OPTION_STRING, 'S', \"gpg-sign\", &gpg_sign, N_(\"key-id\"),\n    + \t\t\tN_(\"GPG-sign commits\"),\n    + \t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n    +-\t\tOPT_BOOL(0, \"autostash\", &options.autostash,\n    +-\t\t\t N_(\"automatically stash/stash pop before and after\")),\n    ++\t\tOPT_AUTOSTASH(&options.autostash),\n    + \t\tOPT_STRING_LIST('x', \"exec\", &exec, N_(\"exec\"),\n    + \t\t\t\tN_(\"add exec lines after each commit of the \"\n    + \t\t\t\t   \"editable list\")),\n    +\n      ## builtin/reset.c ##\n     @@\n      #include \"cache-tree.h\"\n    @@ builtin/reset.c: int cmd_reset(int argc, const char **argv, const char *prefix)\n      \t}\n     -\tif (!pathspec.nr)\n     +\tif (!pathspec.nr) {\n    -+\t\tif (reset_type == HARD)\n    -+\t\t\tsave_autostash(git_path_merge_autostash(the_repository));\n    -+\n      \t\tremove_branch_state(the_repository, 0);\n     +\t}\n      \n      \treturn update_ref_status;\n      }\n     \n    + ## parse-options.h ##\n    +@@ parse-options.h: int parse_opt_passthru_argv(const struct option *, const char *, int);\n    + #define OPT_CLEANUP(v) OPT_STRING(0, \"cleanup\", v, N_(\"mode\"), N_(\"how to strip spaces and #comments from message\"))\n    + #define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, \"pathspec-from-file\", v, N_(\"read pathspec from file\"))\n    + #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\"))\n    ++#define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n    + \n    + #endif\n    +\n      ## path.c ##\n     @@ path.c: REPO_GIT_PATH_FUNC(merge_msg, \"MERGE_MSG\")\n      REPO_GIT_PATH_FUNC(merge_rr, \"MERGE_RR\")\n    @@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '\n     +\ttest_cmp file.1 file\n     +'\n     +\n    -+test_expect_success 'completed merge with --no-commit and --autostash' '\n    ++test_expect_success 'completed merge (git commit) with --no-commit and --autostash' '\n     +\tgit reset --hard c1 &&\n     +\tgit merge-file file file.orig file.9 &&\n     +\tgit diff >expect &&\n    @@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '\n     +\ttest_cmp result.1-5-9 file\n     +'\n     +\n    ++test_expect_success 'completed merge (git merge --continue) with --no-commit and --autostash' '\n    ++\tgit reset --hard c1 &&\n    ++\tgit merge-file file file.orig file.9 &&\n    ++\tgit diff >expect &&\n    ++\tgit merge --no-commit --autostash c2 &&\n    ++\tgit stash show -p MERGE_AUTOSTASH >actual &&\n    ++\ttest_cmp expect actual &&\n    ++\tgit merge --continue 2>err &&\n    ++\ttest_i18ngrep \"Applied autostash.\" err &&\n    ++\tgit show HEAD:file >merge-result &&\n    ++\ttest_cmp result.1-5 merge-result &&\n    ++\ttest_cmp result.1-5-9 file\n    ++'\n    ++\n     +test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '\n     +\tgit reset --hard c1 &&\n     +\tgit merge-file file file.orig file.9 &&\n18:  4dabe214f2 = 22:  f1e54622fb t5520: make test_pull_autostash() accept expect_parent_num\n19:  9b6f16f1bf ! 23:  177c7e537b pull: pass --autostash to merge\n    @@ Documentation/merge-options.txt: ifndef::git-pull[]\n      \tAutomatically create a temporary stash entry before the operation\n     @@ Documentation/merge-options.txt: ifndef::git-pull[]\n      \twith care: the final stash application after a successful\n    - \trebase might result in non-trivial conflicts.\n    + \tmerge might result in non-trivial conflicts.\n      \n     -endif::git-pull[]\n     -\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394739","messageId":"58da8e95558d5d345030db51200962dde71522f1.1585962672.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 05/23] sequencer: make file exists check more efficient","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:18Z","receivedAt":"2020-04-04T01:12:09Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"We currently check whether a file exists and return early before reading\nthe file. Instead of accessing the file twice, always read the file and\ncheck `errno` to see if the file doesn't exist.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 7 ++-----\n 1 file changed, 2 insertions(+), 5 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 09ca68f540..6e63224c41 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -431,13 +431,10 @@ static int write_message(const void *buf, size_t len, const char *filename,\n static int read_oneliner(struct strbuf *buf,\n \tconst char *path, int skip_if_empty)\n {\n-\n-\tif (!file_exists(path))\n-\t\treturn 0;\n-\n \tstrbuf_reset(buf);\n \tif (strbuf_read_file(buf, path, 0) < 0) {\n-\t\twarning_errno(_(\"could not read '%s'\"), path);\n+\t\tif (errno != ENOENT && errno != ENOTDIR)\n+\t\t\twarning_errno(_(\"could not read '%s'\"), path);\n \t\treturn 0;\n \t}\n \n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394740","messageId":"c7a3cfa20005aeeedc27d2eb4af1e2c4470ad73d.1585962672.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:17Z","receivedAt":"2020-04-04T01:12:10Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the original read_oneliner() logic, we duplicated the logic for\nstrbuf_trim_trailing_newline() with one exception: instead of preventing\nbuffer accesses below index 0, it would prevent buffer accesses below\nindex `orig_len`. Although this is correct, it isn't worth having the\nduplicated logic around.\n\nReset `buf` before using it then replace the trimming logic with\nstrbuf_trim().\n\nAs a cleanup, remove all reset_strbuf()s that happen before\nread_oneliner() is called.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 18 +++++-------------\n 1 file changed, 5 insertions(+), 13 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex faab0b13e8..09ca68f540 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -420,8 +420,8 @@ static int write_message(const void *buf, size_t len, const char *filename,\n }\n \n /*\n- * Reads a file that was presumably written by a shell script, i.e. with an\n- * end-of-line marker that needs to be stripped.\n+ * Resets a strbuf then reads a file that was presumably written by a shell\n+ * script, i.e. with an end-of-line marker that needs to be stripped.\n  *\n  * Note that only the last end-of-line marker is stripped, consistent with the\n  * behavior of \"$(cat path)\" in a shell script.\n@@ -431,23 +431,19 @@ static int write_message(const void *buf, size_t len, const char *filename,\n static int read_oneliner(struct strbuf *buf,\n \tconst char *path, int skip_if_empty)\n {\n-\tint orig_len = buf->len;\n \n \tif (!file_exists(path))\n \t\treturn 0;\n \n+\tstrbuf_reset(buf);\n \tif (strbuf_read_file(buf, path, 0) < 0) {\n \t\twarning_errno(_(\"could not read '%s'\"), path);\n \t\treturn 0;\n \t}\n \n-\tif (buf->len > orig_len && buf->buf[buf->len - 1] == '\\n') {\n-\t\tif (--buf->len > orig_len && buf->buf[buf->len - 1] == '\\r')\n-\t\t\t--buf->len;\n-\t\tbuf->buf[buf->len] = '\\0';\n-\t}\n+\tstrbuf_trim(buf);\n \n-\tif (skip_if_empty && buf->len == orig_len)\n+\tif (skip_if_empty && !buf->len)\n \t\treturn 0;\n \n \treturn 1;\n@@ -2471,7 +2467,6 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)\n \n static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)\n {\n-\tstrbuf_reset(buf);\n \tif (!read_oneliner(buf, rebase_path_strategy(), 0))\n \t\treturn;\n \topts->strategy = strbuf_detach(buf, NULL);\n@@ -2494,7 +2489,6 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t\t\tfree(opts->gpg_sign);\n \t\t\t\topts->gpg_sign = xstrdup(buf.buf + 2);\n \t\t\t}\n-\t\t\tstrbuf_reset(&buf);\n \t\t}\n \n \t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {\n@@ -2526,7 +2520,6 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t\topts->keep_redundant_commits = 1;\n \n \t\tread_strategy_opts(opts, &buf);\n-\t\tstrbuf_reset(&buf);\n \n \t\tif (read_oneliner(&opts->current_fixups,\n \t\t\t\t  rebase_path_current_fixups(), 1)) {\n@@ -4006,7 +3999,6 @@ static int pick_commits(struct repository *r,\n \t\t\t\tres = error(_(\"could not read orig-head\"));\n \t\t\t\tgoto cleanup_head_ref;\n \t\t\t}\n-\t\t\tstrbuf_reset(&buf);\n \t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0)) {\n \t\t\t\tres = error(_(\"could not read 'onto'\"));\n \t\t\t\tgoto cleanup_head_ref;\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394741","messageId":"2e7922b259851091b9014c761d340ff1a73a04e4.1585962672.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 07/23] sequencer: configurably warn on non-existent files","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:20Z","receivedAt":"2020-04-04T01:12:10Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we plan on externing read_oneliner(). Future users of\nread_oneliner() will want the ability to output warnings in the event\nthat the `path` doesn't exist. Introduce the\n`READ_ONELINER_WARN_MISSING` flag which, if active, would issue a\nwarning when a file doesn't exist by skipping the `!file_exists()` check\nand letting `strbuf_read_file()` handle that case.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 6c26d61670..b771e8f4ca 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename,\n }\n \n #define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n+#define READ_ONELINER_WARN_MISSING (1 << 1)\n \n /*\n  * Resets a strbuf then reads a file that was presumably written by a shell\n@@ -435,7 +436,8 @@ static int read_oneliner(struct strbuf *buf,\n {\n \tstrbuf_reset(buf);\n \tif (strbuf_read_file(buf, path, 0) < 0) {\n-\t\tif (errno != ENOENT && errno != ENOTDIR)\n+\t\tif ((flags & READ_ONELINER_WARN_MISSING) ||\n+\t\t\t\t(errno != ENOENT && errno != ENOTDIR))\n \t\t\twarning_errno(_(\"could not read '%s'\"), path);\n \t\treturn 0;\n \t}\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394742","messageId":"689f34a2a56242d20f8692281847a5ff7aed36e3.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 09/23] rebase: use read_oneliner()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:22Z","receivedAt":"2020-04-04T01:12:12Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Since in sequencer.c, read_one() basically duplicates the functionality\nof read_oneliner(), reduce code duplication by replacing read_one() with\nread_oneliner(). Also, delete strbuf_reset() calls prior to\nread_oneliner() as read_oneliner() already resets the strbuf.\n\nThis was mostly done with the following Coccinelle script\n\n\t@@\n\texpression a, b;\n\t@@\n\t- read_one(a, b)\n\t+ !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)\n\nLong lines were broken up and strbuf_reset()s were deleted manually.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 42 +++++++++++++++++-------------------------\n 1 file changed, 17 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex bff53d5d16..91873fa0b5 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -586,15 +586,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o\n \treturn path.buf;\n }\n \n-/* Read one file, then strip line endings */\n-static int read_one(const char *path, struct strbuf *buf)\n-{\n-\tif (strbuf_read_file(buf, path, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'\"), path);\n-\tstrbuf_trim_trailing_newline(buf);\n-\treturn 0;\n-}\n-\n /* Initialize the rebase options from the state directory. */\n static int read_basic_state(struct rebase_options *opts)\n {\n@@ -602,8 +593,10 @@ static int read_basic_state(struct rebase_options *opts)\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct object_id oid;\n \n-\tif (read_one(state_dir_path(\"head-name\", opts), &head_name) ||\n-\t    read_one(state_dir_path(\"onto\", opts), &buf))\n+\tif (!read_oneliner(&head_name, state_dir_path(\"head-name\", opts),\n+\t\t\t   READ_ONELINER_WARN_MISSING) ||\n+\t    !read_oneliner(&buf, state_dir_path(\"onto\", opts),\n+\t\t\t   READ_ONELINER_WARN_MISSING))\n \t\treturn -1;\n \topts->head_name = starts_with(head_name.buf, \"refs/\") ?\n \t\txstrdup(head_name.buf) : NULL;\n@@ -617,11 +610,12 @@ static int read_basic_state(struct rebase_options *opts)\n \t * head. Fall back to reading from head to cover for the case that the\n \t * user upgraded git with an ongoing interactive rebase.\n \t */\n-\tstrbuf_reset(&buf);\n \tif (file_exists(state_dir_path(\"orig-head\", opts))) {\n-\t\tif (read_one(state_dir_path(\"orig-head\", opts), &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"orig-head\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_MISSING))\n \t\t\treturn -1;\n-\t} else if (read_one(state_dir_path(\"head\", opts), &buf))\n+\t} else if (!read_oneliner(&buf, state_dir_path(\"head\", opts),\n+\t\t\t\t  READ_ONELINER_WARN_MISSING))\n \t\treturn -1;\n \tif (get_oid(buf.buf, &opts->orig_head))\n \t\treturn error(_(\"invalid orig-head: '%s'\"), buf.buf);\n@@ -640,9 +634,8 @@ static int read_basic_state(struct rebase_options *opts)\n \t}\n \n \tif (file_exists(state_dir_path(\"allow_rerere_autoupdate\", opts))) {\n-\t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"allow_rerere_autoupdate\", opts),\n-\t\t\t    &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"allow_rerere_autoupdate\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_MISSING))\n \t\t\treturn -1;\n \t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n \t\t\topts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;\n@@ -654,25 +647,24 @@ static int read_basic_state(struct rebase_options *opts)\n \t}\n \n \tif (file_exists(state_dir_path(\"gpg_sign_opt\", opts))) {\n-\t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"gpg_sign_opt\", opts),\n-\t\t\t    &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"gpg_sign_opt\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_MISSING))\n \t\t\treturn -1;\n \t\tfree(opts->gpg_sign_opt);\n \t\topts->gpg_sign_opt = xstrdup(buf.buf);\n \t}\n \n \tif (file_exists(state_dir_path(\"strategy\", opts))) {\n-\t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"strategy\", opts), &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_MISSING))\n \t\t\treturn -1;\n \t\tfree(opts->strategy);\n \t\topts->strategy = xstrdup(buf.buf);\n \t}\n \n \tif (file_exists(state_dir_path(\"strategy_opts\", opts))) {\n-\t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"strategy_opts\", opts), &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy_opts\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_MISSING))\n \t\t\treturn -1;\n \t\tfree(opts->strategy_opts);\n \t\topts->strategy_opts = xstrdup(buf.buf);\n@@ -724,7 +716,7 @@ static int apply_autostash(struct rebase_options *opts)\n \tif (!file_exists(path))\n \t\treturn 0;\n \n-\tif (read_one(path, &autostash))\n+\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))\n \t\treturn error(_(\"Could not read '%s'\"), path);\n \t/* Ensure that the hash is not mistaken for a number */\n \tstrbuf_addstr(&autostash, \"^0\");\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394743","messageId":"c26771e9340d313c9c9ec30decc0224b1ae41e8b.1585962672.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 01/23] Makefile: ASCII-sort += lists","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:14Z","receivedAt":"2020-04-04T01:12:12Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"There are many += lists in the Makefile and, over time, they have gotten\nslightly out of ASCII order. Sort all += lists to bring them back in\norder.\n\nASCII sorting was chosen over strict alphabetical order even though, if\nwe omit file prefixes, the lists aren't sorted in strictly alphabetical\norder (e.g. archive.o comes after archive-zip.o instead of before\narchive-tar.o). This is intentional because the purpose of maintaining\nthe sorted list is to ensure line insertions are deterministic. By using\nASCII ordering, it is more easily mechanically reproducible in the\nfuture, such as by using :sort in Vim.\n\nThis patch is best viewed with `--color-moved`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Makefile | 77 ++++++++++++++++++++++++++++----------------------------\n 1 file changed, 39 insertions(+), 38 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 9804a0758b..f709fc3f77 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -604,12 +604,12 @@ unexport CDPATH\n SCRIPT_SH += git-bisect.sh\n SCRIPT_SH += git-difftool--helper.sh\n SCRIPT_SH += git-filter-branch.sh\n+SCRIPT_SH += git-legacy-stash.sh\n SCRIPT_SH += git-merge-octopus.sh\n SCRIPT_SH += git-merge-one-file.sh\n SCRIPT_SH += git-merge-resolve.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-quiltimport.sh\n-SCRIPT_SH += git-legacy-stash.sh\n SCRIPT_SH += git-request-pull.sh\n SCRIPT_SH += git-submodule.sh\n SCRIPT_SH += git-web--browse.sh\n@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh\n SCRIPT_LIB += git-mergetool--lib\n SCRIPT_LIB += git-parse-remote\n SCRIPT_LIB += git-rebase--preserve-merges\n-SCRIPT_LIB += git-sh-setup\n SCRIPT_LIB += git-sh-i18n\n+SCRIPT_LIB += git-sh-setup\n \n SCRIPT_PERL += git-add--interactive.perl\n SCRIPT_PERL += git-archimport.perl\n@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o\n PROGRAM_OBJS += fast-import.o\n PROGRAM_OBJS += http-backend.o\n PROGRAM_OBJS += imap-send.o\n+PROGRAM_OBJS += remote-testsvn.o\n PROGRAM_OBJS += sh-i18n--envsubst.o\n PROGRAM_OBJS += shell.o\n-PROGRAM_OBJS += remote-testsvn.o\n \n # Binary suffix, set to .exe for Windows builds\n X =\n@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o\n TEST_BUILTINS_OBJS += test-example-decorate.o\n TEST_BUILTINS_OBJS += test-genrandom.o\n TEST_BUILTINS_OBJS += test-genzeros.o\n+TEST_BUILTINS_OBJS += test-hash-speed.o\n TEST_BUILTINS_OBJS += test-hash.o\n TEST_BUILTINS_OBJS += test-hashmap.o\n-TEST_BUILTINS_OBJS += test-hash-speed.o\n TEST_BUILTINS_OBJS += test-index-version.o\n TEST_BUILTINS_OBJS += test-json-writer.o\n TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o\n@@ -737,8 +737,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o\n TEST_BUILTINS_OBJS += test-run-command.o\n TEST_BUILTINS_OBJS += test-scrap-cache-tree.o\n TEST_BUILTINS_OBJS += test-serve-v2.o\n-TEST_BUILTINS_OBJS += test-sha1.o\n TEST_BUILTINS_OBJS += test-sha1-array.o\n+TEST_BUILTINS_OBJS += test-sha1.o\n TEST_BUILTINS_OBJS += test-sha256.o\n TEST_BUILTINS_OBJS += test-sigchain.o\n TEST_BUILTINS_OBJS += test-strcmp-offset.o\n@@ -748,10 +748,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o\n TEST_BUILTINS_OBJS += test-subprocess.o\n TEST_BUILTINS_OBJS += test-trace2.o\n TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n-TEST_BUILTINS_OBJS += test-xml-encode.o\n TEST_BUILTINS_OBJS += test-wildmatch.o\n TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n TEST_BUILTINS_OBJS += test-write-cache.o\n+TEST_BUILTINS_OBJS += test-xml-encode.o\n \n # Do not add more tests here unless they have extra dependencies. Add\n # them in TEST_BUILTINS_OBJS above.\n@@ -788,10 +788,10 @@ OTHER_PROGRAMS = git$X\n \n # what test wrappers are needed and 'install' will install, in bindir\n BINDIR_PROGRAMS_NEED_X += git\n-BINDIR_PROGRAMS_NEED_X += git-upload-pack\n BINDIR_PROGRAMS_NEED_X += git-receive-pack\n-BINDIR_PROGRAMS_NEED_X += git-upload-archive\n BINDIR_PROGRAMS_NEED_X += git-shell\n+BINDIR_PROGRAMS_NEED_X += git-upload-archive\n+BINDIR_PROGRAMS_NEED_X += git-upload-pack\n \n BINDIR_PROGRAMS_NO_X += git-cvsserver\n \n@@ -831,9 +831,9 @@ LIB_OBJS += advice.o\n LIB_OBJS += alias.o\n LIB_OBJS += alloc.o\n LIB_OBJS += apply.o\n-LIB_OBJS += archive.o\n LIB_OBJS += archive-tar.o\n LIB_OBJS += archive-zip.o\n+LIB_OBJS += archive.o\n LIB_OBJS += argv-array.o\n LIB_OBJS += attr.o\n LIB_OBJS += base85.o\n@@ -849,9 +849,9 @@ LIB_OBJS += checkout.o\n LIB_OBJS += color.o\n LIB_OBJS += column.o\n LIB_OBJS += combine-diff.o\n-LIB_OBJS += commit.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n+LIB_OBJS += commit.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\n LIB_OBJS += config.o\n@@ -865,17 +865,17 @@ LIB_OBJS += ctype.o\n LIB_OBJS += date.o\n LIB_OBJS += decorate.o\n LIB_OBJS += delta-islands.o\n+LIB_OBJS += diff-delta.o\n+LIB_OBJS += diff-lib.o\n+LIB_OBJS += diff-no-index.o\n+LIB_OBJS += diff.o\n LIB_OBJS += diffcore-break.o\n LIB_OBJS += diffcore-delta.o\n LIB_OBJS += diffcore-order.o\n LIB_OBJS += diffcore-pickaxe.o\n LIB_OBJS += diffcore-rename.o\n-LIB_OBJS += diff-delta.o\n-LIB_OBJS += diff-lib.o\n-LIB_OBJS += diff-no-index.o\n-LIB_OBJS += diff.o\n-LIB_OBJS += dir.o\n LIB_OBJS += dir-iterator.o\n+LIB_OBJS += dir.o\n LIB_OBJS += editor.o\n LIB_OBJS += entry.o\n LIB_OBJS += environment.o\n@@ -893,7 +893,6 @@ LIB_OBJS += gpg-interface.o\n LIB_OBJS += graph.o\n LIB_OBJS += grep.o\n LIB_OBJS += hashmap.o\n-LIB_OBJS += linear-assignment.o\n LIB_OBJS += help.o\n LIB_OBJS += hex.o\n LIB_OBJS += ident.o\n@@ -903,9 +902,10 @@ LIB_OBJS += kwset.o\n LIB_OBJS += levenshtein.o\n LIB_OBJS += line-log.o\n LIB_OBJS += line-range.o\n-LIB_OBJS += list-objects.o\n-LIB_OBJS += list-objects-filter.o\n+LIB_OBJS += linear-assignment.o\n LIB_OBJS += list-objects-filter-options.o\n+LIB_OBJS += list-objects-filter.o\n+LIB_OBJS += list-objects.o\n LIB_OBJS += ll-merge.o\n LIB_OBJS += lockfile.o\n LIB_OBJS += log-tree.o\n@@ -914,31 +914,31 @@ LIB_OBJS += mailinfo.o\n LIB_OBJS += mailmap.o\n LIB_OBJS += match-trees.o\n LIB_OBJS += mem-pool.o\n-LIB_OBJS += merge.o\n LIB_OBJS += merge-blobs.o\n LIB_OBJS += merge-recursive.o\n+LIB_OBJS += merge.o\n LIB_OBJS += mergesort.o\n LIB_OBJS += midx.o\n LIB_OBJS += name-hash.o\n LIB_OBJS += negotiator/default.o\n LIB_OBJS += negotiator/skipping.o\n-LIB_OBJS += notes.o\n LIB_OBJS += notes-cache.o\n LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\n+LIB_OBJS += notes.o\n LIB_OBJS += object.o\n LIB_OBJS += oidmap.o\n LIB_OBJS += oidset.o\n-LIB_OBJS += packfile.o\n-LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-bitmap-write.o\n+LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-objects.o\n LIB_OBJS += pack-revindex.o\n LIB_OBJS += pack-write.o\n+LIB_OBJS += packfile.o\n LIB_OBJS += pager.o\n-LIB_OBJS += parse-options.o\n LIB_OBJS += parse-options-cb.o\n+LIB_OBJS += parse-options.o\n LIB_OBJS += patch-delta.o\n LIB_OBJS += patch-ids.o\n LIB_OBJS += path.o\n@@ -955,8 +955,9 @@ LIB_OBJS += quote.o\n LIB_OBJS += range-diff.o\n LIB_OBJS += reachable.o\n LIB_OBJS += read-cache.o\n-LIB_OBJS += rebase.o\n LIB_OBJS += rebase-interactive.o\n+LIB_OBJS += rebase.o\n+LIB_OBJS += ref-filter.o\n LIB_OBJS += reflog-walk.o\n LIB_OBJS += refs.o\n LIB_OBJS += refs/files-backend.o\n@@ -964,7 +965,6 @@ LIB_OBJS += refs/iterator.o\n LIB_OBJS += refs/packed-backend.o\n LIB_OBJS += refs/ref-cache.o\n LIB_OBJS += refspec.o\n-LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace-object.o\n LIB_OBJS += repo-settings.o\n@@ -979,8 +979,8 @@ LIB_OBJS += serve.o\n LIB_OBJS += server-info.o\n LIB_OBJS += setup.o\n LIB_OBJS += sha1-array.o\n-LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1-file.o\n+LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1-name.o\n LIB_OBJS += shallow.o\n LIB_OBJS += sideband.o\n@@ -990,9 +990,9 @@ LIB_OBJS += stable-qsort.o\n LIB_OBJS += strbuf.o\n LIB_OBJS += streaming.o\n LIB_OBJS += string-list.o\n-LIB_OBJS += submodule.o\n-LIB_OBJS += submodule-config.o\n LIB_OBJS += sub-process.o\n+LIB_OBJS += submodule-config.o\n+LIB_OBJS += submodule.o\n LIB_OBJS += symlinks.o\n LIB_OBJS += tag.o\n LIB_OBJS += tempfile.o\n@@ -1011,11 +1011,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o\n LIB_OBJS += trace2/tr2_tgt_perf.o\n LIB_OBJS += trace2/tr2_tls.o\n LIB_OBJS += trailer.o\n-LIB_OBJS += transport.o\n LIB_OBJS += transport-helper.o\n+LIB_OBJS += transport.o\n LIB_OBJS += tree-diff.o\n-LIB_OBJS += tree.o\n LIB_OBJS += tree-walk.o\n+LIB_OBJS += tree.o\n LIB_OBJS += unpack-trees.o\n LIB_OBJS += upload-pack.o\n LIB_OBJS += url.o\n@@ -1055,9 +1055,9 @@ BUILTIN_OBJS += builtin/checkout.o\n BUILTIN_OBJS += builtin/clean.o\n BUILTIN_OBJS += builtin/clone.o\n BUILTIN_OBJS += builtin/column.o\n+BUILTIN_OBJS += builtin/commit-graph.o\n BUILTIN_OBJS += builtin/commit-tree.o\n BUILTIN_OBJS += builtin/commit.o\n-BUILTIN_OBJS += builtin/commit-graph.o\n BUILTIN_OBJS += builtin/config.o\n BUILTIN_OBJS += builtin/count-objects.o\n BUILTIN_OBJS += builtin/credential.o\n@@ -1088,13 +1088,13 @@ BUILTIN_OBJS += builtin/ls-remote.o\n BUILTIN_OBJS += builtin/ls-tree.o\n BUILTIN_OBJS += builtin/mailinfo.o\n BUILTIN_OBJS += builtin/mailsplit.o\n-BUILTIN_OBJS += builtin/merge.o\n BUILTIN_OBJS += builtin/merge-base.o\n BUILTIN_OBJS += builtin/merge-file.o\n BUILTIN_OBJS += builtin/merge-index.o\n BUILTIN_OBJS += builtin/merge-ours.o\n BUILTIN_OBJS += builtin/merge-recursive.o\n BUILTIN_OBJS += builtin/merge-tree.o\n+BUILTIN_OBJS += builtin/merge.o\n BUILTIN_OBJS += builtin/mktag.o\n BUILTIN_OBJS += builtin/mktree.o\n BUILTIN_OBJS += builtin/multi-pack-index.o\n@@ -1114,9 +1114,9 @@ BUILTIN_OBJS += builtin/read-tree.o\n BUILTIN_OBJS += builtin/rebase.o\n BUILTIN_OBJS += builtin/receive-pack.o\n BUILTIN_OBJS += builtin/reflog.o\n-BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/remote-ext.o\n BUILTIN_OBJS += builtin/remote-fd.o\n+BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/rerere.o\n@@ -2335,16 +2335,16 @@ reconfigure config.mak.autogen: config.status\n endif\n \n XDIFF_OBJS += xdiff/xdiffi.o\n-XDIFF_OBJS += xdiff/xprepare.o\n-XDIFF_OBJS += xdiff/xutils.o\n XDIFF_OBJS += xdiff/xemit.o\n+XDIFF_OBJS += xdiff/xhistogram.o\n XDIFF_OBJS += xdiff/xmerge.o\n XDIFF_OBJS += xdiff/xpatience.o\n-XDIFF_OBJS += xdiff/xhistogram.o\n+XDIFF_OBJS += xdiff/xprepare.o\n+XDIFF_OBJS += xdiff/xutils.o\n \n+VCSSVN_OBJS += vcs-svn/fast_export.o\n VCSSVN_OBJS += vcs-svn/line_buffer.o\n VCSSVN_OBJS += vcs-svn/sliding_window.o\n-VCSSVN_OBJS += vcs-svn/fast_export.o\n VCSSVN_OBJS += vcs-svn/svndiff.o\n VCSSVN_OBJS += vcs-svn/svndump.o\n \n@@ -3152,9 +3152,10 @@ endif\n #\n ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS)\n ALL_COMMANDS += git\n+ALL_COMMANDS += git-citool\n+ALL_COMMANDS += git-gui\n ALL_COMMANDS += gitk\n ALL_COMMANDS += gitweb\n-ALL_COMMANDS += git-gui git-citool\n \n .PHONY: check-docs\n check-docs::\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394744","messageId":"6c3c37994babdf4749e3edfcaa9075e6ee94524b.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 08/23] sequencer: make read_oneliner() extern","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:21Z","receivedAt":"2020-04-04T01:12:13Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"The function read_oneliner() is a generally useful util function.\nInstead of hiding it as a static function within sequencer.c, extern it\nso that it can be reused by others.\n\nThis patch is best viewed with --color-moved.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 14 +-------------\n sequencer.h | 14 ++++++++++++++\n 2 files changed, 15 insertions(+), 13 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b771e8f4ca..d1d59867ae 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -419,19 +419,7 @@ static int write_message(const void *buf, size_t len, const char *filename,\n \treturn 0;\n }\n \n-#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n-#define READ_ONELINER_WARN_MISSING (1 << 1)\n-\n-/*\n- * Resets a strbuf then reads a file that was presumably written by a shell\n- * script, i.e. with an end-of-line marker that needs to be stripped.\n- *\n- * Note that only the last end-of-line marker is stripped, consistent with the\n- * behavior of \"$(cat path)\" in a shell script.\n- *\n- * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n- */\n-static int read_oneliner(struct strbuf *buf,\n+int read_oneliner(struct strbuf *buf,\n \tconst char *path, unsigned flags)\n {\n \tstrbuf_reset(buf);\ndiff --git a/sequencer.h b/sequencer.h\nindex 718a07426d..1f6eda70b6 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -198,6 +198,20 @@ void print_commit_summary(struct repository *repo,\n \t\t\t  const struct object_id *oid,\n \t\t\t  unsigned int flags);\n \n+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n+#define READ_ONELINER_WARN_MISSING (1 << 1)\n+\n+/*\n+ * Resets a strbuf then reads a file that was presumably written by a shell\n+ * script, i.e. with an end-of-line marker that needs to be stripped.\n+ *\n+ * Note that only the last end-of-line marker is stripped, consistent with the\n+ * behavior of \"$(cat path)\" in a shell script.\n+ *\n+ * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n+ */\n+int read_oneliner(struct strbuf *buf,\n+\tconst char *path, unsigned flags);\n int read_author_script(const char *path, char **name, char **email, char **date,\n \t\t       int allow_missing);\n void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394745","messageId":"389b17df334c3d9a8138b5db0ec15c81a8a6ce31.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 12/23] rebase: use apply_autostash() from sequencer.c","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:25Z","receivedAt":"2020-04-04T01:12:14Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"The apply_autostash() function in builtin/rebase.c is similar enough to\nthe apply_autostash() function in sequencer.c that they are almost\ninterchangeable, except for the type of arg they accept. Make the\nsequencer.c version extern and use it in rebase.\n\nThe rebase version was introduced in 6defce2b02 (builtin rebase: support\n`--autostash` option, 2018-09-04) as part of the shell to C conversion.\nIt opted to duplicate the function because, at the time, there was\nanother in-progress project converting interactive rebase from shell to\nC as well and they did not want to clash with them by refactoring\nsequencer.c version of apply_autostash(). Since both efforts are long\ndone, we can freely combine them together now.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 49 ++----------------------------------------------\n sequencer.c      |  2 +-\n sequencer.h      |  2 ++\n 3 files changed, 5 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 91873fa0b5..29745176cb 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -707,51 +707,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)\n \treturn 0;\n }\n \n-static int apply_autostash(struct rebase_options *opts)\n-{\n-\tconst char *path = state_dir_path(\"autostash\", opts);\n-\tstruct strbuf autostash = STRBUF_INIT;\n-\tstruct child_process stash_apply = CHILD_PROCESS_INIT;\n-\n-\tif (!file_exists(path))\n-\t\treturn 0;\n-\n-\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))\n-\t\treturn error(_(\"Could not read '%s'\"), path);\n-\t/* Ensure that the hash is not mistaken for a number */\n-\tstrbuf_addstr(&autostash, \"^0\");\n-\targv_array_pushl(&stash_apply.args,\n-\t\t\t \"stash\", \"apply\", autostash.buf, NULL);\n-\tstash_apply.git_cmd = 1;\n-\tstash_apply.no_stderr = stash_apply.no_stdout =\n-\t\tstash_apply.no_stdin = 1;\n-\tif (!run_command(&stash_apply))\n-\t\tprintf(_(\"Applied autostash.\\n\"));\n-\telse {\n-\t\tstruct argv_array args = ARGV_ARRAY_INIT;\n-\t\tint res = 0;\n-\n-\t\targv_array_pushl(&args,\n-\t\t\t\t \"stash\", \"store\", \"-m\", \"autostash\", \"-q\",\n-\t\t\t\t autostash.buf, NULL);\n-\t\tif (run_command_v_opt(args.argv, RUN_GIT_CMD))\n-\t\t\tres = error(_(\"Cannot store %s\"), autostash.buf);\n-\t\targv_array_clear(&args);\n-\t\tstrbuf_release(&autostash);\n-\t\tif (res)\n-\t\t\treturn res;\n-\n-\t\tfprintf(stderr,\n-\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n-\t\t\t  \"Your changes are safe in the stash.\\n\"\n-\t\t\t  \"You can run \\\"git stash pop\\\" or \\\"git stash drop\\\" \"\n-\t\t\t  \"at any time.\\n\"));\n-\t}\n-\n-\tstrbuf_release(&autostash);\n-\treturn 0;\n-}\n-\n static int finish_rebase(struct rebase_options *opts)\n {\n \tstruct strbuf dir = STRBUF_INIT;\n@@ -759,7 +714,7 @@ static int finish_rebase(struct rebase_options *opts)\n \tint ret = 0;\n \n \tdelete_ref(NULL, \"REBASE_HEAD\", NULL, REF_NO_DEREF);\n-\tapply_autostash(opts);\n+\tapply_autostash(state_dir_path(\"autostash\", opts));\n \tclose_object_store(the_repository->objects);\n \t/*\n \t * We ignore errors in 'gc --auto', since the\n@@ -1204,7 +1159,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)\n \t} else if (status == 2) {\n \t\tstruct strbuf dir = STRBUF_INIT;\n \n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(state_dir_path(\"autostash\", opts));\n \t\tstrbuf_addstr(&dir, opts->state_dir);\n \t\tremove_dir_recursively(&dir, 0);\n \t\tstrbuf_release(&dir);\ndiff --git a/sequencer.c b/sequencer.c\nindex 4b7985942d..dde1f6b30f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3649,7 +3649,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n-static int apply_autostash(const char *path)\n+int apply_autostash(const char *path)\n {\n \tstruct strbuf stash_oid = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\ndiff --git a/sequencer.h b/sequencer.h\nindex 1f6eda70b6..d6ba62a089 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,\n \t\t\t const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n \n+int apply_autostash(const char *path);\n+\n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n void print_commit_summary(struct repository *repo,\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394746","messageId":"a52583beff4494b9147b4c700f7be985b80f4197.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 11/23] sequencer: rename stash_sha1 to stash_oid","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:24Z","receivedAt":"2020-04-04T01:12:15Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"The preferred terminology is to refer to object identifiers as \"OIDs\".\nRename the `stash_sha1` variable to `stash_oid` in order to conform to\nthis.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex ae9efe5825..4b7985942d 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3651,23 +3651,23 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \n static int apply_autostash(const char *path)\n {\n-\tstruct strbuf stash_sha1 = STRBUF_INIT;\n+\tstruct strbuf stash_oid = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\n \tint ret = 0;\n \n-\tif (!read_oneliner(&stash_sha1, path,\n+\tif (!read_oneliner(&stash_oid, path,\n \t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n-\t\tstrbuf_release(&stash_sha1);\n+\t\tstrbuf_release(&stash_oid);\n \t\treturn 0;\n \t}\n-\tstrbuf_trim(&stash_sha1);\n+\tstrbuf_trim(&stash_oid);\n \n \tchild.git_cmd = 1;\n \tchild.no_stdout = 1;\n \tchild.no_stderr = 1;\n \targv_array_push(&child.args, \"stash\");\n \targv_array_push(&child.args, \"apply\");\n-\targv_array_push(&child.args, stash_sha1.buf);\n+\targv_array_push(&child.args, stash_oid.buf);\n \tif (!run_command(&child))\n \t\tfprintf(stderr, _(\"Applied autostash.\\n\"));\n \telse {\n@@ -3679,9 +3679,9 @@ static int apply_autostash(const char *path)\n \t\targv_array_push(&store.args, \"-m\");\n \t\targv_array_push(&store.args, \"autostash\");\n \t\targv_array_push(&store.args, \"-q\");\n-\t\targv_array_push(&store.args, stash_sha1.buf);\n+\t\targv_array_push(&store.args, stash_oid.buf);\n \t\tif (run_command(&store))\n-\t\t\tret = error(_(\"cannot store %s\"), stash_sha1.buf);\n+\t\t\tret = error(_(\"cannot store %s\"), stash_oid.buf);\n \t\telse\n \t\t\tfprintf(stderr,\n \t\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n@@ -3690,7 +3690,7 @@ static int apply_autostash(const char *path)\n \t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"));\n \t}\n \n-\tstrbuf_release(&stash_sha1);\n+\tstrbuf_release(&stash_oid);\n \treturn ret;\n }\n \n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394747","messageId":"d5f51cd80e8bc85146ac8ea0cb95f89afbd7324d.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 15/23] rebase: extract create_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:28Z","receivedAt":"2020-04-04T01:12:17Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future commit, we will lib-ify this code. In preparation for\nthis, extract the code into the create_autostash() function so that it\ncan be cleaned up before it is finally lib-ified.\n\nThis patch is best viewed with `--color-moved` and\n`--color-moved-ws=allow-indentation-change`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 94 +++++++++++++++++++++++++-----------------------\n 1 file changed, 50 insertions(+), 44 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 55730f6360..8349f3a03a 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1269,6 +1269,55 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n+static void create_autostash(struct rebase_options *options)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct lock_file lock_file = LOCK_INIT;\n+\tint fd;\n+\n+\tfd = hold_locked_index(&lock_file, 0);\n+\trefresh_cache(REFRESH_QUIET);\n+\tif (0 <= fd)\n+\t\trepo_update_index_if_able(the_repository, &lock_file);\n+\trollback_lock_file(&lock_file);\n+\n+\tif (has_unstaged_changes(the_repository, 1) ||\n+\t    has_uncommitted_changes(the_repository, 1)) {\n+\t\tconst char *autostash =\n+\t\t\tstate_dir_path(\"autostash\", options);\n+\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n+\t\tstruct object_id oid;\n+\n+\t\targv_array_pushl(&stash.args,\n+\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n+\t\tstash.git_cmd = 1;\n+\t\tstash.no_stdin = 1;\n+\t\tstrbuf_reset(&buf);\n+\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n+\t\t\tdie(_(\"Cannot autostash\"));\n+\t\tstrbuf_trim_trailing_newline(&buf);\n+\t\tif (get_oid(buf.buf, &oid))\n+\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n+\t\t\t    buf.buf);\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n+\n+\t\tif (safe_create_leading_directories_const(autostash))\n+\t\t\tdie(_(\"Could not create directory for '%s'\"),\n+\t\t\t    options->state_dir);\n+\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n+\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n+\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n+\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n+\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n+\t\t\tdie(_(\"could not reset --hard\"));\n+\n+\t\tif (discard_index(the_repository->index) < 0 ||\n+\t\t\trepo_read_index(the_repository) < 0)\n+\t\t\tdie(_(\"could not read index\"));\n+\t}\n+\tstrbuf_release(&buf);\n+}\n \n int cmd_rebase(int argc, const char **argv, const char *prefix)\n {\n@@ -1902,50 +1951,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read index\"));\n \n \tif (options.autostash) {\n-\t\tstruct lock_file lock_file = LOCK_INIT;\n-\t\tint fd;\n-\n-\t\tfd = hold_locked_index(&lock_file, 0);\n-\t\trefresh_cache(REFRESH_QUIET);\n-\t\tif (0 <= fd)\n-\t\t\trepo_update_index_if_able(the_repository, &lock_file);\n-\t\trollback_lock_file(&lock_file);\n-\n-\t\tif (has_unstaged_changes(the_repository, 1) ||\n-\t\t    has_uncommitted_changes(the_repository, 1)) {\n-\t\t\tconst char *autostash =\n-\t\t\t\tstate_dir_path(\"autostash\", &options);\n-\t\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n-\t\t\tstruct object_id oid;\n-\n-\t\t\targv_array_pushl(&stash.args,\n-\t\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n-\t\t\tstash.git_cmd = 1;\n-\t\t\tstash.no_stdin = 1;\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n-\t\t\t\tdie(_(\"Cannot autostash\"));\n-\t\t\tstrbuf_trim_trailing_newline(&buf);\n-\t\t\tif (get_oid(buf.buf, &oid))\n-\t\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n-\t\t\t\t    buf.buf);\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n-\n-\t\t\tif (safe_create_leading_directories_const(autostash))\n-\t\t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t\t    options.state_dir);\n-\t\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n-\t\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n-\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n-\t\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n-\t\t\t\tdie(_(\"could not reset --hard\"));\n-\n-\t\t\tif (discard_index(the_repository->index) < 0 ||\n-\t\t\t\trepo_read_index(the_repository) < 0)\n-\t\t\t\tdie(_(\"could not read index\"));\n-\t\t}\n+\t\tcreate_autostash(&options);\n \t}\n \n \tif (require_clean_work_tree(the_repository, \"rebase\",\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394748","messageId":"fe57e21dbc70d02e4462f28bc36752abd4f6c279.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 13/23] rebase: generify reset_head()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:26Z","receivedAt":"2020-04-04T01:12:17Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we plan on lib-ifying reset_head() so we need it to\nbe more generic. Make it more generic by making it accept a\n`struct repository` argument instead of implicitly using the non-repo\nfunctions. Also, make it accept a `const char *default_reflog_action`\nargument so that the default action of \"rebase\" isn't hardcoded in.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 64 ++++++++++++++++++++++++++++--------------------\n 1 file changed, 37 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 29745176cb..0f650a798e 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -28,6 +28,8 @@\n #include \"sequencer.h\"\n #include \"rebase-interactive.h\"\n \n+#define DEFAULT_REFLOG_ACTION \"rebase\"\n+\n static char const * const builtin_rebase_usage[] = {\n \tN_(\"git rebase [-i] [options] [--exec <cmd>] \"\n \t\t\"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]\"),\n@@ -767,9 +769,10 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)\n #define RESET_HEAD_REFS_ONLY (1<<3)\n #define RESET_ORIG_HEAD (1<<4)\n \n-static int reset_head(struct object_id *oid, const char *action,\n+static int reset_head(struct repository *r, struct object_id *oid, const char *action,\n \t\t      const char *switch_to_branch, unsigned flags,\n-\t\t      const char *reflog_orig_head, const char *reflog_head)\n+\t\t      const char *reflog_orig_head, const char *reflog_head,\n+\t\t      const char *default_reflog_action)\n {\n \tunsigned detach_head = flags & RESET_HEAD_DETACH;\n \tunsigned reset_hard = flags & RESET_HEAD_HARD;\n@@ -791,7 +794,7 @@ static int reset_head(struct object_id *oid, const char *action,\n \tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n \t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n \n-\tif (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {\n+\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n \t\tret = -1;\n \t\tgoto leave_reset_head;\n \t}\n@@ -810,26 +813,26 @@ static int reset_head(struct object_id *oid, const char *action,\n \tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n \tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n \tunpack_tree_opts.head_idx = 1;\n-\tunpack_tree_opts.src_index = the_repository->index;\n-\tunpack_tree_opts.dst_index = the_repository->index;\n+\tunpack_tree_opts.src_index = r->index;\n+\tunpack_tree_opts.dst_index = r->index;\n \tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n \tunpack_tree_opts.update = 1;\n \tunpack_tree_opts.merge = 1;\n \tif (!detach_head)\n \t\tunpack_tree_opts.reset = 1;\n \n-\tif (repo_read_index_unmerged(the_repository) < 0) {\n+\tif (repo_read_index_unmerged(r) < 0) {\n \t\tret = error(_(\"could not read index\"));\n \t\tgoto leave_reset_head;\n \t}\n \n-\tif (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {\n+\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n \t\tret = error(_(\"failed to find tree of %s\"),\n \t\t\t    oid_to_hex(&head_oid));\n \t\tgoto leave_reset_head;\n \t}\n \n-\tif (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {\n+\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n \t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n \t\tgoto leave_reset_head;\n \t}\n@@ -840,16 +843,16 @@ static int reset_head(struct object_id *oid, const char *action,\n \t}\n \n \ttree = parse_tree_indirect(oid);\n-\tprime_cache_tree(the_repository, the_repository->index, tree);\n+\tprime_cache_tree(r, r->index, tree);\n \n-\tif (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {\n+\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n \t\tret = error(_(\"could not write index\"));\n \t\tgoto leave_reset_head;\n \t}\n \n reset_head_refs:\n \treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n-\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : \"rebase\");\n+\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n \tprefix_len = msg.len;\n \n \tif (update_orig_head) {\n@@ -911,8 +914,10 @@ static int move_to_original_branch(struct rebase_options *opts)\n \t\t    opts->head_name, oid_to_hex(&opts->onto->object.oid));\n \tstrbuf_addf(&head_reflog, \"rebase finished: returning to %s\",\n \t\t    opts->head_name);\n-\tret = reset_head(NULL, \"\", opts->head_name, RESET_HEAD_REFS_ONLY,\n-\t\t\t orig_head_reflog.buf, head_reflog.buf);\n+\tret = reset_head(the_repository, NULL, \"\", opts->head_name,\n+\t\t\t RESET_HEAD_REFS_ONLY,\n+\t\t\t orig_head_reflog.buf, head_reflog.buf,\n+\t\t\t DEFAULT_REFLOG_ACTION);\n \n \tstrbuf_release(&orig_head_reflog);\n \tstrbuf_release(&head_reflog);\n@@ -1000,8 +1005,9 @@ static int run_am(struct rebase_options *opts)\n \t\tfree(rebased_patches);\n \t\targv_array_clear(&am.args);\n \n-\t\treset_head(&opts->orig_head, \"checkout\", opts->head_name, 0,\n-\t\t\t   \"HEAD\", NULL);\n+\t\treset_head(the_repository, &opts->orig_head, \"checkout\",\n+\t\t\t   opts->head_name, 0,\n+\t\t\t   \"HEAD\", NULL, DEFAULT_REFLOG_ACTION);\n \t\terror(_(\"\\ngit encountered an error while preparing the \"\n \t\t\t\"patches to replay\\n\"\n \t\t\t\"these revisions:\\n\"\n@@ -1656,8 +1662,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\trerere_clear(the_repository, &merge_rr);\n \t\tstring_list_clear(&merge_rr, 1);\n \n-\t\tif (reset_head(NULL, \"reset\", NULL, RESET_HEAD_HARD,\n-\t\t\t       NULL, NULL) < 0)\n+\t\tif (reset_head(the_repository, NULL, \"reset\", NULL, RESET_HEAD_HARD,\n+\t\t\t       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)\n \t\t\tdie(_(\"could not discard worktree changes\"));\n \t\tremove_branch_state(the_repository, 0);\n \t\tif (read_basic_state(&options))\n@@ -1674,9 +1680,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \t\tif (read_basic_state(&options))\n \t\t\texit(1);\n-\t\tif (reset_head(&options.orig_head, \"reset\",\n+\t\tif (reset_head(the_repository, &options.orig_head, \"reset\",\n \t\t\t       options.head_name, RESET_HEAD_HARD,\n-\t\t\t       NULL, NULL) < 0)\n+\t\t\t       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)\n \t\t\tdie(_(\"could not move back to %s\"),\n \t\t\t    oid_to_hex(&options.orig_head));\n \t\tremove_branch_state(the_repository, 0);\n@@ -2068,8 +2074,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\t    options.state_dir);\n \t\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n \t\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\t\tif (reset_head(NULL, \"reset --hard\",\n-\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)\n+\t\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n+\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n+\t\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n \t\t\t\tdie(_(\"could not reset --hard\"));\n \n \t\t\tif (discard_index(the_repository->index) < 0 ||\n@@ -2109,10 +2116,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\tstrbuf_addf(&buf, \"%s: checkout %s\",\n \t\t\t\t\t    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),\n \t\t\t\t\t    options.switch_to);\n-\t\t\t\tif (reset_head(&options.orig_head, \"checkout\",\n+\t\t\t\tif (reset_head(the_repository,\n+\t\t\t\t\t       &options.orig_head, \"checkout\",\n \t\t\t\t\t       options.head_name,\n \t\t\t\t\t       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,\n-\t\t\t\t\t       NULL, buf.buf) < 0) {\n+\t\t\t\t\t       NULL, buf.buf,\n+\t\t\t\t\t       DEFAULT_REFLOG_ACTION) < 0) {\n \t\t\t\t\tret = !!error(_(\"could not switch to \"\n \t\t\t\t\t\t\t\"%s\"),\n \t\t\t\t\t\t      options.switch_to);\n@@ -2184,10 +2193,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tstrbuf_addf(&msg, \"%s: checkout %s\",\n \t\t    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);\n-\tif (reset_head(&options.onto->object.oid, \"checkout\", NULL,\n+\tif (reset_head(the_repository, &options.onto->object.oid, \"checkout\", NULL,\n \t\t       RESET_HEAD_DETACH | RESET_ORIG_HEAD |\n \t\t       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,\n-\t\t       NULL, msg.buf))\n+\t\t       NULL, msg.buf, DEFAULT_REFLOG_ACTION))\n \t\tdie(_(\"Could not detach HEAD\"));\n \tstrbuf_release(&msg);\n \n@@ -2202,8 +2211,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addf(&msg, \"rebase finished: %s onto %s\",\n \t\t\toptions.head_name ? options.head_name : \"detached HEAD\",\n \t\t\toid_to_hex(&options.onto->object.oid));\n-\t\treset_head(NULL, \"Fast-forwarded\", options.head_name,\n-\t\t\t   RESET_HEAD_REFS_ONLY, \"HEAD\", msg.buf);\n+\t\treset_head(the_repository, NULL, \"Fast-forwarded\", options.head_name,\n+\t\t\t   RESET_HEAD_REFS_ONLY, \"HEAD\", msg.buf,\n+\t\t\t   DEFAULT_REFLOG_ACTION);\n \t\tstrbuf_release(&msg);\n \t\tret = !!finish_rebase(&options);\n \t\tgoto cleanup;\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394749","messageId":"9ab10d23d41a2135a02bf054269a7deb5d07604e.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 16/23] rebase: generify create_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:29Z","receivedAt":"2020-04-04T01:12:20Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we plan on lib-ifying create_autostash() so we need it to\nbe more generic. Make it more generic by making it accept a\n`struct repository` argument instead of implicitly using the non-repo\nfunctions and `the_repository`. Also, make it accept a `path` argument\nso that we no longer rely have to rely on `struct rebase_options`.\nFinally, make it accept a `default_reflog_action` argument so we no\nlonger have to rely on `DEFAULT_REFLOG_ACTION`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 32 ++++++++++++++++----------------\n 1 file changed, 16 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 8349f3a03a..e9b05a6cd8 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1269,22 +1269,21 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n-static void create_autostash(struct rebase_options *options)\n+static void create_autostash(struct repository *r, const char *path,\n+\t\t\t     const char *default_reflog_action)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct lock_file lock_file = LOCK_INIT;\n \tint fd;\n \n-\tfd = hold_locked_index(&lock_file, 0);\n-\trefresh_cache(REFRESH_QUIET);\n+\tfd = repo_hold_locked_index(r, &lock_file, 0);\n+\trefresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);\n \tif (0 <= fd)\n-\t\trepo_update_index_if_able(the_repository, &lock_file);\n+\t\trepo_update_index_if_able(r, &lock_file);\n \trollback_lock_file(&lock_file);\n \n-\tif (has_unstaged_changes(the_repository, 1) ||\n-\t    has_uncommitted_changes(the_repository, 1)) {\n-\t\tconst char *autostash =\n-\t\t\tstate_dir_path(\"autostash\", options);\n+\tif (has_unstaged_changes(r, 1) ||\n+\t    has_uncommitted_changes(r, 1)) {\n \t\tstruct child_process stash = CHILD_PROCESS_INIT;\n \t\tstruct object_id oid;\n \n@@ -1302,18 +1301,18 @@ static void create_autostash(struct rebase_options *options)\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n \n-\t\tif (safe_create_leading_directories_const(autostash))\n+\t\tif (safe_create_leading_directories_const(path))\n \t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t    options->state_dir);\n-\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n+\t\t\t    path);\n+\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n \t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n+\t\tif (reset_head(r, NULL, \"reset --hard\",\n \t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n-\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n+\t\t\t       default_reflog_action) < 0)\n \t\t\tdie(_(\"could not reset --hard\"));\n \n-\t\tif (discard_index(the_repository->index) < 0 ||\n-\t\t\trepo_read_index(the_repository) < 0)\n+\t\tif (discard_index(r->index) < 0 ||\n+\t\t\trepo_read_index(r) < 0)\n \t\t\tdie(_(\"could not read index\"));\n \t}\n \tstrbuf_release(&buf);\n@@ -1951,7 +1950,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read index\"));\n \n \tif (options.autostash) {\n-\t\tcreate_autostash(&options);\n+\t\tcreate_autostash(the_repository, state_dir_path(\"autostash\", &options),\n+\t\t\t\t DEFAULT_REFLOG_ACTION);\n \t}\n \n \tif (require_clean_work_tree(the_repository, \"rebase\",\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394750","messageId":"26cca49be6908083382b2209e405e7462cd077a7.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 17/23] sequencer: extract perform_autostash() from rebase","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:30Z","receivedAt":"2020-04-04T01:12:20Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Lib-ify the autostash code by extracting perform_autostash() from rebase\ninto sequencer. In a future commit, this will be used to implement\n`--autostash` in other builtins.\n\nThis patch is best viewed with `--color-moved`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 49 -----------------------------------------------\n sequencer.c      | 50 ++++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |  2 ++\n 3 files changed, 52 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex e9b05a6cd8..b800c8bfca 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1269,55 +1269,6 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n-static void create_autostash(struct repository *r, const char *path,\n-\t\t\t     const char *default_reflog_action)\n-{\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct lock_file lock_file = LOCK_INIT;\n-\tint fd;\n-\n-\tfd = repo_hold_locked_index(r, &lock_file, 0);\n-\trefresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);\n-\tif (0 <= fd)\n-\t\trepo_update_index_if_able(r, &lock_file);\n-\trollback_lock_file(&lock_file);\n-\n-\tif (has_unstaged_changes(r, 1) ||\n-\t    has_uncommitted_changes(r, 1)) {\n-\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n-\t\tstruct object_id oid;\n-\n-\t\targv_array_pushl(&stash.args,\n-\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n-\t\tstash.git_cmd = 1;\n-\t\tstash.no_stdin = 1;\n-\t\tstrbuf_reset(&buf);\n-\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n-\t\t\tdie(_(\"Cannot autostash\"));\n-\t\tstrbuf_trim_trailing_newline(&buf);\n-\t\tif (get_oid(buf.buf, &oid))\n-\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n-\t\t\t    buf.buf);\n-\t\tstrbuf_reset(&buf);\n-\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n-\n-\t\tif (safe_create_leading_directories_const(path))\n-\t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t    path);\n-\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n-\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\tif (reset_head(r, NULL, \"reset --hard\",\n-\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n-\t\t\t       default_reflog_action) < 0)\n-\t\t\tdie(_(\"could not reset --hard\"));\n-\n-\t\tif (discard_index(r->index) < 0 ||\n-\t\t\trepo_read_index(r) < 0)\n-\t\t\tdie(_(\"could not read index\"));\n-\t}\n-\tstrbuf_release(&buf);\n-}\n-\n int cmd_rebase(int argc, const char **argv, const char *prefix)\n {\n \tstruct rebase_options options = REBASE_OPTIONS_INIT;\ndiff --git a/sequencer.c b/sequencer.c\nindex dde1f6b30f..8efe0976fd 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -32,6 +32,7 @@\n #include \"alias.h\"\n #include \"commit-reach.h\"\n #include \"rebase-interactive.h\"\n+#include \"reset.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -3649,6 +3650,55 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n+void create_autostash(struct repository *r, const char *path,\n+\t\t      const char *default_reflog_action)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct lock_file lock_file = LOCK_INIT;\n+\tint fd;\n+\n+\tfd = repo_hold_locked_index(r, &lock_file, 0);\n+\trefresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);\n+\tif (0 <= fd)\n+\t\trepo_update_index_if_able(r, &lock_file);\n+\trollback_lock_file(&lock_file);\n+\n+\tif (has_unstaged_changes(r, 1) ||\n+\t    has_uncommitted_changes(r, 1)) {\n+\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n+\t\tstruct object_id oid;\n+\n+\t\targv_array_pushl(&stash.args,\n+\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n+\t\tstash.git_cmd = 1;\n+\t\tstash.no_stdin = 1;\n+\t\tstrbuf_reset(&buf);\n+\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n+\t\t\tdie(_(\"Cannot autostash\"));\n+\t\tstrbuf_trim_trailing_newline(&buf);\n+\t\tif (get_oid(buf.buf, &oid))\n+\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n+\t\t\t    buf.buf);\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n+\n+\t\tif (safe_create_leading_directories_const(path))\n+\t\t\tdie(_(\"Could not create directory for '%s'\"),\n+\t\t\t    path);\n+\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n+\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n+\t\tif (reset_head(r, NULL, \"reset --hard\",\n+\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n+\t\t\t       default_reflog_action) < 0)\n+\t\t\tdie(_(\"could not reset --hard\"));\n+\n+\t\tif (discard_index(r->index) < 0 ||\n+\t\t\trepo_read_index(r) < 0)\n+\t\t\tdie(_(\"could not read index\"));\n+\t}\n+\tstrbuf_release(&buf);\n+}\n+\n int apply_autostash(const char *path)\n {\n \tstruct strbuf stash_oid = STRBUF_INIT;\ndiff --git a/sequencer.h b/sequencer.h\nindex d6ba62a089..cf1284f9ed 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,\n \t\t\t const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n \n+void create_autostash(struct repository *r, const char *path,\n+\t\t      const char *default_reflog_action);\n int apply_autostash(const char *path);\n \n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394751","messageId":"e703022fda836f2fd6856e8d5861e2264af4d038.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 18/23] sequencer: unlink autostash in apply_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:31Z","receivedAt":"2020-04-04T01:12:22Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Explicitly remove autostash file in apply_autostash() once it has been\napplied successfully.\n\nThis is currently a no-op because the only users of this function will unlink\nthe state (including the autostash file) after this function runs.\nHowever, in the future, we will introduce a user of the function that\ndoes not explicitly remove the state so we do it here.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 8efe0976fd..5dd1c2438e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3740,6 +3740,7 @@ int apply_autostash(const char *path)\n \t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"));\n \t}\n \n+\tunlink(path);\n \tstrbuf_release(&stash_oid);\n \treturn ret;\n }\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394752","messageId":"598ddea6c1187d78f876136974822e65bbec146b.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 20/23] sequencer: implement apply_autostash_oid()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:33Z","receivedAt":"2020-04-04T01:12:23Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Split apply_save_autostash() into apply_autostash_oid() and\napply_save_autostash() where the former operates on an OID string and\nthe latter reads the OID from a file before passing it into\napply_save_autostash_oid().\n\nThis function is required for a future commmit which will rely on being\nable to apply an autostash whose OID is stored as a string.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 38 ++++++++++++++++++++++++++------------\n sequencer.h |  1 +\n 2 files changed, 27 insertions(+), 12 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex fb52583bc2..b4cab9632a 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3699,26 +3699,18 @@ void create_autostash(struct repository *r, const char *path,\n \tstrbuf_release(&buf);\n }\n \n-static int apply_save_autostash(const char *path, int attempt_apply)\n+static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)\n {\n-\tstruct strbuf stash_oid = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\n \tint ret = 0;\n \n-\tif (!read_oneliner(&stash_oid, path,\n-\t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n-\t\tstrbuf_release(&stash_oid);\n-\t\treturn 0;\n-\t}\n-\tstrbuf_trim(&stash_oid);\n-\n \tif (attempt_apply) {\n \t\tchild.git_cmd = 1;\n \t\tchild.no_stdout = 1;\n \t\tchild.no_stderr = 1;\n \t\targv_array_push(&child.args, \"stash\");\n \t\targv_array_push(&child.args, \"apply\");\n-\t\targv_array_push(&child.args, stash_oid.buf);\n+\t\targv_array_push(&child.args, stash_oid);\n \t\tret = run_command(&child);\n \t}\n \n@@ -3733,9 +3725,9 @@ static int apply_save_autostash(const char *path, int attempt_apply)\n \t\targv_array_push(&store.args, \"-m\");\n \t\targv_array_push(&store.args, \"autostash\");\n \t\targv_array_push(&store.args, \"-q\");\n-\t\targv_array_push(&store.args, stash_oid.buf);\n+\t\targv_array_push(&store.args, stash_oid);\n \t\tif (run_command(&store))\n-\t\t\tret = error(_(\"cannot store %s\"), stash_oid.buf);\n+\t\t\tret = error(_(\"cannot store %s\"), stash_oid);\n \t\telse\n \t\t\tfprintf(stderr,\n \t\t\t\t_(\"%s\\n\"\n@@ -3747,6 +3739,23 @@ static int apply_save_autostash(const char *path, int attempt_apply)\n \t\t\t\t_(\"Autostash exists; creating a new stash entry.\"));\n \t}\n \n+\treturn ret;\n+}\n+\n+static int apply_save_autostash(const char *path, int attempt_apply)\n+{\n+\tstruct strbuf stash_oid = STRBUF_INIT;\n+\tint ret = 0;\n+\n+\tif (!read_oneliner(&stash_oid, path,\n+\t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n+\t\tstrbuf_release(&stash_oid);\n+\t\treturn 0;\n+\t}\n+\tstrbuf_trim(&stash_oid);\n+\n+\tret = apply_save_autostash_oid(stash_oid.buf, attempt_apply);\n+\n \tunlink(path);\n \tstrbuf_release(&stash_oid);\n \treturn ret;\n@@ -3762,6 +3771,11 @@ int apply_autostash(const char *path)\n \treturn apply_save_autostash(path, 1);\n }\n \n+int apply_autostash_oid(const char *stash_oid)\n+{\n+\treturn apply_save_autostash_oid(stash_oid, 1);\n+}\n+\n static const char *reflog_message(struct replay_opts *opts,\n \tconst char *sub_action, const char *fmt, ...)\n {\ndiff --git a/sequencer.h b/sequencer.h\nindex 2d09c1ac0b..a90b768b74 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -195,6 +195,7 @@ void create_autostash(struct repository *r, const char *path,\n \t\t      const char *default_reflog_action);\n int save_autostash(const char *path);\n int apply_autostash(const char *path);\n+int apply_autostash_oid(const char *stash_oid);\n \n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394753","messageId":"045668620dc5173fddb899f38a976059c84b5465.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 14/23] reset: extract reset_head() from rebase","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:27Z","receivedAt":"2020-04-04T01:12:24Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Continue the process of lib-ifying the autostash code. In a future\ncommit, this will be used to implement `--autostash` in other builtins.\n\nThis patch is best viewed with `--color-moved`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Makefile         |   1 +\n builtin/rebase.c | 139 +---------------------------------------------\n reset.c          | 140 +++++++++++++++++++++++++++++++++++++++++++++++\n reset.h          |  20 +++++++\n 4 files changed, 162 insertions(+), 138 deletions(-)\n create mode 100644 reset.c\n create mode 100644 reset.h\n\ndiff --git a/Makefile b/Makefile\nindex f709fc3f77..338fb55b73 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -970,6 +970,7 @@ LIB_OBJS += replace-object.o\n LIB_OBJS += repo-settings.o\n LIB_OBJS += repository.o\n LIB_OBJS += rerere.o\n+LIB_OBJS += reset.o\n LIB_OBJS += resolve-undo.o\n LIB_OBJS += revision.o\n LIB_OBJS += run-command.o\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 0f650a798e..55730f6360 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -27,6 +27,7 @@\n #include \"branch.h\"\n #include \"sequencer.h\"\n #include \"rebase-interactive.h\"\n+#include \"reset.h\"\n \n #define DEFAULT_REFLOG_ACTION \"rebase\"\n \n@@ -761,144 +762,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)\n \t}\n }\n \n-#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n-\n-#define RESET_HEAD_DETACH (1<<0)\n-#define RESET_HEAD_HARD (1<<1)\n-#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n-#define RESET_HEAD_REFS_ONLY (1<<3)\n-#define RESET_ORIG_HEAD (1<<4)\n-\n-static int reset_head(struct repository *r, struct object_id *oid, const char *action,\n-\t\t      const char *switch_to_branch, unsigned flags,\n-\t\t      const char *reflog_orig_head, const char *reflog_head,\n-\t\t      const char *default_reflog_action)\n-{\n-\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n-\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n-\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n-\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n-\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n-\tstruct object_id head_oid;\n-\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n-\tstruct lock_file lock = LOCK_INIT;\n-\tstruct unpack_trees_options unpack_tree_opts;\n-\tstruct tree *tree;\n-\tconst char *reflog_action;\n-\tstruct strbuf msg = STRBUF_INIT;\n-\tsize_t prefix_len;\n-\tstruct object_id *orig = NULL, oid_orig,\n-\t\t*old_orig = NULL, oid_old_orig;\n-\tint ret = 0, nr = 0;\n-\n-\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n-\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n-\n-\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n-\t\tret = -1;\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n-\t\tret = error(_(\"could not determine HEAD revision\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!oid)\n-\t\toid = &head_oid;\n-\n-\tif (refs_only)\n-\t\tgoto reset_head_refs;\n-\n-\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n-\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n-\tunpack_tree_opts.head_idx = 1;\n-\tunpack_tree_opts.src_index = r->index;\n-\tunpack_tree_opts.dst_index = r->index;\n-\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n-\tunpack_tree_opts.update = 1;\n-\tunpack_tree_opts.merge = 1;\n-\tif (!detach_head)\n-\t\tunpack_tree_opts.reset = 1;\n-\n-\tif (repo_read_index_unmerged(r) < 0) {\n-\t\tret = error(_(\"could not read index\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n-\t\tret = error(_(\"failed to find tree of %s\"),\n-\t\t\t    oid_to_hex(&head_oid));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n-\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n-\t\tret = -1;\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\ttree = parse_tree_indirect(oid);\n-\tprime_cache_tree(r, r->index, tree);\n-\n-\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n-\t\tret = error(_(\"could not write index\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-reset_head_refs:\n-\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n-\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n-\tprefix_len = msg.len;\n-\n-\tif (update_orig_head) {\n-\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n-\t\t\told_orig = &oid_old_orig;\n-\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n-\t\t\torig = &oid_orig;\n-\t\t\tif (!reflog_orig_head) {\n-\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n-\t\t\t\treflog_orig_head = msg.buf;\n-\t\t\t}\n-\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n-\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n-\t\t} else if (old_orig)\n-\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n-\t}\n-\n-\tif (!reflog_head) {\n-\t\tstrbuf_setlen(&msg, prefix_len);\n-\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n-\t\treflog_head = msg.buf;\n-\t}\n-\tif (!switch_to_branch)\n-\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n-\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n-\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n-\telse {\n-\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n-\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n-\t\tif (!ret)\n-\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n-\t\t\t\t\t    reflog_head);\n-\t}\n-\tif (run_hook)\n-\t\trun_hook_le(NULL, \"post-checkout\",\n-\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n-\t\t\t    oid_to_hex(oid), \"1\", NULL);\n-\n-leave_reset_head:\n-\tstrbuf_release(&msg);\n-\trollback_lock_file(&lock);\n-\twhile (nr)\n-\t\tfree((void *)desc[--nr].buffer);\n-\treturn ret;\n-}\n-\n static int move_to_original_branch(struct rebase_options *opts)\n {\n \tstruct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;\ndiff --git a/reset.c b/reset.c\nnew file mode 100644\nindex 0000000000..79b683bffa\n--- /dev/null\n+++ b/reset.c\n@@ -0,0 +1,140 @@\n+#include \"git-compat-util.h\"\n+#include \"cache-tree.h\"\n+#include \"lockfile.h\"\n+#include \"refs.h\"\n+#include \"reset.h\"\n+#include \"run-command.h\"\n+#include \"tree-walk.h\"\n+#include \"tree.h\"\n+#include \"unpack-trees.h\"\n+\n+int reset_head(struct repository *r, struct object_id *oid, const char *action,\n+\t       const char *switch_to_branch, unsigned flags,\n+\t       const char *reflog_orig_head, const char *reflog_head,\n+\t       const char *default_reflog_action)\n+{\n+\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n+\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n+\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n+\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n+\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n+\tstruct object_id head_oid;\n+\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n+\tstruct lock_file lock = LOCK_INIT;\n+\tstruct unpack_trees_options unpack_tree_opts;\n+\tstruct tree *tree;\n+\tconst char *reflog_action;\n+\tstruct strbuf msg = STRBUF_INIT;\n+\tsize_t prefix_len;\n+\tstruct object_id *orig = NULL, oid_orig,\n+\t\t*old_orig = NULL, oid_old_orig;\n+\tint ret = 0, nr = 0;\n+\n+\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n+\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n+\n+\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n+\t\tret = -1;\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n+\t\tret = error(_(\"could not determine HEAD revision\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!oid)\n+\t\toid = &head_oid;\n+\n+\tif (refs_only)\n+\t\tgoto reset_head_refs;\n+\n+\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n+\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n+\tunpack_tree_opts.head_idx = 1;\n+\tunpack_tree_opts.src_index = r->index;\n+\tunpack_tree_opts.dst_index = r->index;\n+\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n+\tunpack_tree_opts.update = 1;\n+\tunpack_tree_opts.merge = 1;\n+\tif (!detach_head)\n+\t\tunpack_tree_opts.reset = 1;\n+\n+\tif (repo_read_index_unmerged(r) < 0) {\n+\t\tret = error(_(\"could not read index\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n+\t\tret = error(_(\"failed to find tree of %s\"),\n+\t\t\t    oid_to_hex(&head_oid));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n+\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n+\t\tret = -1;\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\ttree = parse_tree_indirect(oid);\n+\tprime_cache_tree(r, r->index, tree);\n+\n+\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n+\t\tret = error(_(\"could not write index\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+reset_head_refs:\n+\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n+\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n+\tprefix_len = msg.len;\n+\n+\tif (update_orig_head) {\n+\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n+\t\t\told_orig = &oid_old_orig;\n+\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n+\t\t\torig = &oid_orig;\n+\t\t\tif (!reflog_orig_head) {\n+\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n+\t\t\t\treflog_orig_head = msg.buf;\n+\t\t\t}\n+\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n+\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n+\t\t} else if (old_orig)\n+\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n+\t}\n+\n+\tif (!reflog_head) {\n+\t\tstrbuf_setlen(&msg, prefix_len);\n+\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n+\t\treflog_head = msg.buf;\n+\t}\n+\tif (!switch_to_branch)\n+\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n+\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n+\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n+\telse {\n+\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n+\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n+\t\tif (!ret)\n+\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n+\t\t\t\t\t    reflog_head);\n+\t}\n+\tif (run_hook)\n+\t\trun_hook_le(NULL, \"post-checkout\",\n+\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n+\t\t\t    oid_to_hex(oid), \"1\", NULL);\n+\n+leave_reset_head:\n+\tstrbuf_release(&msg);\n+\trollback_lock_file(&lock);\n+\twhile (nr)\n+\t\tfree((void *)desc[--nr].buffer);\n+\treturn ret;\n+\n+}\ndiff --git a/reset.h b/reset.h\nnew file mode 100644\nindex 0000000000..12f83c78e2\n--- /dev/null\n+++ b/reset.h\n@@ -0,0 +1,20 @@\n+#ifndef RESET_H\n+#define RESET_H\n+\n+#include \"hash.h\"\n+#include \"repository.h\"\n+\n+#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n+\n+#define RESET_HEAD_DETACH (1<<0)\n+#define RESET_HEAD_HARD (1<<1)\n+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n+#define RESET_HEAD_REFS_ONLY (1<<3)\n+#define RESET_ORIG_HEAD (1<<4)\n+\n+int reset_head(struct repository *r, struct object_id *oid, const char *action,\n+\t       const char *switch_to_branch, unsigned flags,\n+\t       const char *reflog_orig_head, const char *reflog_head,\n+\t       const char *default_reflog_action);\n+\n+#endif\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394754","messageId":"f1e54622fbd9e7a3e94fa97d758e39c01b97b1bc.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 22/23] t5520: make test_pull_autostash() accept expect_parent_num","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:35Z","receivedAt":"2020-04-04T01:12:26Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Before, test_pull_autostash() was hardcoded to run\n`test_cmp_rev HEAD^ copy` to test that a rebase happened. However, in a\nfuture patch, we plan on testing merging as well. Make\ntest_pull_autostash() accept a parent number as an argument so that, in\nthe future, we can test if a merge happened in addition to a rebase.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t5520-pull.sh | 14 ++++++++------\n 1 file changed, 8 insertions(+), 6 deletions(-)\n\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 2f86fca042..f610dc14de 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -10,11 +10,13 @@ modify () {\n }\n \n test_pull_autostash () {\n+\texpect_parent_num=\"$1\" &&\n+\tshift &&\n \tgit reset --hard before-rebase &&\n \techo dirty >new_file &&\n \tgit add new_file &&\n \tgit pull \"$@\" . copy &&\n-\ttest_cmp_rev HEAD^ copy &&\n+\ttest_cmp_rev HEAD^\"$expect_parent_num\" copy &&\n \techo dirty >expect &&\n \ttest_cmp expect new_file &&\n \techo \"modified again\" >expect &&\n@@ -369,22 +371,22 @@ test_expect_success '--rebase fails with multiple branches' '\n \n test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' '\n \ttest_config rebase.autostash true &&\n-\ttest_pull_autostash --rebase\n+\ttest_pull_autostash 1 --rebase\n '\n \n test_expect_success 'pull --rebase --autostash & rebase.autostash=true' '\n \ttest_config rebase.autostash true &&\n-\ttest_pull_autostash --rebase --autostash\n+\ttest_pull_autostash 1 --rebase --autostash\n '\n \n test_expect_success 'pull --rebase --autostash & rebase.autostash=false' '\n \ttest_config rebase.autostash false &&\n-\ttest_pull_autostash --rebase --autostash\n+\ttest_pull_autostash 1 --rebase --autostash\n '\n \n test_expect_success 'pull --rebase --autostash & rebase.autostash unset' '\n \ttest_unconfig rebase.autostash &&\n-\ttest_pull_autostash --rebase --autostash\n+\ttest_pull_autostash 1 --rebase --autostash\n '\n \n test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '\n@@ -422,7 +424,7 @@ test_expect_success 'pull.rebase' '\n \n test_expect_success 'pull --autostash & pull.rebase=true' '\n \ttest_config pull.rebase true &&\n-\ttest_pull_autostash --autostash\n+\ttest_pull_autostash 1 --autostash\n '\n \n test_expect_success 'pull --no-autostash & pull.rebase=true' '\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394755","messageId":"75dc3f10a1aac24007b660a02ce829c8702ffd0e.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 19/23] sequencer: implement save_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:32Z","receivedAt":"2020-04-04T01:12:27Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Extract common functionality of apply_autostash() into\napply_save_autostash() and use it to implement save_autostash(). This\nfunction will be used in a future commit.\n\nThe difference between save_autostash() and apply_autostash() is that\nthe latter does not try to apply the stash. It skips that step and\njust stores the created entry in the stash reflog.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 37 +++++++++++++++++++++++++++----------\n sequencer.h |  1 +\n 2 files changed, 28 insertions(+), 10 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 5dd1c2438e..fb52583bc2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3699,7 +3699,7 @@ void create_autostash(struct repository *r, const char *path,\n \tstrbuf_release(&buf);\n }\n \n-int apply_autostash(const char *path)\n+static int apply_save_autostash(const char *path, int attempt_apply)\n {\n \tstruct strbuf stash_oid = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\n@@ -3712,13 +3712,17 @@ int apply_autostash(const char *path)\n \t}\n \tstrbuf_trim(&stash_oid);\n \n-\tchild.git_cmd = 1;\n-\tchild.no_stdout = 1;\n-\tchild.no_stderr = 1;\n-\targv_array_push(&child.args, \"stash\");\n-\targv_array_push(&child.args, \"apply\");\n-\targv_array_push(&child.args, stash_oid.buf);\n-\tif (!run_command(&child))\n+\tif (attempt_apply) {\n+\t\tchild.git_cmd = 1;\n+\t\tchild.no_stdout = 1;\n+\t\tchild.no_stderr = 1;\n+\t\targv_array_push(&child.args, \"stash\");\n+\t\targv_array_push(&child.args, \"apply\");\n+\t\targv_array_push(&child.args, stash_oid.buf);\n+\t\tret = run_command(&child);\n+\t}\n+\n+\tif (attempt_apply && !ret)\n \t\tfprintf(stderr, _(\"Applied autostash.\\n\"));\n \telse {\n \t\tstruct child_process store = CHILD_PROCESS_INIT;\n@@ -3734,10 +3738,13 @@ int apply_autostash(const char *path)\n \t\t\tret = error(_(\"cannot store %s\"), stash_oid.buf);\n \t\telse\n \t\t\tfprintf(stderr,\n-\t\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n+\t\t\t\t_(\"%s\\n\"\n \t\t\t\t  \"Your changes are safe in the stash.\\n\"\n \t\t\t\t  \"You can run \\\"git stash pop\\\" or\"\n-\t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"));\n+\t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"),\n+\t\t\t\tattempt_apply ?\n+\t\t\t\t_(\"Applying autostash resulted in conflicts.\") :\n+\t\t\t\t_(\"Autostash exists; creating a new stash entry.\"));\n \t}\n \n \tunlink(path);\n@@ -3745,6 +3752,16 @@ int apply_autostash(const char *path)\n \treturn ret;\n }\n \n+int save_autostash(const char *path)\n+{\n+\treturn apply_save_autostash(path, 0);\n+}\n+\n+int apply_autostash(const char *path)\n+{\n+\treturn apply_save_autostash(path, 1);\n+}\n+\n static const char *reflog_message(struct replay_opts *opts,\n \tconst char *sub_action, const char *fmt, ...)\n {\ndiff --git a/sequencer.h b/sequencer.h\nindex cf1284f9ed..2d09c1ac0b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -193,6 +193,7 @@ void commit_post_rewrite(struct repository *r,\n \n void create_autostash(struct repository *r, const char *path,\n \t\t      const char *default_reflog_action);\n+int save_autostash(const char *path);\n int apply_autostash(const char *path);\n \n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394756","messageId":"177c7e537b1d853f34719882702e1f39d06e5a16.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 23/23] pull: pass --autostash to merge","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:36Z","receivedAt":"2020-04-04T01:12:28Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Before, `--autostash` only worked with `git pull --rebase`. However, in\nthe last patch, merge learned `--autostash` as well so there's no reason\nwhy we should have this restriction anymore. Teach pull to pass\n`--autostash` to merge, just like it did for rebase.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-pull.txt      |  9 -------\n Documentation/merge-options.txt |  4 +--\n builtin/pull.c                  |  9 ++++---\n t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------\n 4 files changed, 42 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt\nindex dfb901f8b8..ba3772de9f 100644\n--- a/Documentation/git-pull.txt\n+++ b/Documentation/git-pull.txt\n@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.\n --no-rebase::\n \tOverride earlier --rebase.\n \n---autostash::\n---no-autostash::\n-\tBefore starting rebase, stash local modifications away (see\n-\tlinkgit:git-stash[1]) if needed, and apply the stash entry when\n-\tdone. `--no-autostash` is useful to override the `rebase.autoStash`\n-\tconfiguration variable (see linkgit:git-config[1]).\n-+\n-This option is only valid when \"--rebase\" is used.\n-\n Options related to fetching\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n \ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 3985e6d4a9..48bfcda084 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -155,6 +155,8 @@ ifndef::git-pull[]\n \tNote that not all merge strategies may support progress\n \treporting.\n \n+endif::git-pull[]\n+\n --autostash::\n --no-autostash::\n \tAutomatically create a temporary stash entry before the operation\n@@ -163,8 +165,6 @@ ifndef::git-pull[]\n \twith care: the final stash application after a successful\n \tmerge might result in non-trivial conflicts.\n \n-endif::git-pull[]\n-\n --allow-unrelated-histories::\n \tBy default, `git merge` command refuses to merge histories\n \tthat do not share a common ancestor.  This option can be\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 3e624d1e00..9beb4841d1 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -163,7 +163,7 @@ static struct option pull_options[] = {\n \t\tN_(\"verify that the named commit has a valid GPG signature\"),\n \t\tPARSE_OPT_NOARG),\n \tOPT_BOOL(0, \"autostash\", &opt_autostash,\n-\t\tN_(\"automatically stash/stash pop before and after rebase\")),\n+\t\tN_(\"automatically stash/stash pop before and after\")),\n \tOPT_PASSTHRU_ARGV('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n \t\tN_(\"merge strategy to use\"),\n \t\t0),\n@@ -661,6 +661,10 @@ static int run_merge(void)\n \targv_array_pushv(&args, opt_strategy_opts.argv);\n \tif (opt_gpg_sign)\n \t\targv_array_push(&args, opt_gpg_sign);\n+\tif (opt_autostash == 0)\n+\t\targv_array_push(&args, \"--no-autostash\");\n+\telse if (opt_autostash == 1)\n+\t\targv_array_push(&args, \"--autostash\");\n \tif (opt_allow_unrelated_histories > 0)\n \t\targv_array_push(&args, \"--allow-unrelated-histories\");\n \n@@ -908,9 +912,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (get_oid(\"HEAD\", &orig_head))\n \t\toidclr(&orig_head);\n \n-\tif (!opt_rebase && opt_autostash != -1)\n-\t\tdie(_(\"--[no-]autostash option is only valid with --rebase.\"));\n-\n \tautostash = config_autostash;\n \tif (opt_rebase) {\n \t\tif (opt_autostash != -1)\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex f610dc14de..37535d63a9 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -28,7 +28,7 @@ test_pull_autostash_fail () {\n \techo dirty >new_file &&\n \tgit add new_file &&\n \ttest_must_fail git pull \"$@\" . copy 2>err &&\n-\ttest_i18ngrep \"uncommitted changes.\" err\n+\ttest_i18ngrep \"\\(uncommitted changes.\\)\\|\\(overwritten by merge:\\)\" err\n }\n \n test_expect_success setup '\n@@ -404,13 +404,40 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '\n \ttest_pull_autostash_fail --rebase --no-autostash\n '\n \n-for i in --autostash --no-autostash\n-do\n-\ttest_expect_success \"pull $i (without --rebase) is illegal\" '\n-\t\ttest_must_fail git pull $i . copy 2>err &&\n-\t\ttest_i18ngrep \"only valid with --rebase\" err\n-\t'\n-done\n+test_expect_success 'pull succeeds with dirty working directory and merge.autostash set' '\n+\ttest_config merge.autostash true &&\n+\ttest_pull_autostash 2\n+'\n+\n+test_expect_success 'pull --autostash & merge.autostash=true' '\n+\ttest_config merge.autostash true &&\n+\ttest_pull_autostash 2 --autostash\n+'\n+\n+test_expect_success 'pull --autostash & merge.autostash=false' '\n+\ttest_config merge.autostash false &&\n+\ttest_pull_autostash 2 --autostash\n+'\n+\n+test_expect_success 'pull --autostash & merge.autostash unset' '\n+\ttest_unconfig merge.autostash &&\n+\ttest_pull_autostash 2 --autostash\n+'\n+\n+test_expect_success 'pull --no-autostash & merge.autostash=true' '\n+\ttest_config merge.autostash true &&\n+\ttest_pull_autostash_fail --no-autostash\n+'\n+\n+test_expect_success 'pull --no-autostash & merge.autostash=false' '\n+\ttest_config merge.autostash false &&\n+\ttest_pull_autostash_fail --no-autostash\n+'\n+\n+test_expect_success 'pull --no-autostash & merge.autostash unset' '\n+\ttest_unconfig merge.autostash &&\n+\ttest_pull_autostash_fail --no-autostash\n+'\n \n test_expect_success 'pull.rebase' '\n \tgit reset --hard before-rebase &&\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394757","messageId":"e0b8e409afbf8d90b4fc6406a67a1a1e930e022f.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 10/23] sequencer: make apply_rebase() accept a path","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:23Z","receivedAt":"2020-04-04T01:12:29Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In order to make apply_rebase() more generic for future extraction, make\nit accept a `path` argument so that the location from where to read the\nreference to the autostash commit can be customized. Remove the `opts`\nargument since it was unused before anyway.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 14 +++++++-------\n 1 file changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex d1d59867ae..ae9efe5825 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3649,13 +3649,13 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n-static int apply_autostash(struct replay_opts *opts)\n+static int apply_autostash(const char *path)\n {\n \tstruct strbuf stash_sha1 = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\n \tint ret = 0;\n \n-\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(),\n+\tif (!read_oneliner(&stash_sha1, path,\n \t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\tstrbuf_release(&stash_sha1);\n \t\treturn 0;\n@@ -3748,7 +3748,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,\n \t\treturn error(_(\"%s: not a valid OID\"), orig_head);\n \n \tif (run_git_checkout(r, opts, oid_to_hex(onto), action)) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \t\treturn error(_(\"could not detach HEAD\"));\n \t}\n@@ -4061,7 +4061,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\trun_command(&hook);\n \t\t\t}\n \t\t}\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \n \t\tif (!opts->quiet) {\n \t\t\tif (!opts->verbose)\n@@ -5070,7 +5070,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\ttodo_list_add_exec_commands(todo_list, commands);\n \n \tif (count_commands(todo_list) == 0) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \n \t\treturn error(_(\"nothing to do\"));\n@@ -5081,12 +5081,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (res == -1)\n \t\treturn -1;\n \telse if (res == -2) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \n \t\treturn -1;\n \t} else if (res == -3) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \t\ttodo_list_release(&new_todo);\n \n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394758","messageId":"7adf7941921065fa1a5e68524e2dd3cd28f9a12b.1585962673.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 21/23] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:34Z","receivedAt":"2020-04-04T01:12:29Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In rebase, one can pass the `--autostash` option to cause the worktree\nto be automatically stashed before continuing with the rebase. This\noption is missing in merge, however.\n\nImplement the `--autostash` option and corresponding `merge.autoStash`\noption in merge which stashes before merging and then pops after.\n\nThis option is useful when a developer has some local changes on a topic\nbranch but they realize that their work depends on another branch.\nPreviously, they had to run something like\n\n\tgit fetch ...\n\tgit stash push\n\tgit merge FETCH_HEAD\n\tgit stash pop\n\nbut now, that is reduced to\n\n\tgit fetch ...\n\tgit merge --autostash FETCH_HEAD\n\nWhen an autostash is generated, it is automatically reapplied to the\nworktree only in three explicit situations:\n\n\t1. An incomplete merge is commit using `git commit`.\n\t2. A merge completes successfully.\n\t3. A merge is aborted using `git merge --abort`.\n\nIn all other situations where the merge state is removed using\nremove_merge_branch_state() such as aborting a merge via\n`git reset --hard`, the autostash is saved into the stash reflog\ninstead keeping the worktree clean.\n\nSuggested-by: Alban Gruin <alban.gruin@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/merge.txt  |  10 +++\n Documentation/git-merge.txt     |   6 +-\n Documentation/merge-options.txt |   8 +++\n branch.c                        |   1 +\n builtin/commit.c                |   2 +\n builtin/merge.c                 |  27 ++++++++\n builtin/rebase.c                |   3 +-\n builtin/reset.c                 |   4 +-\n parse-options.h                 |   1 +\n path.c                          |   1 +\n path.h                          |   4 +-\n t/t3033-merge-toplevel.sh       |  22 ++++++\n t/t7600-merge.sh                | 118 ++++++++++++++++++++++++++++++++\n 13 files changed, 201 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex 6a313937f8..cb2ed58907 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -70,6 +70,16 @@ merge.stat::\n \tWhether to print the diffstat between ORIG_HEAD and the merge result\n \tat the end of the merge.  True by default.\n \n+merge.autoStash::\n+\tWhen set to true, automatically create a temporary stash entry\n+\tbefore the operation begins, and apply it after the operation\n+\tends.  This means that you can run merge on a dirty worktree.\n+\tHowever, use with care: the final stash application after a\n+\tsuccessful merge might result in non-trivial conflicts.\n+\tThis option can be overridden by the `--no-autostash` and\n+\t`--autostash` options of linkgit:git-merge[1].\n+\tDefaults to false.\n+\n merge.tool::\n \tControls which merge tool is used by linkgit:git-mergetool[1].\n \tThe list below shows the valid built-in values.\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 092529c619..9cb14f0059 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -94,7 +94,8 @@ will be appended to the specified message.\n \n --abort::\n \tAbort the current conflict resolution process, and\n-\ttry to reconstruct the pre-merge state.\n+\ttry to reconstruct the pre-merge state. If an autostash entry is\n+\tpresent, apply it back to the worktree.\n +\n If there were uncommitted worktree changes present when the merge\n started, 'git merge --abort' will in some cases be unable to\n@@ -102,7 +103,8 @@ reconstruct these changes. It is therefore recommended to always\n commit or stash your changes before running 'git merge'.\n +\n 'git merge --abort' is equivalent to 'git reset --merge' when\n-`MERGE_HEAD` is present.\n+`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present where\n+the stash entry is applied to the worktree.\n \n --quit::\n \tForget about the current merge in progress. Leave the index\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 40dc4f5e8c..3985e6d4a9 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -155,6 +155,14 @@ ifndef::git-pull[]\n \tNote that not all merge strategies may support progress\n \treporting.\n \n+--autostash::\n+--no-autostash::\n+\tAutomatically create a temporary stash entry before the operation\n+\tbegins, and apply it after the operation ends.  This means\n+\tthat you can run the operation on a dirty worktree.  However, use\n+\twith care: the final stash application after a successful\n+\tmerge might result in non-trivial conflicts.\n+\n endif::git-pull[]\n \n --allow-unrelated-histories::\ndiff --git a/branch.c b/branch.c\nindex 579494738a..2d9e7675a6 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)\n \tunlink(git_path_merge_rr(r));\n \tunlink(git_path_merge_msg(r));\n \tunlink(git_path_merge_mode(r));\n+\tsave_autostash(git_path_merge_autostash(r));\n }\n \n void remove_branch_state(struct repository *r, int verbose)\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 7ba33a3bec..c1b25d2954 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1713,6 +1713,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\t\t\t     &oid, flags);\n \t}\n \n+\tapply_autostash(git_path_merge_autostash(the_repository));\n+\n \tUNLEAK(err);\n \tUNLEAK(sb);\n \treturn 0;\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex d127d2225f..f2f8069f85 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -82,6 +82,7 @@ static int show_progress = -1;\n static int default_to_upstream = 1;\n static int signoff;\n static const char *sign_commit;\n+static int autostash;\n static int no_verify;\n \n static struct strategy all_strategy[] = {\n@@ -286,6 +287,7 @@ static struct option builtin_merge_options[] = {\n \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n+\tOPT_AUTOSTASH(&autostash),\n \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n@@ -475,6 +477,7 @@ static void finish(struct commit *head_commit,\n \t/* Run a post-merge hook */\n \trun_hook_le(NULL, \"post-merge\", squash ? \"1\" : \"0\", NULL);\n \n+\tapply_autostash(git_path_merge_autostash(the_repository));\n \tstrbuf_release(&reflog_message);\n }\n \n@@ -634,6 +637,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn 0;\n \t} else if (!strcmp(k, \"gpg.mintrustlevel\")) {\n \t\tcheck_trust_level = 0;\n+\t} else if (!strcmp(k, \"merge.autostash\")) {\n+\t\tautostash = git_config_bool(k, v);\n+\t\treturn 0;\n \t}\n \n \tstatus = fmt_merge_msg_config(k, v, cb);\n@@ -1281,6 +1287,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (abort_current_merge) {\n \t\tint nargc = 2;\n \t\tconst char *nargv[] = {\"reset\", \"--merge\", NULL};\n+\t\tstruct strbuf stash_oid = STRBUF_INIT;\n \n \t\tif (orig_argc != 2)\n \t\t\tusage_msg_opt(_(\"--abort expects no arguments\"),\n@@ -1289,8 +1296,19 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\tif (!file_exists(git_path_merge_head(the_repository)))\n \t\t\tdie(_(\"There is no merge to abort (MERGE_HEAD missing).\"));\n \n+\t\tif (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),\n+\t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n+\t\t\tstrbuf_trim(&stash_oid);\n+\t\t\tunlink(git_path_merge_autostash(the_repository));\n+\t\t}\n+\n \t\t/* Invoke 'git reset --merge' */\n \t\tret = cmd_reset(nargc, nargv, prefix);\n+\n+\t\tif (stash_oid.len)\n+\t\t\tapply_autostash_oid(stash_oid.buf);\n+\n+\t\tstrbuf_release(&stash_oid);\n \t\tgoto done;\n \t}\n \n@@ -1513,6 +1531,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\tgoto done;\n \t\t}\n \n+\t\tif (autostash)\n+\t\t\tcreate_autostash(the_repository,\n+\t\t\t\t\t git_path_merge_autostash(the_repository),\n+\t\t\t\t\t \"merge\");\n \t\tif (checkout_fast_forward(the_repository,\n \t\t\t\t\t  &head_commit->object.oid,\n \t\t\t\t\t  &commit->object.oid,\n@@ -1579,6 +1601,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (fast_forward == FF_ONLY)\n \t\tdie(_(\"Not possible to fast-forward, aborting.\"));\n \n+\tif (autostash)\n+\t\tcreate_autostash(the_repository,\n+\t\t\t\t git_path_merge_autostash(the_repository),\n+\t\t\t\t \"merge\");\n+\n \t/* We are going to make a new commit. */\n \tgit_committer_info(IDENT_STRICT);\n \ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex b800c8bfca..e681ff58de 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1371,8 +1371,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_STRING, 'S', \"gpg-sign\", &gpg_sign, N_(\"key-id\"),\n \t\t\tN_(\"GPG-sign commits\"),\n \t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n-\t\tOPT_BOOL(0, \"autostash\", &options.autostash,\n-\t\t\t N_(\"automatically stash/stash pop before and after\")),\n+\t\tOPT_AUTOSTASH(&options.autostash),\n \t\tOPT_STRING_LIST('x', \"exec\", &exec, N_(\"exec\"),\n \t\t\t\tN_(\"add exec lines after each commit of the \"\n \t\t\t\t   \"editable list\")),\ndiff --git a/builtin/reset.c b/builtin/reset.c\nindex 18228c312e..0d155101f4 100644\n--- a/builtin/reset.c\n+++ b/builtin/reset.c\n@@ -25,6 +25,7 @@\n #include \"cache-tree.h\"\n #include \"submodule.h\"\n #include \"submodule-config.h\"\n+#include \"sequencer.h\"\n \n #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n \n@@ -437,8 +438,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \t\tif (reset_type == HARD && !update_ref_status && !quiet)\n \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n \t}\n-\tif (!pathspec.nr)\n+\tif (!pathspec.nr) {\n \t\tremove_branch_state(the_repository, 0);\n+\t}\n \n \treturn update_ref_status;\n }\ndiff --git a/parse-options.h b/parse-options.h\nindex fece5ba628..46af942093 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -336,5 +336,6 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);\n #define OPT_CLEANUP(v) OPT_STRING(0, \"cleanup\", v, N_(\"mode\"), N_(\"how to strip spaces and #comments from message\"))\n #define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, \"pathspec-from-file\", v, N_(\"read pathspec from file\"))\n #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\"))\n+#define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n \n #endif\ndiff --git a/path.c b/path.c\nindex 88cf593007..d764738146 100644\n--- a/path.c\n+++ b/path.c\n@@ -1535,5 +1535,6 @@ REPO_GIT_PATH_FUNC(merge_msg, \"MERGE_MSG\")\n REPO_GIT_PATH_FUNC(merge_rr, \"MERGE_RR\")\n REPO_GIT_PATH_FUNC(merge_mode, \"MERGE_MODE\")\n REPO_GIT_PATH_FUNC(merge_head, \"MERGE_HEAD\")\n+REPO_GIT_PATH_FUNC(merge_autostash, \"MERGE_AUTOSTASH\")\n REPO_GIT_PATH_FUNC(fetch_head, \"FETCH_HEAD\")\n REPO_GIT_PATH_FUNC(shallow, \"shallow\")\ndiff --git a/path.h b/path.h\nindex 14d6dcad16..1f1bf8f87a 100644\n--- a/path.h\n+++ b/path.h\n@@ -177,11 +177,12 @@ struct path_cache {\n \tconst char *merge_rr;\n \tconst char *merge_mode;\n \tconst char *merge_head;\n+\tconst char *merge_autostash;\n \tconst char *fetch_head;\n \tconst char *shallow;\n };\n \n-#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n+#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n \n const char *git_path_cherry_pick_head(struct repository *r);\n const char *git_path_revert_head(struct repository *r);\n@@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);\n const char *git_path_merge_rr(struct repository *r);\n const char *git_path_merge_mode(struct repository *r);\n const char *git_path_merge_head(struct repository *r);\n+const char *git_path_merge_autostash(struct repository *r);\n const char *git_path_fetch_head(struct repository *r);\n const char *git_path_shallow(struct repository *r);\n \ndiff --git a/t/t3033-merge-toplevel.sh b/t/t3033-merge-toplevel.sh\nindex d314599428..e29c284b9b 100755\n--- a/t/t3033-merge-toplevel.sh\n+++ b/t/t3033-merge-toplevel.sh\n@@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' '\n \ttest_must_fail git merge five\n '\n \n+test_expect_success 'refuse two-project merge by default, quit before --autostash happens' '\n+\tt3033_reset &&\n+\tgit reset --hard four &&\n+\techo change >>one.t &&\n+\tgit diff >expect &&\n+\ttest_must_fail git merge --autostash five 2>err &&\n+\ttest_i18ngrep ! \"stash\" err &&\n+\tgit diff >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'two-project merge with --allow-unrelated-histories' '\n \tt3033_reset &&\n \tgit reset --hard four &&\n@@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' '\n \tgit diff --exit-code five\n '\n \n+test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' '\n+\tt3033_reset &&\n+\tgit reset --hard four &&\n+\techo change >>one.t &&\n+\tgit diff one.t >expect &&\n+\tgit merge --allow-unrelated-histories --autostash five 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit diff one.t >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 4fa0ef8e3b..d809310df1 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -30,13 +30,17 @@ Testing basic merge operations/option parsing.\n . \"$TEST_DIRECTORY\"/lib-gpg.sh\n \n test_write_lines 1 2 3 4 5 6 7 8 9 >file\n+cp file file.orig\n test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1\n+test_write_lines 1 2 '3 X' 4 5 6 7 8 9 >file.3\n test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5\n test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9\n test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1\n test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n+test_write_lines '1 X' 2 3 4 5 6 7 8 '9 X' >result.1-9\n test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n+test_write_lines '1 X' 2 '3 X' 4 '5 X' 6 7 8 '9 X' >result.1-3-5-9\n test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n \n create_merge_msgs () {\n@@ -675,6 +679,120 @@ test_expect_success 'refresh the index before merging' '\n \tgit merge c3\n '\n \n+test_expect_success 'merge with --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit merge --autostash c2 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'merge with merge.autoStash' '\n+\ttest_config merge.autoStash true &&\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit merge c2 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'fast-forward merge with --autostash' '\n+\tgit reset --hard c0 &&\n+\tgit merge-file file file.orig file.5 &&\n+\tgit merge --autostash c1 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\ttest_cmp result.1-5 file\n+'\n+\n+test_expect_success 'octopus merge with --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.3 &&\n+\tgit merge --autostash c2 c3 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5-9 merge-result &&\n+\ttest_cmp result.1-3-5-9 file\n+'\n+\n+test_expect_success 'conflicted merge with --autostash, --abort restores stash' '\n+\tgit reset --hard c3 &&\n+\tcp file.1 file &&\n+\ttest_must_fail git merge --autostash c7 &&\n+\tgit merge --abort 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\ttest_cmp file.1 file\n+'\n+\n+test_expect_success 'completed merge (git commit) with --no-commit and --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit diff >expect &&\n+\tgit merge --no-commit --autostash c2 &&\n+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n+\ttest_cmp expect actual &&\n+\tgit commit 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'completed merge (git merge --continue) with --no-commit and --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit diff >expect &&\n+\tgit merge --no-commit --autostash c2 &&\n+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n+\ttest_cmp expect actual &&\n+\tgit merge --continue 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit diff >expect &&\n+\tgit merge --no-commit --autostash c2 &&\n+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n+\ttest_cmp expect actual &&\n+\tgit merge --abort 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit diff >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit diff >expect &&\n+\tgit merge --no-commit --autostash c2 &&\n+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n+\ttest_cmp expect actual &&\n+\tgit reset --hard 2>err &&\n+\ttest_i18ngrep \"Autostash exists; creating a new stash entry.\" err &&\n+\tgit diff --exit-code\n+'\n+\n+test_expect_success 'merge with conflicted --autostash changes' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9y &&\n+\tgit diff >expect &&\n+\ttest_when_finished \"test_might_fail git stash drop\" &&\n+\tgit merge --autostash c3 2>err &&\n+\ttest_i18ngrep \"Applying autostash resulted in conflicts.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-9 merge-result &&\n+\tgit stash show -p >actual &&\n+\ttest_cmp expect actual\n+'\n+\n cat >expected.branch <<\\EOF\n Merge branch 'c5-branch' (early part)\n EOF\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394759","messageId":"6ead240957da808be1261bc960227c4183da3dd9.1585962672.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v4 06/23] sequencer: make read_oneliner() accept flags","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-04T01:11:19Z","receivedAt":"2020-04-04T01:12:31Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future commit, we will need read_oneliner() to accept flags other\nthan just `skip_if_empty`. Instead of having an argument for each flag,\nteach read_oneliner() to accept the bitfield `flags` instead. For now,\nonly recognize the `READ_ONELINER_SKIP_IF_EMPTY` flag. More flags will\nbe added in a future commit.\n\nThe result of this is that parallel topics which introduce invocations\nof read_oneliner() will still be compatible with this new function\nsignature since, instead of passing 1 or 0 for `skip_if_empty`, they'll\nbe passing 1 or 0 to `flags`, which gives equivalent behavior.\n\nMechanically fix up invocations of read_oneliner() with the following\nspatch\n\n\t@@\n\texpression a, b;\n\t@@\n\t  read_oneliner(a, b,\n\t- 1\n\t+ READ_ONELINER_SKIP_IF_EMPTY\n\t  )\n\nand manually break up long lines in the result.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 21 ++++++++++++++-------\n 1 file changed, 14 insertions(+), 7 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 6e63224c41..6c26d61670 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -419,6 +419,8 @@ static int write_message(const void *buf, size_t len, const char *filename,\n \treturn 0;\n }\n \n+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n+\n /*\n  * Resets a strbuf then reads a file that was presumably written by a shell\n  * script, i.e. with an end-of-line marker that needs to be stripped.\n@@ -429,7 +431,7 @@ static int write_message(const void *buf, size_t len, const char *filename,\n  * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n  */\n static int read_oneliner(struct strbuf *buf,\n-\tconst char *path, int skip_if_empty)\n+\tconst char *path, unsigned flags)\n {\n \tstrbuf_reset(buf);\n \tif (strbuf_read_file(buf, path, 0) < 0) {\n@@ -440,7 +442,7 @@ static int read_oneliner(struct strbuf *buf,\n \n \tstrbuf_trim(buf);\n \n-\tif (skip_if_empty && !buf->len)\n+\tif ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !buf->len)\n \t\treturn 0;\n \n \treturn 1;\n@@ -2479,7 +2481,8 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tint ret = 0;\n \n-\t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {\n+\t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(),\n+\t\t\t\t  READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\t\tif (!starts_with(buf.buf, \"-S\"))\n \t\t\t\tstrbuf_reset(&buf);\n \t\t\telse {\n@@ -2488,7 +2491,8 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t\t}\n \t\t}\n \n-\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {\n+\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(),\n+\t\t\t\t  READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n \t\t\t\topts->allow_rerere_auto = RERERE_AUTOUPDATE;\n \t\t\telse if (!strcmp(buf.buf, \"--no-rerere-autoupdate\"))\n@@ -2519,7 +2523,8 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\tread_strategy_opts(opts, &buf);\n \n \t\tif (read_oneliner(&opts->current_fixups,\n-\t\t\t\t  rebase_path_current_fixups(), 1)) {\n+\t\t\t\t  rebase_path_current_fixups(),\n+\t\t\t\t  READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\t\tconst char *p = opts->current_fixups.buf;\n \t\t\topts->current_fixup_count = 1;\n \t\t\twhile ((p = strchr(p, '\\n'))) {\n@@ -3660,7 +3665,8 @@ static int apply_autostash(struct replay_opts *opts)\n \tstruct child_process child = CHILD_PROCESS_INIT;\n \tint ret = 0;\n \n-\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {\n+\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(),\n+\t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\tstrbuf_release(&stash_sha1);\n \t\treturn 0;\n \t}\n@@ -4283,7 +4289,8 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tstruct object_id oid;\n \n-\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&\n+\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(),\n+\t\t\t\t  READ_ONELINER_SKIP_IF_EMPTY) &&\n \t\t    !get_oid_committish(buf.buf, &oid))\n \t\t\trecord_in_rewritten(&oid, peek_command(&todo_list, 0));\n \t\tstrbuf_release(&buf);\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394809","messageId":"xmqqzhbpwrcb.fsf@gitster.c.googlers.com","threadId":"52048","inReplyTo":"76585e5b1367a3adf18d761b2af9356ee64b46fd.1585962672.git.liu.denton@gmail.com","subject":"Re: [PATCH v4 03/23] sequencer: stop leaking buf","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-04-05T21:33:08Z","receivedAt":"2020-04-05T21:33:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> In read_populate_opts(), we call read_oneliner() _after_ calling\n> strbuf_release(). This means that `buf` is leaked at the end of the\n> function.\n\nI do not think the above makes much sense.  _release() will free the\npiece of memory occupied by .buf and reinitializes the strbuf, and\nin doing so there is no leak.  read_oneliner() called after it\nallocates and reads into there.  Freeing the resource needs to be\ndone after the caller is done with what read_oneliner() has read.\n\nThere is a leak, because read_oneliner() gets called and before the\ncode has a chance to do strbuf_reease() there is an error return.\nThat does not have anything to do with the call to strbuf_release()\nin the middle of the function.\n\nBut that leak has nothing to do with the release called before\nread_oneliner().\n\n> Always clean up the strbuf by going to `done_rebase_i` whether or not\n> we return an error.\n>\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>  sequencer.c | 13 +++++++++----\n>  1 file changed, 9 insertions(+), 4 deletions(-)\n>\n> diff --git a/sequencer.c b/sequencer.c\n> index e528225e78..faab0b13e8 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -2485,6 +2485,7 @@ static int read_populate_opts(struct replay_opts *opts)\n>  {\n>  \tif (is_rebase_i(opts)) {\n>  \t\tstruct strbuf buf = STRBUF_INIT;\n> +\t\tint ret = 0;\n>  \n>  \t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {\n>  \t\t\tif (!starts_with(buf.buf, \"-S\"))\n> @@ -2525,7 +2526,7 @@ static int read_populate_opts(struct replay_opts *opts)\n>  \t\t\topts->keep_redundant_commits = 1;\n>  \n>  \t\tread_strategy_opts(opts, &buf);\n> -\t\tstrbuf_release(&buf);\n> +\t\tstrbuf_reset(&buf);\n\nAs read_oneliner() has a strbuf_reset() at the beginning *anyway*,\nwhy not just get rid of the call to _release() here instead?  After\nall, there is no _release() or _reset() before the call to read_oneliner()\nin the next hunk, or two calls to read_oneliner() inside the\nread_strategy_opts() function called in the above.\n\n>  \t\tif (read_oneliner(&opts->current_fixups,\n>  \t\t\t\t  rebase_path_current_fixups(), 1)) {\n> @@ -2538,12 +2539,16 @@ static int read_populate_opts(struct replay_opts *opts)\n>  \t\t}\n>  \n>  \t\tif (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {\n> -\t\t\tif (get_oid_hex(buf.buf, &opts->squash_onto) < 0)\n> -\t\t\t\treturn error(_(\"unusable squash-onto\"));\n> +\t\t\tif (get_oid_hex(buf.buf, &opts->squash_onto) < 0) {\n> +\t\t\t\tret = error(_(\"unusable squash-onto\"));\n> +\t\t\t\tgoto done_rebase_i;\n> +\t\t\t}\n>  \t\t\topts->have_squash_onto = 1;\n>  \t\t}\n>  \n> -\t\treturn 0;\n> +done_rebase_i:\n> +\t\tstrbuf_release(&buf);\n> +\t\treturn ret;\n\nThis part indeed IS the right fix to the existing leak.\n\n>  \t}\n>  \n>  \tif (!file_exists(git_path_opts_file()))\n"},{"id":"394810","messageId":"xmqqv9mdwr4o.fsf@gitster.c.googlers.com","threadId":"52048","inReplyTo":"xmqqzhbpwrcb.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v4 03/23] sequencer: stop leaking buf","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-04-05T21:37:43Z","receivedAt":"2020-04-05T21:37:51Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>>  \t\tread_strategy_opts(opts, &buf);\n>> -\t\tstrbuf_release(&buf);\n>> +\t\tstrbuf_reset(&buf);\n>\n> As read_oneliner() has a strbuf_reset() at the beginning *anyway*,\n> why not just get rid of the call to _release() here instead?  After\n> all, there is no _release() or _reset() before the call to read_oneliner()\n> in the next hunk, or two calls to read_oneliner() inside the\n> read_strategy_opts() function called in the above.\n\nAh, I was reading the state of applying the series thru to the end.\nAt this point, there is no reset there, so you'd need to call reset\nto ensure that the next read_oneliner() on &buf will not append, but\nrestart from the beginning.\n\n>>  \t\tif (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {\n>> -\t\t\tif (get_oid_hex(buf.buf, &opts->squash_onto) < 0)\n>> -\t\t\t\treturn error(_(\"unusable squash-onto\"));\n"},{"id":"394811","messageId":"xmqqr1x1wqpk.fsf@gitster.c.googlers.com","threadId":"52048","inReplyTo":"c7a3cfa20005aeeedc27d2eb4af1e2c4470ad73d.1585962672.git.liu.denton@gmail.com","subject":"Re: [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-04-05T21:46:47Z","receivedAt":"2020-04-05T21:46:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> @@ -2471,7 +2467,6 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)\n>  \n>  static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)\n>  {\n> -\tstrbuf_reset(buf);\n>  \tif (!read_oneliner(buf, rebase_path_strategy(), 0))\n>  \t\treturn;\n>  \topts->strategy = strbuf_detach(buf, NULL);\n> @@ -2494,7 +2489,6 @@ static int read_populate_opts(struct replay_opts *opts)\n>  \t\t\t\tfree(opts->gpg_sign);\n>  \t\t\t\topts->gpg_sign = xstrdup(buf.buf + 2);\n>  \t\t\t}\n> -\t\t\tstrbuf_reset(&buf);\n>  \t\t}\n>  \n>  \t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {\n> @@ -2526,7 +2520,6 @@ static int read_populate_opts(struct replay_opts *opts)\n>  \t\t\topts->keep_redundant_commits = 1;\n>  \n>  \t\tread_strategy_opts(opts, &buf);\n> -\t\tstrbuf_reset(&buf);\n>  \n\n>  \t\tif (read_oneliner(&opts->current_fixups,\n>  \t\t\t\t  rebase_path_current_fixups(), 1)) {\n\nIs this conversion correct around here?  read_oneliner() used to\n\"append\" what was read from the file to what is already in the\nstrbuf, and many strbuf_reset() in this function was because these\ncallers of read_oneliner() in this function that has strbuf_reset()\nimmediately before did *not* want the \"append\" semantics.  But this\none looks different.  Where in the original does the current_fixups\nstrbuf get emptied for this read_oneliner() to ignore the previous\ncontents?  Or are we relying on the caller not to have done anything\nto current_fixups before it calls this function?\n\nIn other words, the original behaviour of read_oneliner() having the\n\"append\" semantics suggests me that there were callers that wanted\nto keep the current contents and append---this current_fixups may\nnot be one of them, but nevertheless, changing the semantics of the\nfunction from \"append\" to \"discard and read from scratch\" without\nvetting all the existing callers smells iffy to me.\n\n"},{"id":"394815","messageId":"20200405234209.GA1346725@generichostname","threadId":"52048","inReplyTo":"xmqqzhbpwrcb.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v4 03/23] sequencer: stop leaking buf","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-05T23:42:09Z","receivedAt":"2020-04-05T23:42:17Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Junio,\n\nOn Sun, Apr 05, 2020 at 02:33:08PM -0700, Junio C Hamano wrote:\n> Denton Liu <liu.denton@gmail.com> writes:\n> \n> > In read_populate_opts(), we call read_oneliner() _after_ calling\n> > strbuf_release(). This means that `buf` is leaked at the end of the\n> > function.\n> \n> I do not think the above makes much sense.  _release() will free the\n> piece of memory occupied by .buf and reinitializes the strbuf, and\n> in doing so there is no leak.  read_oneliner() called after it\n> allocates and reads into there.  Freeing the resource needs to be\n> done after the caller is done with what read_oneliner() has read.\n> \n> There is a leak, because read_oneliner() gets called and before the\n> code has a chance to do strbuf_reease() there is an error return.\n> That does not have anything to do with the call to strbuf_release()\n> in the middle of the function.\n\nThere is also a leak in the case where we don't take the early return\nsince, before this patch, the read_oneliner() has no strbuf_release()\nfollowing it which means buf gets leaked.\n\n> But that leak has nothing to do with the release called before\n> read_oneliner().\n\nWhat I meant to say in my paragraph is essentially, \"strbuf_release() is\nplaced too early, there is a read_oneliner() after that nullifies the\neffects of strbuf_release()\". I can rewrite this to be more clear.\n\nThanks,\n\nDenton\n"},{"id":"394817","messageId":"20200406013956.GA1879124@generichostname","threadId":"52048","inReplyTo":"xmqqr1x1wqpk.fsf@gitster.c.googlers.com","subject":"Re: [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-06T01:39:56Z","receivedAt":"2020-04-06T01:40:04Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Junio,\n\nOn Sun, Apr 05, 2020 at 02:46:47PM -0700, Junio C Hamano wrote:\n> Denton Liu <liu.denton@gmail.com> writes:\n> \n> > @@ -2471,7 +2467,6 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)\n> >  \n> >  static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)\n> >  {\n> > -\tstrbuf_reset(buf);\n> >  \tif (!read_oneliner(buf, rebase_path_strategy(), 0))\n> >  \t\treturn;\n> >  \topts->strategy = strbuf_detach(buf, NULL);\n> > @@ -2494,7 +2489,6 @@ static int read_populate_opts(struct replay_opts *opts)\n> >  \t\t\t\tfree(opts->gpg_sign);\n> >  \t\t\t\topts->gpg_sign = xstrdup(buf.buf + 2);\n> >  \t\t\t}\n> > -\t\t\tstrbuf_reset(&buf);\n> >  \t\t}\n> >  \n> >  \t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {\n> > @@ -2526,7 +2520,6 @@ static int read_populate_opts(struct replay_opts *opts)\n> >  \t\t\topts->keep_redundant_commits = 1;\n> >  \n> >  \t\tread_strategy_opts(opts, &buf);\n> > -\t\tstrbuf_reset(&buf);\n> >  \n> \n> >  \t\tif (read_oneliner(&opts->current_fixups,\n> >  \t\t\t\t  rebase_path_current_fixups(), 1)) {\n> \n> Is this conversion correct around here?  read_oneliner() used to\n> \"append\" what was read from the file to what is already in the\n> strbuf, and many strbuf_reset() in this function was because these\n> callers of read_oneliner() in this function that has strbuf_reset()\n> immediately before did *not* want the \"append\" semantics.  But this\n> one looks different.  Where in the original does the current_fixups\n> strbuf get emptied for this read_oneliner() to ignore the previous\n> contents?  Or are we relying on the caller not to have done anything\n> to current_fixups before it calls this function?\n\nAs far as I can tell, opts->current_fixups is always empty when\nread_oneliner() is called here.\n\n> In other words, the original behaviour of read_oneliner() having the\n> \"append\" semantics suggests me that there were callers that wanted\n> to keep the current contents and append---this current_fixups may\n> not be one of them, but nevertheless, changing the semantics of the\n> function from \"append\" to \"discard and read from scratch\" without\n> vetting all the existing callers smells iffy to me.\n\nBefore making this change, I manually checked all invocations of\nread_oneliner() and ensured that they all passed in an empty strbuf.\nSame thing with \"rebase: use read_oneliner()\", I manually checked all of\nthose invocations as well. It's quite possible that I made a mistake,\nthough.\n\nTo be doubly sure that I caught everything, I ran the test suite on\n'master' with this patch:\n\n\tdiff --git a/builtin/rebase.c b/builtin/rebase.c\n\tindex 27a07d4e78..a0c03dd1d6 100644\n\t--- a/builtin/rebase.c\n\t+++ b/builtin/rebase.c\n\t@@ -589,6 +589,9 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o\n\t /* Read one file, then strip line endings */\n\t static int read_one(const char *path, struct strbuf *buf)\n\t {\n\t+\tif (buf->len)\n\t+\t\tBUG(\"rebase change bad: %s\", buf->buf);\n\t+\n\t\tif (strbuf_read_file(buf, path, 0) < 0)\n\t\t\treturn error_errno(_(\"could not read '%s'\"), path);\n\t\tstrbuf_trim_trailing_newline(buf);\n\tdiff --git a/sequencer.c b/sequencer.c\n\tindex 6fd2674632..d7bc5c9c95 100644\n\t--- a/sequencer.c\n\t+++ b/sequencer.c\n\t@@ -433,6 +433,9 @@ static int read_oneliner(struct strbuf *buf,\n\t {\n\t\tint orig_len = buf->len;\n\t \n\t+\tif (buf->len)\n\t+\t\tBUG(\"sequencer change bad: %s\", buf->buf);\n\t+\n\t\tif (!file_exists(path))\n\t\t\treturn 0;\n\nThanks,\n\nDenton\n"},{"id":"394838","messageId":"4e04fd90-bb71-0d7e-e611-5c408161d3e5@gmail.com","threadId":"52048","inReplyTo":"c7a3cfa20005aeeedc27d2eb4af1e2c4470ad73d.1585962672.git.liu.denton@gmail.com","subject":"Re: [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-06T14:03:12Z","receivedAt":"2020-04-06T14:03:18Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 04/04/2020 02:11, Denton Liu wrote:\n> In the original read_oneliner() logic, we duplicated the logic for\n> strbuf_trim_trailing_newline() with one exception: instead of preventing\n> buffer accesses below index 0, it would prevent buffer accesses below\n> index `orig_len`. Although this is correct, it isn't worth having the\n> duplicated logic around.\n> \n> Reset `buf` before using it then replace the trimming logic with\n> strbuf_trim().\n\nI should have picked up on this before but this changes the semantics of \nthe function as it strips all whitespace from the start and end of the \nstrbuf. Above you talked about using strbuf_trim_trailing_newline() \ninstead which would not change the semantics of this function. You could \ntest to see if we've read anything and only call \nstrbuf_trim_trailing_newline() in that case without messing with \nstrbuf_reset(). (there is a corner case where if the buffer ends with \n'\\r' when the function is called and it reads a single '\\n' then the \n'\\r' would be stripped as well but I think that is unlikely to happen in \nthe wild)\n\nBest Wishes\n\nPhillip\n\n> As a cleanup, remove all reset_strbuf()s that happen before\n> read_oneliner() is called.\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   sequencer.c | 18 +++++-------------\n>   1 file changed, 5 insertions(+), 13 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index faab0b13e8..09ca68f540 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -420,8 +420,8 @@ static int write_message(const void *buf, size_t len, const char *filename,\n>   }\n>   \n>   /*\n> - * Reads a file that was presumably written by a shell script, i.e. with an\n> - * end-of-line marker that needs to be stripped.\n> + * Resets a strbuf then reads a file that was presumably written by a shell\n> + * script, i.e. with an end-of-line marker that needs to be stripped.\n>    *\n>    * Note that only the last end-of-line marker is stripped, consistent with the\n>    * behavior of \"$(cat path)\" in a shell script.\n> @@ -431,23 +431,19 @@ static int write_message(const void *buf, size_t len, const char *filename,\n>   static int read_oneliner(struct strbuf *buf,\n>   \tconst char *path, int skip_if_empty)\n>   {\n> -\tint orig_len = buf->len;\n>   \n>   \tif (!file_exists(path))\n>   \t\treturn 0;\n>   \n> +\tstrbuf_reset(buf);\n>   \tif (strbuf_read_file(buf, path, 0) < 0) {\n>   \t\twarning_errno(_(\"could not read '%s'\"), path);\n>   \t\treturn 0;\n>   \t}\n>   \n> -\tif (buf->len > orig_len && buf->buf[buf->len - 1] == '\\n') {\n> -\t\tif (--buf->len > orig_len && buf->buf[buf->len - 1] == '\\r')\n> -\t\t\t--buf->len;\n> -\t\tbuf->buf[buf->len] = '\\0';\n> -\t}\n> +\tstrbuf_trim(buf);\n>   \n> -\tif (skip_if_empty && buf->len == orig_len)\n> +\tif (skip_if_empty && !buf->len)\n>   \t\treturn 0;\n>   \n>   \treturn 1;\n> @@ -2471,7 +2467,6 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)\n>   \n>   static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)\n>   {\n> -\tstrbuf_reset(buf);\n>   \tif (!read_oneliner(buf, rebase_path_strategy(), 0))\n>   \t\treturn;\n>   \topts->strategy = strbuf_detach(buf, NULL);\n> @@ -2494,7 +2489,6 @@ static int read_populate_opts(struct replay_opts *opts)\n>   \t\t\t\tfree(opts->gpg_sign);\n>   \t\t\t\topts->gpg_sign = xstrdup(buf.buf + 2);\n>   \t\t\t}\n> -\t\t\tstrbuf_reset(&buf);\n>   \t\t}\n>   \n>   \t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {\n> @@ -2526,7 +2520,6 @@ static int read_populate_opts(struct replay_opts *opts)\n>   \t\t\topts->keep_redundant_commits = 1;\n>   \n>   \t\tread_strategy_opts(opts, &buf);\n> -\t\tstrbuf_reset(&buf);\n>   \n>   \t\tif (read_oneliner(&opts->current_fixups,\n>   \t\t\t\t  rebase_path_current_fixups(), 1)) {\n> @@ -4006,7 +3999,6 @@ static int pick_commits(struct repository *r,\n>   \t\t\t\tres = error(_(\"could not read orig-head\"));\n>   \t\t\t\tgoto cleanup_head_ref;\n>   \t\t\t}\n> -\t\t\tstrbuf_reset(&buf);\n>   \t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0)) {\n>   \t\t\t\tres = error(_(\"could not read 'onto'\"));\n>   \t\t\t\tgoto cleanup_head_ref;\n> \n"},{"id":"394840","messageId":"1c382358-08ca-8bf8-cbd6-d00406472bcd@gmail.com","threadId":"52048","inReplyTo":"4e04fd90-bb71-0d7e-e611-5c408161d3e5@gmail.com","subject":"Re: [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-06T14:42:59Z","receivedAt":"2020-04-06T14:43:04Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 06/04/2020 15:03, Phillip Wood wrote:\n> Hi Denton\n> \n> On 04/04/2020 02:11, Denton Liu wrote:\n>> In the original read_oneliner() logic, we duplicated the logic for\n>> strbuf_trim_trailing_newline() with one exception: instead of preventing\n>> buffer accesses below index 0, it would prevent buffer accesses below\n>> index `orig_len`. Although this is correct, it isn't worth having the\n>> duplicated logic around.\n>>\n>> Reset `buf` before using it then replace the trimming logic with\n>> strbuf_trim().\n> \n> I should have picked up on this before but this changes the semantics of \n> the function as it strips all whitespace from the start and end of the \n> strbuf. Above you talked about using strbuf_trim_trailing_newline() \n> instead which would not change the semantics of this function. You could \n> test to see if we've read anything and only call \n> strbuf_trim_trailing_newline() in that case without messing with \n> strbuf_reset(). (there is a corner case where if the buffer ends with \n> '\\r' when the function is called and it reads a single '\\n' then the \n> '\\r' would be stripped as well but I think that is unlikely to happen in \n> the wild)\n\nI'd also be fine with dropping this patch and leaving the trimming code \nas is\n\nBest Wishes\n\nPhillip\n\n> Best Wishes\n> \n> Phillip\n> \n>> As a cleanup, remove all reset_strbuf()s that happen before\n>> read_oneliner() is called.\n>>\n>> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n>> ---\n>>   sequencer.c | 18 +++++-------------\n>>   1 file changed, 5 insertions(+), 13 deletions(-)\n>>\n>> diff --git a/sequencer.c b/sequencer.c\n>> index faab0b13e8..09ca68f540 100644\n>> --- a/sequencer.c\n>> +++ b/sequencer.c\n>> @@ -420,8 +420,8 @@ static int write_message(const void *buf, size_t \n>> len, const char *filename,\n>>   }\n>>   /*\n>> - * Reads a file that was presumably written by a shell script, i.e. \n>> with an\n>> - * end-of-line marker that needs to be stripped.\n>> + * Resets a strbuf then reads a file that was presumably written by a \n>> shell\n>> + * script, i.e. with an end-of-line marker that needs to be stripped.\n>>    *\n>>    * Note that only the last end-of-line marker is stripped, \n>> consistent with the\n>>    * behavior of \"$(cat path)\" in a shell script.\n>> @@ -431,23 +431,19 @@ static int write_message(const void *buf, size_t \n>> len, const char *filename,\n>>   static int read_oneliner(struct strbuf *buf,\n>>       const char *path, int skip_if_empty)\n>>   {\n>> -    int orig_len = buf->len;\n>>       if (!file_exists(path))\n>>           return 0;\n>> +    strbuf_reset(buf);\n>>       if (strbuf_read_file(buf, path, 0) < 0) {\n>>           warning_errno(_(\"could not read '%s'\"), path);\n>>           return 0;\n>>       }\n>> -    if (buf->len > orig_len && buf->buf[buf->len - 1] == '\\n') {\n>> -        if (--buf->len > orig_len && buf->buf[buf->len - 1] == '\\r')\n>> -            --buf->len;\n>> -        buf->buf[buf->len] = '\\0';\n>> -    }\n>> +    strbuf_trim(buf);\n>> -    if (skip_if_empty && buf->len == orig_len)\n>> +    if (skip_if_empty && !buf->len)\n>>           return 0;\n>>       return 1;\n>> @@ -2471,7 +2467,6 @@ void parse_strategy_opts(struct replay_opts \n>> *opts, char *raw_opts)\n>>   static void read_strategy_opts(struct replay_opts *opts, struct \n>> strbuf *buf)\n>>   {\n>> -    strbuf_reset(buf);\n>>       if (!read_oneliner(buf, rebase_path_strategy(), 0))\n>>           return;\n>>       opts->strategy = strbuf_detach(buf, NULL);\n>> @@ -2494,7 +2489,6 @@ static int read_populate_opts(struct replay_opts \n>> *opts)\n>>                   free(opts->gpg_sign);\n>>                   opts->gpg_sign = xstrdup(buf.buf + 2);\n>>               }\n>> -            strbuf_reset(&buf);\n>>           }\n>>           if (read_oneliner(&buf, \n>> rebase_path_allow_rerere_autoupdate(), 1)) {\n>> @@ -2526,7 +2520,6 @@ static int read_populate_opts(struct replay_opts \n>> *opts)\n>>               opts->keep_redundant_commits = 1;\n>>           read_strategy_opts(opts, &buf);\n>> -        strbuf_reset(&buf);\n>>           if (read_oneliner(&opts->current_fixups,\n>>                     rebase_path_current_fixups(), 1)) {\n>> @@ -4006,7 +3999,6 @@ static int pick_commits(struct repository *r,\n>>                   res = error(_(\"could not read orig-head\"));\n>>                   goto cleanup_head_ref;\n>>               }\n>> -            strbuf_reset(&buf);\n>>               if (!read_oneliner(&buf, rebase_path_onto(), 0)) {\n>>                   res = error(_(\"could not read 'onto'\"));\n>>                   goto cleanup_head_ref;\n>>\n"},{"id":"394841","messageId":"02110ed3-19c2-9074-2b30-90a4b289b0a8@gmail.com","threadId":"52048","inReplyTo":"2e7922b259851091b9014c761d340ff1a73a04e4.1585962672.git.liu.denton@gmail.com","subject":"Re: [PATCH v4 07/23] sequencer: configurably warn on non-existent files","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-06T14:45:37Z","receivedAt":"2020-04-06T14:45:41Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 04/04/2020 02:11, Denton Liu wrote:\n> In the future, we plan on externing read_oneliner(). Future users of\n> read_oneliner() will want the ability to output warnings in the event\n> that the `path` doesn't exist. Introduce the\n> `READ_ONELINER_WARN_MISSING` flag which, if active, would issue a\n> warning when a file doesn't exist by skipping the `!file_exists()` check\n> and letting `strbuf_read_file()` handle that case.\n\nNow that you've updated the function in a previous patch the \nfile_exists() check referred to in the message no longer exists\n\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   sequencer.c | 4 +++-\n>   1 file changed, 3 insertions(+), 1 deletion(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index 6c26d61670..b771e8f4ca 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename,\n>   }\n>   \n>   #define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n> +#define READ_ONELINER_WARN_MISSING (1 << 1)\n>   \n>   /*\n>    * Resets a strbuf then reads a file that was presumably written by a shell\n> @@ -435,7 +436,8 @@ static int read_oneliner(struct strbuf *buf,\n>   {\n>   \tstrbuf_reset(buf);\n>   \tif (strbuf_read_file(buf, path, 0) < 0) {\n> -\t\tif (errno != ENOENT && errno != ENOTDIR)\n> +\t\tif ((flags & READ_ONELINER_WARN_MISSING) ||\n> +\t\t\t\t(errno != ENOENT && errno != ENOTDIR))\n\nWe don't need the extra indentation for '(errno != ...', it should align \nwith the second '(' on the line above I think\n\nBest Wishes\n\nPhillip\n\n>   \t\t\twarning_errno(_(\"could not read '%s'\"), path);\n>   \t\treturn 0;\n>   \t}\n> \n"},{"id":"394842","messageId":"09c74578-9d25-b4d9-8808-4a6a9f92e918@gmail.com","threadId":"52048","inReplyTo":"e0b8e409afbf8d90b4fc6406a67a1a1e930e022f.1585962673.git.liu.denton@gmail.com","subject":"Re: [PATCH v4 10/23] sequencer: make apply_rebase() accept a path","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-06T15:07:57Z","receivedAt":"2020-04-06T15:08:04Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 04/04/2020 02:11, Denton Liu wrote:\n> In order to make apply_rebase()\n\nI think you mean apply_autostash() (in the subject as well)\n\nBest Wishes\n\nPhillip\n\n> more generic for future extraction, make\n> it accept a `path` argument so that the location from where to read the\n> reference to the autostash commit can be customized. Remove the `opts`\n> argument since it was unused before anyway.\n> \n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   sequencer.c | 14 +++++++-------\n>   1 file changed, 7 insertions(+), 7 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index d1d59867ae..ae9efe5825 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -3649,13 +3649,13 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n>   \treturn -1;\n>   }\n>   \n> -static int apply_autostash(struct replay_opts *opts)\n> +static int apply_autostash(const char *path)\n>   {\n>   \tstruct strbuf stash_sha1 = STRBUF_INIT;\n>   \tstruct child_process child = CHILD_PROCESS_INIT;\n>   \tint ret = 0;\n>   \n> -\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(),\n> +\tif (!read_oneliner(&stash_sha1, path,\n>   \t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n>   \t\tstrbuf_release(&stash_sha1);\n>   \t\treturn 0;\n> @@ -3748,7 +3748,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,\n>   \t\treturn error(_(\"%s: not a valid OID\"), orig_head);\n>   \n>   \tif (run_git_checkout(r, opts, oid_to_hex(onto), action)) {\n> -\t\tapply_autostash(opts);\n> +\t\tapply_autostash(rebase_path_autostash());\n>   \t\tsequencer_remove_state(opts);\n>   \t\treturn error(_(\"could not detach HEAD\"));\n>   \t}\n> @@ -4061,7 +4061,7 @@ static int pick_commits(struct repository *r,\n>   \t\t\t\trun_command(&hook);\n>   \t\t\t}\n>   \t\t}\n> -\t\tapply_autostash(opts);\n> +\t\tapply_autostash(rebase_path_autostash());\n>   \n>   \t\tif (!opts->quiet) {\n>   \t\t\tif (!opts->verbose)\n> @@ -5070,7 +5070,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n>   \t\ttodo_list_add_exec_commands(todo_list, commands);\n>   \n>   \tif (count_commands(todo_list) == 0) {\n> -\t\tapply_autostash(opts);\n> +\t\tapply_autostash(rebase_path_autostash());\n>   \t\tsequencer_remove_state(opts);\n>   \n>   \t\treturn error(_(\"nothing to do\"));\n> @@ -5081,12 +5081,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n>   \tif (res == -1)\n>   \t\treturn -1;\n>   \telse if (res == -2) {\n> -\t\tapply_autostash(opts);\n> +\t\tapply_autostash(rebase_path_autostash());\n>   \t\tsequencer_remove_state(opts);\n>   \n>   \t\treturn -1;\n>   \t} else if (res == -3) {\n> -\t\tapply_autostash(opts);\n> +\t\tapply_autostash(rebase_path_autostash());\n>   \t\tsequencer_remove_state(opts);\n>   \t\ttodo_list_release(&new_todo);\n>   \n> \n"},{"id":"394843","messageId":"f178c14b-3cf9-ca2f-258a-3475f2fb9678@gmail.com","threadId":"52048","inReplyTo":"75dc3f10a1aac24007b660a02ce829c8702ffd0e.1585962673.git.liu.denton@gmail.com","subject":"Re: [PATCH v4 19/23] sequencer: implement save_autostash()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-06T15:15:46Z","receivedAt":"2020-04-06T15:15:52Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 04/04/2020 02:11, Denton Liu wrote:\n> Extract common functionality of apply_autostash() into\n> apply_save_autostash() and use it to implement save_autostash(). This\n> function will be used in a future commit.\n> \n> The difference between save_autostash() and apply_autostash() is that\n> the latter does not try to apply the stash. \n\nthe latter is apply_autostash() - surely that tries to apply the stash?\n\n> It skips that step and\n> just stores the created entry in the stash reflog.\n\nI think we want save_autostash() so we can call it to save the stash \nwhen running a reset - perhaps we should mention that in the commit message.\n\nBest Wishes\n\nPhillip\n\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   sequencer.c | 37 +++++++++++++++++++++++++++----------\n>   sequencer.h |  1 +\n>   2 files changed, 28 insertions(+), 10 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index 5dd1c2438e..fb52583bc2 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -3699,7 +3699,7 @@ void create_autostash(struct repository *r, const char *path,\n>   \tstrbuf_release(&buf);\n>   }\n>   \n> -int apply_autostash(const char *path)\n> +static int apply_save_autostash(const char *path, int attempt_apply)\n>   {\n>   \tstruct strbuf stash_oid = STRBUF_INIT;\n>   \tstruct child_process child = CHILD_PROCESS_INIT;\n> @@ -3712,13 +3712,17 @@ int apply_autostash(const char *path)\n>   \t}\n>   \tstrbuf_trim(&stash_oid);\n>   \n> -\tchild.git_cmd = 1;\n> -\tchild.no_stdout = 1;\n> -\tchild.no_stderr = 1;\n> -\targv_array_push(&child.args, \"stash\");\n> -\targv_array_push(&child.args, \"apply\");\n> -\targv_array_push(&child.args, stash_oid.buf);\n> -\tif (!run_command(&child))\n> +\tif (attempt_apply) {\n> +\t\tchild.git_cmd = 1;\n> +\t\tchild.no_stdout = 1;\n> +\t\tchild.no_stderr = 1;\n> +\t\targv_array_push(&child.args, \"stash\");\n> +\t\targv_array_push(&child.args, \"apply\");\n> +\t\targv_array_push(&child.args, stash_oid.buf);\n> +\t\tret = run_command(&child);\n> +\t}\n> +\n> +\tif (attempt_apply && !ret)\n>   \t\tfprintf(stderr, _(\"Applied autostash.\\n\"));\n>   \telse {\n>   \t\tstruct child_process store = CHILD_PROCESS_INIT;\n> @@ -3734,10 +3738,13 @@ int apply_autostash(const char *path)\n>   \t\t\tret = error(_(\"cannot store %s\"), stash_oid.buf);\n>   \t\telse\n>   \t\t\tfprintf(stderr,\n> -\t\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n> +\t\t\t\t_(\"%s\\n\"\n>   \t\t\t\t  \"Your changes are safe in the stash.\\n\"\n>   \t\t\t\t  \"You can run \\\"git stash pop\\\" or\"\n> -\t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"));\n> +\t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"),\n> +\t\t\t\tattempt_apply ?\n> +\t\t\t\t_(\"Applying autostash resulted in conflicts.\") :\n> +\t\t\t\t_(\"Autostash exists; creating a new stash entry.\"));\n>   \t}\n>   \n>   \tunlink(path);\n> @@ -3745,6 +3752,16 @@ int apply_autostash(const char *path)\n>   \treturn ret;\n>   }\n>   \n> +int save_autostash(const char *path)\n> +{\n> +\treturn apply_save_autostash(path, 0);\n> +}\n> +\n> +int apply_autostash(const char *path)\n> +{\n> +\treturn apply_save_autostash(path, 1);\n> +}\n> +\n>   static const char *reflog_message(struct replay_opts *opts,\n>   \tconst char *sub_action, const char *fmt, ...)\n>   {\n> diff --git a/sequencer.h b/sequencer.h\n> index cf1284f9ed..2d09c1ac0b 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -193,6 +193,7 @@ void commit_post_rewrite(struct repository *r,\n>   \n>   void create_autostash(struct repository *r, const char *path,\n>   \t\t      const char *default_reflog_action);\n> +int save_autostash(const char *path);\n>   int apply_autostash(const char *path);\n>   \n>   #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n> \n"},{"id":"394845","messageId":"07ec44b0-1162-aac9-1a07-579a3768cbd1@gmail.com","threadId":"52048","inReplyTo":"7adf7941921065fa1a5e68524e2dd3cd28f9a12b.1585962673.git.liu.denton@gmail.com","subject":"Re: [PATCH v4 21/23] merge: teach --autostash option","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-06T15:20:24Z","receivedAt":"2020-04-06T15:20:33Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nThanks for re-rolling, I think this addresses all of my previous \nconcerns, I've got a few minor comments below and a query about 'git \nmerge --quit'\n\nOn 04/04/2020 02:11, Denton Liu wrote:\n> In rebase, one can pass the `--autostash` option to cause the worktree\n> to be automatically stashed before continuing with the rebase. This\n> option is missing in merge, however.\n> \n> Implement the `--autostash` option and corresponding `merge.autoStash`\n> option in merge which stashes before merging and then pops after.\n> \n> This option is useful when a developer has some local changes on a topic\n> branch but they realize that their work depends on another branch.\n> Previously, they had to run something like\n> \n> \tgit fetch ...\n> \tgit stash push\n> \tgit merge FETCH_HEAD\n> \tgit stash pop\n> \n> but now, that is reduced to\n> \n> \tgit fetch ...\n> \tgit merge --autostash FETCH_HEAD\n> \n> When an autostash is generated, it is automatically reapplied to the\n> worktree only in three explicit situations:\n> \n> \t1. An incomplete merge is commit using `git commit`.\n> \t2. A merge completes successfully.\n> \t3. A merge is aborted using `git merge --abort`.\n> \n> In all other situations where the merge state is removed using\n> remove_merge_branch_state() such as aborting a merge via\n> `git reset --hard`, the autostash is saved into the stash reflog\n> instead keeping the worktree clean.\n> \n> Suggested-by: Alban Gruin <alban.gruin@gmail.com>\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>   Documentation/config/merge.txt  |  10 +++\n>   Documentation/git-merge.txt     |   6 +-\n>   Documentation/merge-options.txt |   8 +++\n>   branch.c                        |   1 +\n>   builtin/commit.c                |   2 +\n>   builtin/merge.c                 |  27 ++++++++\n>   builtin/rebase.c                |   3 +-\n>   builtin/reset.c                 |   4 +-\n>   parse-options.h                 |   1 +\n>   path.c                          |   1 +\n>   path.h                          |   4 +-\n>   t/t3033-merge-toplevel.sh       |  22 ++++++\n>   t/t7600-merge.sh                | 118 ++++++++++++++++++++++++++++++++\n>   13 files changed, 201 insertions(+), 6 deletions(-)\n> \n> diff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\n> index 6a313937f8..cb2ed58907 100644\n> --- a/Documentation/config/merge.txt\n> +++ b/Documentation/config/merge.txt\n> @@ -70,6 +70,16 @@ merge.stat::\n>   \tWhether to print the diffstat between ORIG_HEAD and the merge result\n>   \tat the end of the merge.  True by default.\n>   \n> +merge.autoStash::\n> +\tWhen set to true, automatically create a temporary stash entry\n> +\tbefore the operation begins, and apply it after the operation\n> +\tends.  This means that you can run merge on a dirty worktree.\n> +\tHowever, use with care: the final stash application after a\n> +\tsuccessful merge might result in non-trivial conflicts.\n> +\tThis option can be overridden by the `--no-autostash` and\n> +\t`--autostash` options of linkgit:git-merge[1].\n> +\tDefaults to false.\n> +\n>   merge.tool::\n>   \tControls which merge tool is used by linkgit:git-mergetool[1].\n>   \tThe list below shows the valid built-in values.\n> diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\n> index 092529c619..9cb14f0059 100644\n> --- a/Documentation/git-merge.txt\n> +++ b/Documentation/git-merge.txt\n> @@ -94,7 +94,8 @@ will be appended to the specified message.\n>   \n>   --abort::\n>   \tAbort the current conflict resolution process, and\n> -\ttry to reconstruct the pre-merge state.\n> +\ttry to reconstruct the pre-merge state. If an autostash entry is\n> +\tpresent, apply it back to the worktree.\n\nBeing picky I'd suggest deleting 'back'\n\n>   +\n>   If there were uncommitted worktree changes present when the merge\n>   started, 'git merge --abort' will in some cases be unable to\n> @@ -102,7 +103,8 @@ reconstruct these changes. It is therefore recommended to always\n>   commit or stash your changes before running 'git merge'.\n>   +\n>   'git merge --abort' is equivalent to 'git reset --merge' when\n> -`MERGE_HEAD` is present.\n> +`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present where\n> +the stash entry is applied to the worktree.\n\nPerhaps\n+`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present in\n+which case 'git merge --abort' applies the stash entry to the worktree\n+whereas 'git reset --merge' will save the stashed changes in the stash\n+reflog.\n\n>   --quit::\n>   \tForget about the current merge in progress. Leave the index\n\nShould we be saving the stash if the user runs 'git merge --quit'?\n\n> diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\n> index 40dc4f5e8c..3985e6d4a9 100644\n> --- a/Documentation/merge-options.txt\n> +++ b/Documentation/merge-options.txt\n> @@ -155,6 +155,14 @@ ifndef::git-pull[]\n>   \tNote that not all merge strategies may support progress\n>   \treporting.\n>   \n> +--autostash::\n> +--no-autostash::\n> +\tAutomatically create a temporary stash entry before the operation\n> +\tbegins, and apply it after the operation ends.  This means\n> +\tthat you can run the operation on a dirty worktree.  However, use\n> +\twith care: the final stash application after a successful\n> +\tmerge might result in non-trivial conflicts.\n> +\n>   endif::git-pull[]\n>   \n>   --allow-unrelated-histories::\n> diff --git a/branch.c b/branch.c\n> index 579494738a..2d9e7675a6 100644\n> --- a/branch.c\n> +++ b/branch.c\n> @@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)\n>   \tunlink(git_path_merge_rr(r));\n>   \tunlink(git_path_merge_msg(r));\n>   \tunlink(git_path_merge_mode(r));\n> +\tsave_autostash(git_path_merge_autostash(r));\n>   }\n>   \n>   void remove_branch_state(struct repository *r, int verbose)\n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index 7ba33a3bec..c1b25d2954 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -1713,6 +1713,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n>   \t\t\t\t     &oid, flags);\n>   \t}\n>   \n> +\tapply_autostash(git_path_merge_autostash(the_repository));\n> +\n\nI think this is the right place to apply the stash. There is a corner \ncase - if commit_index_files() fails then we leave MERGE_AUTOSTASH lying \naround. I think that is probably the best we can do - if we cannot write \nthe new index file to disk then saving or applying the stash would \nprobably fail as well.\n\n>   \tUNLEAK(err);\n>   \tUNLEAK(sb);\n>   \treturn 0;\n> diff --git a/builtin/merge.c b/builtin/merge.c\n> index d127d2225f..f2f8069f85 100644\n> --- a/builtin/merge.c\n> +++ b/builtin/merge.c\n> @@ -82,6 +82,7 @@ static int show_progress = -1;\n>   static int default_to_upstream = 1;\n>   static int signoff;\n>   static const char *sign_commit;\n> +static int autostash;\n>   static int no_verify;\n>   \n>   static struct strategy all_strategy[] = {\n> @@ -286,6 +287,7 @@ static struct option builtin_merge_options[] = {\n>   \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n>   \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n>   \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n> +\tOPT_AUTOSTASH(&autostash),\n>   \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n>   \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n>   \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n> @@ -475,6 +477,7 @@ static void finish(struct commit *head_commit,\n>   \t/* Run a post-merge hook */\n>   \trun_hook_le(NULL, \"post-merge\", squash ? \"1\" : \"0\", NULL);\n>   \n> +\tapply_autostash(git_path_merge_autostash(the_repository));\n\nI think this is the right place to apply it\n\n>   \tstrbuf_release(&reflog_message);\n>   }\n>   \n> @@ -634,6 +637,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n>   \t\treturn 0;\n>   \t} else if (!strcmp(k, \"gpg.mintrustlevel\")) {\n>   \t\tcheck_trust_level = 0;\n> +\t} else if (!strcmp(k, \"merge.autostash\")) {\n> +\t\tautostash = git_config_bool(k, v);\n> +\t\treturn 0;\n>   \t}\n>   \n>   \tstatus = fmt_merge_msg_config(k, v, cb);\n> @@ -1281,6 +1287,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>   \tif (abort_current_merge) {\n>   \t\tint nargc = 2;\n>   \t\tconst char *nargv[] = {\"reset\", \"--merge\", NULL};\n> +\t\tstruct strbuf stash_oid = STRBUF_INIT;\n>   \n>   \t\tif (orig_argc != 2)\n>   \t\t\tusage_msg_opt(_(\"--abort expects no arguments\"),\n> @@ -1289,8 +1296,19 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>   \t\tif (!file_exists(git_path_merge_head(the_repository)))\n>   \t\t\tdie(_(\"There is no merge to abort (MERGE_HEAD missing).\"));\n>   \n> +\t\tif (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),\n> +\t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n\ndodgy indentation\n\n> +\t\t\tstrbuf_trim(&stash_oid);\n\ndo we really need to trim the buffer?\n\n> +\t\t\tunlink(git_path_merge_autostash(the_repository));\n> +\t\t}\n> +\n>   \t\t/* Invoke 'git reset --merge' */\n>   \t\tret = cmd_reset(nargc, nargv, prefix);\n> +\n> +\t\tif (stash_oid.len)\n> +\t\t\tapply_autostash_oid(stash_oid.buf);\n> +\n> +\t\tstrbuf_release(&stash_oid);\n>   \t\tgoto done;\n>   \t}\n>   \n> @@ -1513,6 +1531,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>   \t\t\tgoto done;\n>   \t\t}\n>   \n> +\t\tif (autostash)\n> +\t\t\tcreate_autostash(the_repository,\n> +\t\t\t\t\t git_path_merge_autostash(the_repository),\n> +\t\t\t\t\t \"merge\");\n>   \t\tif (checkout_fast_forward(the_repository,\n>   \t\t\t\t\t  &head_commit->object.oid,\n>   \t\t\t\t\t  &commit->object.oid,\n> @@ -1579,6 +1601,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n>   \tif (fast_forward == FF_ONLY)\n>   \t\tdie(_(\"Not possible to fast-forward, aborting.\"));\n>   \n> +\tif (autostash)\n> +\t\tcreate_autostash(the_repository,\n> +\t\t\t\t git_path_merge_autostash(the_repository),\n> +\t\t\t\t \"merge\");\n> +\n>   \t/* We are going to make a new commit. */\n>   \tgit_committer_info(IDENT_STRICT);\n>   \n> diff --git a/builtin/rebase.c b/builtin/rebase.c\n> index b800c8bfca..e681ff58de 100644\n> --- a/builtin/rebase.c\n> +++ b/builtin/rebase.c\n> @@ -1371,8 +1371,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n>   \t\t{ OPTION_STRING, 'S', \"gpg-sign\", &gpg_sign, N_(\"key-id\"),\n>   \t\t\tN_(\"GPG-sign commits\"),\n>   \t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n> -\t\tOPT_BOOL(0, \"autostash\", &options.autostash,\n> -\t\t\t N_(\"automatically stash/stash pop before and after\")),\n> +\t\tOPT_AUTOSTASH(&options.autostash),\n>   \t\tOPT_STRING_LIST('x', \"exec\", &exec, N_(\"exec\"),\n>   \t\t\t\tN_(\"add exec lines after each commit of the \"\n>   \t\t\t\t   \"editable list\")),\n> diff --git a/builtin/reset.c b/builtin/reset.c\n> index 18228c312e..0d155101f4 100644\n> --- a/builtin/reset.c\n> +++ b/builtin/reset.c\n> @@ -25,6 +25,7 @@\n>   #include \"cache-tree.h\"\n>   #include \"submodule.h\"\n>   #include \"submodule-config.h\"\n> +#include \"sequencer.h\"\n>   \n>   #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n>   \n> @@ -437,8 +438,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n>   \t\tif (reset_type == HARD && !update_ref_status && !quiet)\n>   \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n>   \t}\n> -\tif (!pathspec.nr)\n> +\tif (!pathspec.nr) {\n>   \t\tremove_branch_state(the_repository, 0);\n> +\t}\n\nNo need to add these braces any longer\n\nBest Wishes\n\nPhillip\n\n>   \n>   \treturn update_ref_status;\n>   }\n> diff --git a/parse-options.h b/parse-options.h\n> index fece5ba628..46af942093 100644\n> --- a/parse-options.h\n> +++ b/parse-options.h\n> @@ -336,5 +336,6 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);\n>   #define OPT_CLEANUP(v) OPT_STRING(0, \"cleanup\", v, N_(\"mode\"), N_(\"how to strip spaces and #comments from message\"))\n>   #define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, \"pathspec-from-file\", v, N_(\"read pathspec from file\"))\n>   #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\"))\n> +#define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n>   \n>   #endif\n> diff --git a/path.c b/path.c\n> index 88cf593007..d764738146 100644\n> --- a/path.c\n> +++ b/path.c\n> @@ -1535,5 +1535,6 @@ REPO_GIT_PATH_FUNC(merge_msg, \"MERGE_MSG\")\n>   REPO_GIT_PATH_FUNC(merge_rr, \"MERGE_RR\")\n>   REPO_GIT_PATH_FUNC(merge_mode, \"MERGE_MODE\")\n>   REPO_GIT_PATH_FUNC(merge_head, \"MERGE_HEAD\")\n> +REPO_GIT_PATH_FUNC(merge_autostash, \"MERGE_AUTOSTASH\")\n>   REPO_GIT_PATH_FUNC(fetch_head, \"FETCH_HEAD\")\n>   REPO_GIT_PATH_FUNC(shallow, \"shallow\")\n> diff --git a/path.h b/path.h\n> index 14d6dcad16..1f1bf8f87a 100644\n> --- a/path.h\n> +++ b/path.h\n> @@ -177,11 +177,12 @@ struct path_cache {\n>   \tconst char *merge_rr;\n>   \tconst char *merge_mode;\n>   \tconst char *merge_head;\n> +\tconst char *merge_autostash;\n>   \tconst char *fetch_head;\n>   \tconst char *shallow;\n>   };\n>   \n> -#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n> +#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n>   \n>   const char *git_path_cherry_pick_head(struct repository *r);\n>   const char *git_path_revert_head(struct repository *r);\n> @@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);\n>   const char *git_path_merge_rr(struct repository *r);\n>   const char *git_path_merge_mode(struct repository *r);\n>   const char *git_path_merge_head(struct repository *r);\n> +const char *git_path_merge_autostash(struct repository *r);\n>   const char *git_path_fetch_head(struct repository *r);\n>   const char *git_path_shallow(struct repository *r);\n>   \n> diff --git a/t/t3033-merge-toplevel.sh b/t/t3033-merge-toplevel.sh\n> index d314599428..e29c284b9b 100755\n> --- a/t/t3033-merge-toplevel.sh\n> +++ b/t/t3033-merge-toplevel.sh\n> @@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' '\n>   \ttest_must_fail git merge five\n>   '\n>   \n> +test_expect_success 'refuse two-project merge by default, quit before --autostash happens' '\n> +\tt3033_reset &&\n> +\tgit reset --hard four &&\n> +\techo change >>one.t &&\n> +\tgit diff >expect &&\n> +\ttest_must_fail git merge --autostash five 2>err &&\n> +\ttest_i18ngrep ! \"stash\" err &&\n> +\tgit diff >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n>   test_expect_success 'two-project merge with --allow-unrelated-histories' '\n>   \tt3033_reset &&\n>   \tgit reset --hard four &&\n> @@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' '\n>   \tgit diff --exit-code five\n>   '\n>   \n> +test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' '\n> +\tt3033_reset &&\n> +\tgit reset --hard four &&\n> +\techo change >>one.t &&\n> +\tgit diff one.t >expect &&\n> +\tgit merge --allow-unrelated-histories --autostash five 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit diff one.t >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n>   test_done\n> diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\n> index 4fa0ef8e3b..d809310df1 100755\n> --- a/t/t7600-merge.sh\n> +++ b/t/t7600-merge.sh\n> @@ -30,13 +30,17 @@ Testing basic merge operations/option parsing.\n>   . \"$TEST_DIRECTORY\"/lib-gpg.sh\n>   \n>   test_write_lines 1 2 3 4 5 6 7 8 9 >file\n> +cp file file.orig\n>   test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1\n> +test_write_lines 1 2 '3 X' 4 5 6 7 8 9 >file.3\n>   test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5\n>   test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9\n>   test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n>   test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1\n>   test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n> +test_write_lines '1 X' 2 3 4 5 6 7 8 '9 X' >result.1-9\n>   test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n> +test_write_lines '1 X' 2 '3 X' 4 '5 X' 6 7 8 '9 X' >result.1-3-5-9\n>   test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n>   \n>   create_merge_msgs () {\n> @@ -675,6 +679,120 @@ test_expect_success 'refresh the index before merging' '\n>   \tgit merge c3\n>   '\n>   \n> +test_expect_success 'merge with --autostash' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9 &&\n> +\tgit merge --autostash c2 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit show HEAD:file >merge-result &&\n> +\ttest_cmp result.1-5 merge-result &&\n> +\ttest_cmp result.1-5-9 file\n> +'\n> +\n> +test_expect_success 'merge with merge.autoStash' '\n> +\ttest_config merge.autoStash true &&\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9 &&\n> +\tgit merge c2 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit show HEAD:file >merge-result &&\n> +\ttest_cmp result.1-5 merge-result &&\n> +\ttest_cmp result.1-5-9 file\n> +'\n> +\n> +test_expect_success 'fast-forward merge with --autostash' '\n> +\tgit reset --hard c0 &&\n> +\tgit merge-file file file.orig file.5 &&\n> +\tgit merge --autostash c1 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\ttest_cmp result.1-5 file\n> +'\n> +\n> +test_expect_success 'octopus merge with --autostash' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.3 &&\n> +\tgit merge --autostash c2 c3 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit show HEAD:file >merge-result &&\n> +\ttest_cmp result.1-5-9 merge-result &&\n> +\ttest_cmp result.1-3-5-9 file\n> +'\n> +\n> +test_expect_success 'conflicted merge with --autostash, --abort restores stash' '\n> +\tgit reset --hard c3 &&\n> +\tcp file.1 file &&\n> +\ttest_must_fail git merge --autostash c7 &&\n> +\tgit merge --abort 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\ttest_cmp file.1 file\n> +'\n> +\n> +test_expect_success 'completed merge (git commit) with --no-commit and --autostash' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9 &&\n> +\tgit diff >expect &&\n> +\tgit merge --no-commit --autostash c2 &&\n> +\tgit stash show -p MERGE_AUTOSTASH >actual &&\n> +\ttest_cmp expect actual &&\n> +\tgit commit 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit show HEAD:file >merge-result &&\n> +\ttest_cmp result.1-5 merge-result &&\n> +\ttest_cmp result.1-5-9 file\n> +'\n> +\n> +test_expect_success 'completed merge (git merge --continue) with --no-commit and --autostash' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9 &&\n> +\tgit diff >expect &&\n> +\tgit merge --no-commit --autostash c2 &&\n> +\tgit stash show -p MERGE_AUTOSTASH >actual &&\n> +\ttest_cmp expect actual &&\n> +\tgit merge --continue 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit show HEAD:file >merge-result &&\n> +\ttest_cmp result.1-5 merge-result &&\n> +\ttest_cmp result.1-5-9 file\n> +'\n> +\n> +test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9 &&\n> +\tgit diff >expect &&\n> +\tgit merge --no-commit --autostash c2 &&\n> +\tgit stash show -p MERGE_AUTOSTASH >actual &&\n> +\ttest_cmp expect actual &&\n> +\tgit merge --abort 2>err &&\n> +\ttest_i18ngrep \"Applied autostash.\" err &&\n> +\tgit diff >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n> +test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9 &&\n> +\tgit diff >expect &&\n> +\tgit merge --no-commit --autostash c2 &&\n> +\tgit stash show -p MERGE_AUTOSTASH >actual &&\n> +\ttest_cmp expect actual &&\n> +\tgit reset --hard 2>err &&\n> +\ttest_i18ngrep \"Autostash exists; creating a new stash entry.\" err &&\n> +\tgit diff --exit-code\n> +'\n> +\n> +test_expect_success 'merge with conflicted --autostash changes' '\n> +\tgit reset --hard c1 &&\n> +\tgit merge-file file file.orig file.9y &&\n> +\tgit diff >expect &&\n> +\ttest_when_finished \"test_might_fail git stash drop\" &&\n> +\tgit merge --autostash c3 2>err &&\n> +\ttest_i18ngrep \"Applying autostash resulted in conflicts.\" err &&\n> +\tgit show HEAD:file >merge-result &&\n> +\ttest_cmp result.1-9 merge-result &&\n> +\tgit stash show -p >actual &&\n> +\ttest_cmp expect actual\n> +'\n> +\n>   cat >expected.branch <<\\EOF\n>   Merge branch 'c5-branch' (early part)\n>   EOF\n> \n"},{"id":"394919","messageId":"20200407130946.GA2503067@generichostname","threadId":"52048","inReplyTo":"07ec44b0-1162-aac9-1a07-579a3768cbd1@gmail.com","subject":"Re: [PATCH v4 21/23] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T13:09:46Z","receivedAt":"2020-04-07T13:09:53Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Phillip,\n\nThanks for the review. The documentation rewordings are particularly\nhelpful.\n\nOn Mon, Apr 06, 2020 at 04:20:24PM +0100, Phillip Wood wrote:\n> >   --quit::\n> >   \tForget about the current merge in progress. Leave the index\n> \n> Should we be saving the stash if the user runs 'git merge --quit'?\n\nYes, this is its current behaviour, although it's undocumented. In my\nnext reroll, I've added a test case and documentation for this. This is\nsimilar to how `git rebase --quit` behaves.\n\nNow that I think about it, when `git rebase --quit` happens, the\nautostash is just dropped out of existence, which is probably\nundesirable behaviour. I'll probably submit a later series to save that\nautostash so that it's not lost forever.\n\nThanks,\n\nDenton\n"},{"id":"394920","messageId":"20200407135611.GB2503067@generichostname","threadId":"52048","inReplyTo":"1c382358-08ca-8bf8-cbd6-d00406472bcd@gmail.com","subject":"Re: [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T13:56:11Z","receivedAt":"2020-04-07T13:56:21Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi Phillip,\n\nOn Mon, Apr 06, 2020 at 03:42:59PM +0100, Phillip Wood wrote:\n> Hi Denton\n> \n> On 06/04/2020 15:03, Phillip Wood wrote:\n> > Hi Denton\n> > \n> > On 04/04/2020 02:11, Denton Liu wrote:\n> > > In the original read_oneliner() logic, we duplicated the logic for\n> > > strbuf_trim_trailing_newline() with one exception: instead of preventing\n> > > buffer accesses below index 0, it would prevent buffer accesses below\n> > > index `orig_len`. Although this is correct, it isn't worth having the\n> > > duplicated logic around.\n> > > \n> > > Reset `buf` before using it then replace the trimming logic with\n> > > strbuf_trim().\n> > \n> > I should have picked up on this before but this changes the semantics of\n> > the function as it strips all whitespace from the start and end of the\n> > strbuf. Above you talked about using strbuf_trim_trailing_newline()\n> > instead which would not change the semantics of this function. You could\n> > test to see if we've read anything and only call\n> > strbuf_trim_trailing_newline() in that case without messing with\n> > strbuf_reset(). (there is a corner case where if the buffer ends with\n> > '\\r' when the function is called and it reads a single '\\n' then the\n> > '\\r' would be stripped as well but I think that is unlikely to happen in\n> > the wild)\n> \n> I'd also be fine with dropping this patch and leaving the trimming code as\n> is\n\nYeah, I'll just drop this patch. I don't think it's worth holding up the\nrest of the series on this small detail. I'll probably try to resurrect\nthis at a later time.\n\nThanks,\n\nDenton\n\n> Best Wishes\n> \n> Phillip\n"},{"id":"394930","messageId":"059e0e8e43f633086fc297f8328288047b45d0e0.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 02/22] t7600: use test_write_lines()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:49Z","receivedAt":"2020-04-07T14:28:23Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In t7600, we were rewriting `printf '%s\\n' ...` to create files from\nparameters, one per line. However, we already have a function that wraps\nthis for us: test_write_lines(). Rewrite these instances to use that\nfunction instead of open coding it.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t7600-merge.sh | 22 +++++++++++-----------\n 1 file changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 132608879a..4fa0ef8e3b 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -29,15 +29,15 @@ Testing basic merge operations/option parsing.\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-gpg.sh\n \n-printf '%s\\n' 1 2 3 4 5 6 7 8 9 >file\n-printf '%s\\n' '1 X' 2 3 4 5 6 7 8 9 >file.1\n-printf '%s\\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5\n-printf '%s\\n' 1 2 3 4 5 6 7 8 '9 X' >file.9\n-printf '%s\\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n-printf '%s\\n' '1 X' 2 3 4 5 6 7 8 9 >result.1\n-printf '%s\\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n-printf '%s\\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n-printf '%s\\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n+test_write_lines 1 2 3 4 5 6 7 8 9 >file\n+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1\n+test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5\n+test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9\n+test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n+test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1\n+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n+test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n+test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n \n create_merge_msgs () {\n \techo \"Merge tag 'c2'\" >msg.1-5 &&\n@@ -81,7 +81,7 @@ verify_head () {\n }\n \n verify_parents () {\n-\tprintf '%s\\n' \"$@\" >parents.expected &&\n+\ttest_write_lines \"$@\" >parents.expected &&\n \t>parents.actual &&\n \ti=1 &&\n \twhile test $i -le $#\n@@ -95,7 +95,7 @@ verify_parents () {\n }\n \n verify_mergeheads () {\n-\tprintf '%s\\n' \"$@\" >mergehead.expected &&\n+\ttest_write_lines \"$@\" >mergehead.expected &&\n \twhile read sha1 rest\n \tdo\n \t\tgit rev-parse $sha1\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394931","messageId":"c26771e9340d313c9c9ec30decc0224b1ae41e8b.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 01/22] Makefile: ASCII-sort += lists","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:48Z","receivedAt":"2020-04-07T14:28:24Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"There are many += lists in the Makefile and, over time, they have gotten\nslightly out of ASCII order. Sort all += lists to bring them back in\norder.\n\nASCII sorting was chosen over strict alphabetical order even though, if\nwe omit file prefixes, the lists aren't sorted in strictly alphabetical\norder (e.g. archive.o comes after archive-zip.o instead of before\narchive-tar.o). This is intentional because the purpose of maintaining\nthe sorted list is to ensure line insertions are deterministic. By using\nASCII ordering, it is more easily mechanically reproducible in the\nfuture, such as by using :sort in Vim.\n\nThis patch is best viewed with `--color-moved`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Makefile | 77 ++++++++++++++++++++++++++++----------------------------\n 1 file changed, 39 insertions(+), 38 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 9804a0758b..f709fc3f77 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -604,12 +604,12 @@ unexport CDPATH\n SCRIPT_SH += git-bisect.sh\n SCRIPT_SH += git-difftool--helper.sh\n SCRIPT_SH += git-filter-branch.sh\n+SCRIPT_SH += git-legacy-stash.sh\n SCRIPT_SH += git-merge-octopus.sh\n SCRIPT_SH += git-merge-one-file.sh\n SCRIPT_SH += git-merge-resolve.sh\n SCRIPT_SH += git-mergetool.sh\n SCRIPT_SH += git-quiltimport.sh\n-SCRIPT_SH += git-legacy-stash.sh\n SCRIPT_SH += git-request-pull.sh\n SCRIPT_SH += git-submodule.sh\n SCRIPT_SH += git-web--browse.sh\n@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh\n SCRIPT_LIB += git-mergetool--lib\n SCRIPT_LIB += git-parse-remote\n SCRIPT_LIB += git-rebase--preserve-merges\n-SCRIPT_LIB += git-sh-setup\n SCRIPT_LIB += git-sh-i18n\n+SCRIPT_LIB += git-sh-setup\n \n SCRIPT_PERL += git-add--interactive.perl\n SCRIPT_PERL += git-archimport.perl\n@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o\n PROGRAM_OBJS += fast-import.o\n PROGRAM_OBJS += http-backend.o\n PROGRAM_OBJS += imap-send.o\n+PROGRAM_OBJS += remote-testsvn.o\n PROGRAM_OBJS += sh-i18n--envsubst.o\n PROGRAM_OBJS += shell.o\n-PROGRAM_OBJS += remote-testsvn.o\n \n # Binary suffix, set to .exe for Windows builds\n X =\n@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o\n TEST_BUILTINS_OBJS += test-example-decorate.o\n TEST_BUILTINS_OBJS += test-genrandom.o\n TEST_BUILTINS_OBJS += test-genzeros.o\n+TEST_BUILTINS_OBJS += test-hash-speed.o\n TEST_BUILTINS_OBJS += test-hash.o\n TEST_BUILTINS_OBJS += test-hashmap.o\n-TEST_BUILTINS_OBJS += test-hash-speed.o\n TEST_BUILTINS_OBJS += test-index-version.o\n TEST_BUILTINS_OBJS += test-json-writer.o\n TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o\n@@ -737,8 +737,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o\n TEST_BUILTINS_OBJS += test-run-command.o\n TEST_BUILTINS_OBJS += test-scrap-cache-tree.o\n TEST_BUILTINS_OBJS += test-serve-v2.o\n-TEST_BUILTINS_OBJS += test-sha1.o\n TEST_BUILTINS_OBJS += test-sha1-array.o\n+TEST_BUILTINS_OBJS += test-sha1.o\n TEST_BUILTINS_OBJS += test-sha256.o\n TEST_BUILTINS_OBJS += test-sigchain.o\n TEST_BUILTINS_OBJS += test-strcmp-offset.o\n@@ -748,10 +748,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o\n TEST_BUILTINS_OBJS += test-subprocess.o\n TEST_BUILTINS_OBJS += test-trace2.o\n TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n-TEST_BUILTINS_OBJS += test-xml-encode.o\n TEST_BUILTINS_OBJS += test-wildmatch.o\n TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n TEST_BUILTINS_OBJS += test-write-cache.o\n+TEST_BUILTINS_OBJS += test-xml-encode.o\n \n # Do not add more tests here unless they have extra dependencies. Add\n # them in TEST_BUILTINS_OBJS above.\n@@ -788,10 +788,10 @@ OTHER_PROGRAMS = git$X\n \n # what test wrappers are needed and 'install' will install, in bindir\n BINDIR_PROGRAMS_NEED_X += git\n-BINDIR_PROGRAMS_NEED_X += git-upload-pack\n BINDIR_PROGRAMS_NEED_X += git-receive-pack\n-BINDIR_PROGRAMS_NEED_X += git-upload-archive\n BINDIR_PROGRAMS_NEED_X += git-shell\n+BINDIR_PROGRAMS_NEED_X += git-upload-archive\n+BINDIR_PROGRAMS_NEED_X += git-upload-pack\n \n BINDIR_PROGRAMS_NO_X += git-cvsserver\n \n@@ -831,9 +831,9 @@ LIB_OBJS += advice.o\n LIB_OBJS += alias.o\n LIB_OBJS += alloc.o\n LIB_OBJS += apply.o\n-LIB_OBJS += archive.o\n LIB_OBJS += archive-tar.o\n LIB_OBJS += archive-zip.o\n+LIB_OBJS += archive.o\n LIB_OBJS += argv-array.o\n LIB_OBJS += attr.o\n LIB_OBJS += base85.o\n@@ -849,9 +849,9 @@ LIB_OBJS += checkout.o\n LIB_OBJS += color.o\n LIB_OBJS += column.o\n LIB_OBJS += combine-diff.o\n-LIB_OBJS += commit.o\n LIB_OBJS += commit-graph.o\n LIB_OBJS += commit-reach.o\n+LIB_OBJS += commit.o\n LIB_OBJS += compat/obstack.o\n LIB_OBJS += compat/terminal.o\n LIB_OBJS += config.o\n@@ -865,17 +865,17 @@ LIB_OBJS += ctype.o\n LIB_OBJS += date.o\n LIB_OBJS += decorate.o\n LIB_OBJS += delta-islands.o\n+LIB_OBJS += diff-delta.o\n+LIB_OBJS += diff-lib.o\n+LIB_OBJS += diff-no-index.o\n+LIB_OBJS += diff.o\n LIB_OBJS += diffcore-break.o\n LIB_OBJS += diffcore-delta.o\n LIB_OBJS += diffcore-order.o\n LIB_OBJS += diffcore-pickaxe.o\n LIB_OBJS += diffcore-rename.o\n-LIB_OBJS += diff-delta.o\n-LIB_OBJS += diff-lib.o\n-LIB_OBJS += diff-no-index.o\n-LIB_OBJS += diff.o\n-LIB_OBJS += dir.o\n LIB_OBJS += dir-iterator.o\n+LIB_OBJS += dir.o\n LIB_OBJS += editor.o\n LIB_OBJS += entry.o\n LIB_OBJS += environment.o\n@@ -893,7 +893,6 @@ LIB_OBJS += gpg-interface.o\n LIB_OBJS += graph.o\n LIB_OBJS += grep.o\n LIB_OBJS += hashmap.o\n-LIB_OBJS += linear-assignment.o\n LIB_OBJS += help.o\n LIB_OBJS += hex.o\n LIB_OBJS += ident.o\n@@ -903,9 +902,10 @@ LIB_OBJS += kwset.o\n LIB_OBJS += levenshtein.o\n LIB_OBJS += line-log.o\n LIB_OBJS += line-range.o\n-LIB_OBJS += list-objects.o\n-LIB_OBJS += list-objects-filter.o\n+LIB_OBJS += linear-assignment.o\n LIB_OBJS += list-objects-filter-options.o\n+LIB_OBJS += list-objects-filter.o\n+LIB_OBJS += list-objects.o\n LIB_OBJS += ll-merge.o\n LIB_OBJS += lockfile.o\n LIB_OBJS += log-tree.o\n@@ -914,31 +914,31 @@ LIB_OBJS += mailinfo.o\n LIB_OBJS += mailmap.o\n LIB_OBJS += match-trees.o\n LIB_OBJS += mem-pool.o\n-LIB_OBJS += merge.o\n LIB_OBJS += merge-blobs.o\n LIB_OBJS += merge-recursive.o\n+LIB_OBJS += merge.o\n LIB_OBJS += mergesort.o\n LIB_OBJS += midx.o\n LIB_OBJS += name-hash.o\n LIB_OBJS += negotiator/default.o\n LIB_OBJS += negotiator/skipping.o\n-LIB_OBJS += notes.o\n LIB_OBJS += notes-cache.o\n LIB_OBJS += notes-merge.o\n LIB_OBJS += notes-utils.o\n+LIB_OBJS += notes.o\n LIB_OBJS += object.o\n LIB_OBJS += oidmap.o\n LIB_OBJS += oidset.o\n-LIB_OBJS += packfile.o\n-LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-bitmap-write.o\n+LIB_OBJS += pack-bitmap.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-objects.o\n LIB_OBJS += pack-revindex.o\n LIB_OBJS += pack-write.o\n+LIB_OBJS += packfile.o\n LIB_OBJS += pager.o\n-LIB_OBJS += parse-options.o\n LIB_OBJS += parse-options-cb.o\n+LIB_OBJS += parse-options.o\n LIB_OBJS += patch-delta.o\n LIB_OBJS += patch-ids.o\n LIB_OBJS += path.o\n@@ -955,8 +955,9 @@ LIB_OBJS += quote.o\n LIB_OBJS += range-diff.o\n LIB_OBJS += reachable.o\n LIB_OBJS += read-cache.o\n-LIB_OBJS += rebase.o\n LIB_OBJS += rebase-interactive.o\n+LIB_OBJS += rebase.o\n+LIB_OBJS += ref-filter.o\n LIB_OBJS += reflog-walk.o\n LIB_OBJS += refs.o\n LIB_OBJS += refs/files-backend.o\n@@ -964,7 +965,6 @@ LIB_OBJS += refs/iterator.o\n LIB_OBJS += refs/packed-backend.o\n LIB_OBJS += refs/ref-cache.o\n LIB_OBJS += refspec.o\n-LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace-object.o\n LIB_OBJS += repo-settings.o\n@@ -979,8 +979,8 @@ LIB_OBJS += serve.o\n LIB_OBJS += server-info.o\n LIB_OBJS += setup.o\n LIB_OBJS += sha1-array.o\n-LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1-file.o\n+LIB_OBJS += sha1-lookup.o\n LIB_OBJS += sha1-name.o\n LIB_OBJS += shallow.o\n LIB_OBJS += sideband.o\n@@ -990,9 +990,9 @@ LIB_OBJS += stable-qsort.o\n LIB_OBJS += strbuf.o\n LIB_OBJS += streaming.o\n LIB_OBJS += string-list.o\n-LIB_OBJS += submodule.o\n-LIB_OBJS += submodule-config.o\n LIB_OBJS += sub-process.o\n+LIB_OBJS += submodule-config.o\n+LIB_OBJS += submodule.o\n LIB_OBJS += symlinks.o\n LIB_OBJS += tag.o\n LIB_OBJS += tempfile.o\n@@ -1011,11 +1011,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o\n LIB_OBJS += trace2/tr2_tgt_perf.o\n LIB_OBJS += trace2/tr2_tls.o\n LIB_OBJS += trailer.o\n-LIB_OBJS += transport.o\n LIB_OBJS += transport-helper.o\n+LIB_OBJS += transport.o\n LIB_OBJS += tree-diff.o\n-LIB_OBJS += tree.o\n LIB_OBJS += tree-walk.o\n+LIB_OBJS += tree.o\n LIB_OBJS += unpack-trees.o\n LIB_OBJS += upload-pack.o\n LIB_OBJS += url.o\n@@ -1055,9 +1055,9 @@ BUILTIN_OBJS += builtin/checkout.o\n BUILTIN_OBJS += builtin/clean.o\n BUILTIN_OBJS += builtin/clone.o\n BUILTIN_OBJS += builtin/column.o\n+BUILTIN_OBJS += builtin/commit-graph.o\n BUILTIN_OBJS += builtin/commit-tree.o\n BUILTIN_OBJS += builtin/commit.o\n-BUILTIN_OBJS += builtin/commit-graph.o\n BUILTIN_OBJS += builtin/config.o\n BUILTIN_OBJS += builtin/count-objects.o\n BUILTIN_OBJS += builtin/credential.o\n@@ -1088,13 +1088,13 @@ BUILTIN_OBJS += builtin/ls-remote.o\n BUILTIN_OBJS += builtin/ls-tree.o\n BUILTIN_OBJS += builtin/mailinfo.o\n BUILTIN_OBJS += builtin/mailsplit.o\n-BUILTIN_OBJS += builtin/merge.o\n BUILTIN_OBJS += builtin/merge-base.o\n BUILTIN_OBJS += builtin/merge-file.o\n BUILTIN_OBJS += builtin/merge-index.o\n BUILTIN_OBJS += builtin/merge-ours.o\n BUILTIN_OBJS += builtin/merge-recursive.o\n BUILTIN_OBJS += builtin/merge-tree.o\n+BUILTIN_OBJS += builtin/merge.o\n BUILTIN_OBJS += builtin/mktag.o\n BUILTIN_OBJS += builtin/mktree.o\n BUILTIN_OBJS += builtin/multi-pack-index.o\n@@ -1114,9 +1114,9 @@ BUILTIN_OBJS += builtin/read-tree.o\n BUILTIN_OBJS += builtin/rebase.o\n BUILTIN_OBJS += builtin/receive-pack.o\n BUILTIN_OBJS += builtin/reflog.o\n-BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/remote-ext.o\n BUILTIN_OBJS += builtin/remote-fd.o\n+BUILTIN_OBJS += builtin/remote.o\n BUILTIN_OBJS += builtin/repack.o\n BUILTIN_OBJS += builtin/replace.o\n BUILTIN_OBJS += builtin/rerere.o\n@@ -2335,16 +2335,16 @@ reconfigure config.mak.autogen: config.status\n endif\n \n XDIFF_OBJS += xdiff/xdiffi.o\n-XDIFF_OBJS += xdiff/xprepare.o\n-XDIFF_OBJS += xdiff/xutils.o\n XDIFF_OBJS += xdiff/xemit.o\n+XDIFF_OBJS += xdiff/xhistogram.o\n XDIFF_OBJS += xdiff/xmerge.o\n XDIFF_OBJS += xdiff/xpatience.o\n-XDIFF_OBJS += xdiff/xhistogram.o\n+XDIFF_OBJS += xdiff/xprepare.o\n+XDIFF_OBJS += xdiff/xutils.o\n \n+VCSSVN_OBJS += vcs-svn/fast_export.o\n VCSSVN_OBJS += vcs-svn/line_buffer.o\n VCSSVN_OBJS += vcs-svn/sliding_window.o\n-VCSSVN_OBJS += vcs-svn/fast_export.o\n VCSSVN_OBJS += vcs-svn/svndiff.o\n VCSSVN_OBJS += vcs-svn/svndump.o\n \n@@ -3152,9 +3152,10 @@ endif\n #\n ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS)\n ALL_COMMANDS += git\n+ALL_COMMANDS += git-citool\n+ALL_COMMANDS += git-gui\n ALL_COMMANDS += gitk\n ALL_COMMANDS += gitweb\n-ALL_COMMANDS += git-gui git-citool\n \n .PHONY: check-docs\n check-docs::\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394932","messageId":"cover.1586269542.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1585962672.git.liu.denton@gmail.com","subject":"[PATCH v5 00/22] merge: learn --autostash","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:47Z","receivedAt":"2020-04-07T14:28:24Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Alban reported[1] that he expected merge to have an --autostash option,\njust like rebase. Since there's not really any reason why merge can't\nhave it, let's implement it in this patchset.\n\nThe majority of this patchset is spent refactoring. This is so that the\nactual implementation in merge is done as simply as possible.\n\nChagnes since v4:\n\n* Drop 'sequencer: reuse strbuf_trim() in read_oneliner()'\n\n* Fix commit message in 'sequencer: make apply_autostash() accept a path'\n\n* Add more detail in commit message 'sequencer: implement save_autostash()'\n\n* In 'merge: teach --autostash option', improve documentation, remove\n  builtin/reset.c changes, add a `git merge --quit` test case\n\nChanges since v3:\n\n* Replace 'sequencer: use file strbuf for read_oneliner()' with patches 3-5\n\n* Rename flag to READ_ONELINER_WARN_MISSING\n\n* Add 'sequencer: rename stash_sha1 to stash_oid'\n\n* Many changes to 'merge: teach --autostash option'\n\nChanges since v2:\n\n* Change patch 1 to refer to ASCII order[2]\n\n* Make read_oneliner() accept a collective flags arg instead of two\n  separate args[3]\n\n* `git reset --hard` now leaves the worktree clean[4]\n\n* Rebased against 'master' since the base is quite old and a new release is\n  coming out soon\n\nChanges since v1:\n\n* Completely changed how the refactoring was done\n\n* More tests and documentation\n\n[1]: https://github.com/gitgitgadget/git/issues/394\n[2]: https://lore.kernel.org/git/xmqqwoadfq8n.fsf@gitster-ct.c.googlers.com/\n[3]: https://lore.kernel.org/git/xmqqwoaikehz.fsf@gitster-ct.c.googlers.com/\n[4]: https://lore.kernel.org/git/20200123042906.GA29009@generichostname/\n\nDenton Liu (22):\n  Makefile: ASCII-sort += lists\n  t7600: use test_write_lines()\n  sequencer: stop leaking buf\n  sequencer: make file exists check more efficient\n  sequencer: make read_oneliner() accept flags\n  sequencer: configurably warn on non-existent files\n  sequencer: make read_oneliner() extern\n  rebase: use read_oneliner()\n  sequencer: make apply_autostash() accept a path\n  sequencer: rename stash_sha1 to stash_oid\n  rebase: use apply_autostash() from sequencer.c\n  rebase: generify reset_head()\n  reset: extract reset_head() from rebase\n  rebase: extract create_autostash()\n  rebase: generify create_autostash()\n  sequencer: extract perform_autostash() from rebase\n  sequencer: unlink autostash in apply_autostash()\n  sequencer: implement save_autostash()\n  sequencer: implement apply_autostash_oid()\n  merge: teach --autostash option\n  t5520: make test_pull_autostash() accept expect_parent_num\n  pull: pass --autostash to merge\n\n Documentation/config/merge.txt  |  10 ++\n Documentation/git-merge.txt     |  11 +-\n Documentation/git-pull.txt      |   9 -\n Documentation/merge-options.txt |   8 +\n Makefile                        |  78 ++++----\n branch.c                        |   1 +\n builtin/commit.c                |   2 +\n builtin/merge.c                 |  25 +++\n builtin/pull.c                  |   9 +-\n builtin/rebase.c                | 307 +++++---------------------------\n parse-options.h                 |   1 +\n path.c                          |   1 +\n path.h                          |   4 +-\n reset.c                         | 140 +++++++++++++++\n reset.h                         |  20 +++\n sequencer.c                     | 176 +++++++++++++-----\n sequencer.h                     |  20 +++\n t/t3033-merge-toplevel.sh       |  22 +++\n t/t5520-pull.sh                 |  57 ++++--\n t/t7600-merge.sh                | 154 ++++++++++++++--\n 20 files changed, 665 insertions(+), 390 deletions(-)\n create mode 100644 reset.c\n create mode 100644 reset.h\n\nRange-diff against v4:\n 1:  c26771e934 =  1:  c26771e934 Makefile: ASCII-sort += lists\n 2:  059e0e8e43 =  2:  059e0e8e43 t7600: use test_write_lines()\n 3:  76585e5b13 =  3:  76585e5b13 sequencer: stop leaking buf\n 4:  c7a3cfa200 <  -:  ---------- sequencer: reuse strbuf_trim() in read_oneliner()\n 5:  58da8e9555 !  4:  dc4674d222 sequencer: make file exists check more efficient\n    @@ Commit message\n         check `errno` to see if the file doesn't exist.\n     \n      ## sequencer.c ##\n    -@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,\n    - static int read_oneliner(struct strbuf *buf,\n    - \tconst char *path, int skip_if_empty)\n    +@@ sequencer.c: static int read_oneliner(struct strbuf *buf,\n      {\n    --\n    + \tint orig_len = buf->len;\n    + \n     -\tif (!file_exists(path))\n     -\t\treturn 0;\n     -\n    - \tstrbuf_reset(buf);\n      \tif (strbuf_read_file(buf, path, 0) < 0) {\n     -\t\twarning_errno(_(\"could not read '%s'\"), path);\n     +\t\tif (errno != ENOENT && errno != ENOTDIR)\n 6:  6ead240957 !  5:  f30ad07823 sequencer: make read_oneliner() accept flags\n    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n     +#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n     +\n      /*\n    -  * Resets a strbuf then reads a file that was presumably written by a shell\n    -  * script, i.e. with an end-of-line marker that needs to be stripped.\n    +  * Reads a file that was presumably written by a shell script, i.e. with an\n    +  * end-of-line marker that needs to be stripped.\n     @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,\n       * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n       */\n    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n     -\tconst char *path, int skip_if_empty)\n     +\tconst char *path, unsigned flags)\n      {\n    - \tstrbuf_reset(buf);\n    - \tif (strbuf_read_file(buf, path, 0) < 0) {\n    + \tint orig_len = buf->len;\n    + \n     @@ sequencer.c: static int read_oneliner(struct strbuf *buf,\n    + \t\tbuf->buf[buf->len] = '\\0';\n    + \t}\n      \n    - \tstrbuf_trim(buf);\n    - \n    --\tif (skip_if_empty && !buf->len)\n    -+\tif ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !buf->len)\n    +-\tif (skip_if_empty && buf->len == orig_len)\n    ++\tif ((flags & READ_ONELINER_SKIP_IF_EMPTY) && buf->len == orig_len)\n      \t\treturn 0;\n      \n      \treturn 1;\n    @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n      \t\t\t\tstrbuf_reset(&buf);\n      \t\t\telse {\n     @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n    - \t\t\t}\n    + \t\t\tstrbuf_reset(&buf);\n      \t\t}\n      \n     -\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {\n    @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n      \t\t\t\topts->allow_rerere_auto = RERERE_AUTOUPDATE;\n      \t\t\telse if (!strcmp(buf.buf, \"--no-rerere-autoupdate\"))\n     @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n    - \t\tread_strategy_opts(opts, &buf);\n    + \t\tstrbuf_reset(&buf);\n      \n      \t\tif (read_oneliner(&opts->current_fixups,\n     -\t\t\t\t  rebase_path_current_fixups(), 1)) {\n 7:  2e7922b259 !  6:  8c504427e3 sequencer: configurably warn on non-existent files\n    @@ Commit message\n         read_oneliner() will want the ability to output warnings in the event\n         that the `path` doesn't exist. Introduce the\n         `READ_ONELINER_WARN_MISSING` flag which, if active, would issue a\n    -    warning when a file doesn't exist by skipping the `!file_exists()` check\n    -    and letting `strbuf_read_file()` handle that case.\n    +    warning when a file doesn't exist by always executing warning_errno()\n    +    in the case where strbuf_read_file() fails.\n     \n      ## sequencer.c ##\n     @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,\n    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n     +#define READ_ONELINER_WARN_MISSING (1 << 1)\n      \n      /*\n    -  * Resets a strbuf then reads a file that was presumably written by a shell\n    +  * Reads a file that was presumably written by a shell script, i.e. with an\n     @@ sequencer.c: static int read_oneliner(struct strbuf *buf,\n    - {\n    - \tstrbuf_reset(buf);\n    + \tint orig_len = buf->len;\n    + \n      \tif (strbuf_read_file(buf, path, 0) < 0) {\n     -\t\tif (errno != ENOENT && errno != ENOTDIR)\n     +\t\tif ((flags & READ_ONELINER_WARN_MISSING) ||\n    -+\t\t\t\t(errno != ENOENT && errno != ENOTDIR))\n    ++\t\t    (errno != ENOENT && errno != ENOTDIR))\n      \t\t\twarning_errno(_(\"could not read '%s'\"), path);\n      \t\treturn 0;\n      \t}\n 8:  6c3c37994b !  7:  50e93770e7 sequencer: make read_oneliner() extern\n    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n     -#define READ_ONELINER_WARN_MISSING (1 << 1)\n     -\n     -/*\n    -- * Resets a strbuf then reads a file that was presumably written by a shell\n    -- * script, i.e. with an end-of-line marker that needs to be stripped.\n    +- * Reads a file that was presumably written by a shell script, i.e. with an\n    +- * end-of-line marker that needs to be stripped.\n     - *\n     - * Note that only the last end-of-line marker is stripped, consistent with the\n     - * behavior of \"$(cat path)\" in a shell script.\n    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n     +int read_oneliner(struct strbuf *buf,\n      \tconst char *path, unsigned flags)\n      {\n    - \tstrbuf_reset(buf);\n    + \tint orig_len = buf->len;\n     \n      ## sequencer.h ##\n     @@ sequencer.h: void print_commit_summary(struct repository *repo,\n    @@ sequencer.h: void print_commit_summary(struct repository *repo,\n     +#define READ_ONELINER_WARN_MISSING (1 << 1)\n     +\n     +/*\n    -+ * Resets a strbuf then reads a file that was presumably written by a shell\n    -+ * script, i.e. with an end-of-line marker that needs to be stripped.\n    ++ * Reads a file that was presumably written by a shell script, i.e. with an\n    ++ * end-of-line marker that needs to be stripped.\n     + *\n     + * Note that only the last end-of-line marker is stripped, consistent with the\n     + * behavior of \"$(cat path)\" in a shell script.\n 9:  689f34a2a5 !  8:  0cc279fc14 rebase: use read_oneliner()\n    @@ Commit message\n     \n         Since in sequencer.c, read_one() basically duplicates the functionality\n         of read_oneliner(), reduce code duplication by replacing read_one() with\n    -    read_oneliner(). Also, delete strbuf_reset() calls prior to\n    -    read_oneliner() as read_oneliner() already resets the strbuf.\n    +    read_oneliner().\n     \n    -    This was mostly done with the following Coccinelle script\n    +    This was done with the following Coccinelle script\n     \n                 @@\n                 expression a, b;\n    @@ Commit message\n                 - read_one(a, b)\n                 + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)\n     \n    -    Long lines were broken up and strbuf_reset()s were deleted manually.\n    +    and long lines were manually broken up.\n     \n      ## builtin/rebase.c ##\n     @@ builtin/rebase.c: static const char *state_dir_path(const char *filename, struct rebase_options *o\n    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n      \topts->head_name = starts_with(head_name.buf, \"refs/\") ?\n      \t\txstrdup(head_name.buf) : NULL;\n     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n    - \t * head. Fall back to reading from head to cover for the case that the\n    - \t * user upgraded git with an ongoing interactive rebase.\n      \t */\n    --\tstrbuf_reset(&buf);\n    + \tstrbuf_reset(&buf);\n      \tif (file_exists(state_dir_path(\"orig-head\", opts))) {\n     -\t\tif (read_one(state_dir_path(\"orig-head\", opts), &buf))\n     +\t\tif (!read_oneliner(&buf, state_dir_path(\"orig-head\", opts),\n    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n      \tif (get_oid(buf.buf, &opts->orig_head))\n      \t\treturn error(_(\"invalid orig-head: '%s'\"), buf.buf);\n     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n    - \t}\n      \n      \tif (file_exists(state_dir_path(\"allow_rerere_autoupdate\", opts))) {\n    --\t\tstrbuf_reset(&buf);\n    + \t\tstrbuf_reset(&buf);\n     -\t\tif (read_one(state_dir_path(\"allow_rerere_autoupdate\", opts),\n     -\t\t\t    &buf))\n     +\t\tif (!read_oneliner(&buf, state_dir_path(\"allow_rerere_autoupdate\", opts),\n    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n      \t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n      \t\t\topts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;\n     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n    - \t}\n      \n      \tif (file_exists(state_dir_path(\"gpg_sign_opt\", opts))) {\n    --\t\tstrbuf_reset(&buf);\n    + \t\tstrbuf_reset(&buf);\n     -\t\tif (read_one(state_dir_path(\"gpg_sign_opt\", opts),\n     -\t\t\t    &buf))\n     +\t\tif (!read_oneliner(&buf, state_dir_path(\"gpg_sign_opt\", opts),\n    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n      \t\t\treturn -1;\n      \t\tfree(opts->gpg_sign_opt);\n      \t\topts->gpg_sign_opt = xstrdup(buf.buf);\n    - \t}\n    +@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n      \n      \tif (file_exists(state_dir_path(\"strategy\", opts))) {\n    --\t\tstrbuf_reset(&buf);\n    + \t\tstrbuf_reset(&buf);\n     -\t\tif (read_one(state_dir_path(\"strategy\", opts), &buf))\n     +\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy\", opts),\n     +\t\t\t\t   READ_ONELINER_WARN_MISSING))\n      \t\t\treturn -1;\n      \t\tfree(opts->strategy);\n      \t\topts->strategy = xstrdup(buf.buf);\n    - \t}\n    +@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n      \n      \tif (file_exists(state_dir_path(\"strategy_opts\", opts))) {\n    --\t\tstrbuf_reset(&buf);\n    + \t\tstrbuf_reset(&buf);\n     -\t\tif (read_one(state_dir_path(\"strategy_opts\", opts), &buf))\n     +\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy_opts\", opts),\n     +\t\t\t\t   READ_ONELINER_WARN_MISSING))\n10:  e0b8e409af !  9:  3dbed2a832 sequencer: make apply_rebase() accept a path\n    @@ Metadata\n     Author: Denton Liu <liu.denton@gmail.com>\n     \n      ## Commit message ##\n    -    sequencer: make apply_rebase() accept a path\n    +    sequencer: make apply_autostash() accept a path\n     \n    -    In order to make apply_rebase() more generic for future extraction, make\n    +    In order to make apply_autostash() more generic for future extraction, make\n         it accept a `path` argument so that the location from where to read the\n         reference to the autostash commit can be customized. Remove the `opts`\n         argument since it was unused before anyway.\n11:  a52583beff = 10:  937b4964d1 sequencer: rename stash_sha1 to stash_oid\n12:  389b17df33 = 11:  98a7f5280c rebase: use apply_autostash() from sequencer.c\n13:  fe57e21dbc = 12:  67f3b5c225 rebase: generify reset_head()\n14:  045668620d = 13:  af5e808667 reset: extract reset_head() from rebase\n15:  d5f51cd80e = 14:  114d9a7655 rebase: extract create_autostash()\n16:  9ab10d23d4 = 15:  c2ed4cd39d rebase: generify create_autostash()\n17:  26cca49be6 = 16:  3be4a27dfe sequencer: extract perform_autostash() from rebase\n18:  e703022fda = 17:  129d5d97ae sequencer: unlink autostash in apply_autostash()\n19:  75dc3f10a1 ! 18:  7e04ce8d8e sequencer: implement save_autostash()\n    @@ Commit message\n         function will be used in a future commit.\n     \n         The difference between save_autostash() and apply_autostash() is that\n    -    the latter does not try to apply the stash. It skips that step and\n    +    the former does not try to apply the stash. It skips that step and\n         just stores the created entry in the stash reflog.\n     \n    +    This is useful in the case where we abort an operation when an autostash\n    +    is present but we don't want to dirty the worktree with the application\n    +    of the stash. For example, in a future commit, we will implement\n    +    `git merge --autostash`. Since merges can be aborted using\n    +    `git reset --hard`, we'd make use of save_autostash() to save the\n    +    autostash entry instead of applying it to the worktree thus keeping the\n    +    worktree undirtied.\n    +\n      ## sequencer.c ##\n     @@ sequencer.c: void create_autostash(struct repository *r, const char *path,\n      \tstrbuf_release(&buf);\n20:  598ddea6c1 = 19:  732b3f9945 sequencer: implement apply_autostash_oid()\n21:  7adf794192 ! 20:  f9f698b79a merge: teach --autostash option\n    @@ Commit message\n         `git reset --hard`, the autostash is saved into the stash reflog\n         instead keeping the worktree clean.\n     \n    +    Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n         Suggested-by: Alban Gruin <alban.gruin@gmail.com>\n     \n      ## Documentation/config/merge.txt ##\n    @@ Documentation/git-merge.txt: will be appended to the specified message.\n      \tAbort the current conflict resolution process, and\n     -\ttry to reconstruct the pre-merge state.\n     +\ttry to reconstruct the pre-merge state. If an autostash entry is\n    -+\tpresent, apply it back to the worktree.\n    ++\tpresent, apply it to the worktree.\n      +\n      If there were uncommitted worktree changes present when the merge\n      started, 'git merge --abort' will in some cases be unable to\n    @@ Documentation/git-merge.txt: reconstruct these changes. It is therefore recommen\n      +\n      'git merge --abort' is equivalent to 'git reset --merge' when\n     -`MERGE_HEAD` is present.\n    -+`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present where\n    -+the stash entry is applied to the worktree.\n    ++`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present in\n    ++which case 'git merge --abort' applies the stash entry to the worktree\n    ++whereas 'git reset --merge' will save the stashed changes in the stash\n    ++reflog.\n      \n      --quit::\n      \tForget about the current merge in progress. Leave the index\n    +-\tand the working tree as-is.\n    ++\tand the working tree as-is. If `MERGE_AUTOSTASH` is present, the\n    ++\tstash entry will be saved to the stash reflog.\n    + \n    + --continue::\n    + \tAfter a 'git merge' stops due to conflicts you can conclude the\n     \n      ## Documentation/merge-options.txt ##\n     @@ Documentation/merge-options.txt: ifndef::git-pull[]\n    @@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)\n      \t\t\tdie(_(\"There is no merge to abort (MERGE_HEAD missing).\"));\n      \n     +\t\tif (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),\n    -+\t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n    -+\t\t\tstrbuf_trim(&stash_oid);\n    ++\t\t    READ_ONELINER_SKIP_IF_EMPTY))\n     +\t\t\tunlink(git_path_merge_autostash(the_repository));\n    -+\t\t}\n     +\n      \t\t/* Invoke 'git reset --merge' */\n      \t\tret = cmd_reset(nargc, nargv, prefix);\n    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n      \t\t\t\tN_(\"add exec lines after each commit of the \"\n      \t\t\t\t   \"editable list\")),\n     \n    - ## builtin/reset.c ##\n    -@@\n    - #include \"cache-tree.h\"\n    - #include \"submodule.h\"\n    - #include \"submodule-config.h\"\n    -+#include \"sequencer.h\"\n    - \n    - #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n    - \n    -@@ builtin/reset.c: int cmd_reset(int argc, const char **argv, const char *prefix)\n    - \t\tif (reset_type == HARD && !update_ref_status && !quiet)\n    - \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n    - \t}\n    --\tif (!pathspec.nr)\n    -+\tif (!pathspec.nr) {\n    - \t\tremove_branch_state(the_repository, 0);\n    -+\t}\n    - \n    - \treturn update_ref_status;\n    - }\n    -\n      ## parse-options.h ##\n     @@ parse-options.h: int parse_opt_passthru_argv(const struct option *, const char *, int);\n      #define OPT_CLEANUP(v) OPT_STRING(0, \"cleanup\", v, N_(\"mode\"), N_(\"how to strip spaces and #comments from message\"))\n    @@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '\n     +\tgit diff --exit-code\n     +'\n     +\n    ++test_expect_success 'quit merge with --no-commit and --autostash' '\n    ++\tgit reset --hard c1 &&\n    ++\tgit merge-file file file.orig file.9 &&\n    ++\tgit diff >expect &&\n    ++\tgit merge --no-commit --autostash c2 &&\n    ++\tgit stash show -p MERGE_AUTOSTASH >actual &&\n    ++\ttest_cmp expect actual &&\n    ++\tgit diff HEAD >expect &&\n    ++\tgit merge --quit 2>err &&\n    ++\ttest_i18ngrep \"Autostash exists; creating a new stash entry.\" err &&\n    ++\tgit diff HEAD >actual &&\n    ++\ttest_cmp expect actual\n    ++'\n    ++\n     +test_expect_success 'merge with conflicted --autostash changes' '\n     +\tgit reset --hard c1 &&\n     +\tgit merge-file file file.orig file.9y &&\n22:  f1e54622fb = 21:  13f3fadbef t5520: make test_pull_autostash() accept expect_parent_num\n23:  177c7e537b = 22:  f4fec1e780 pull: pass --autostash to merge\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394933","messageId":"dc4674d222317fa2f503e6e1b3cad6a78dd70676.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 04/22] sequencer: make file exists check more efficient","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:51Z","receivedAt":"2020-04-07T14:28:27Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"We currently check whether a file exists and return early before reading\nthe file. Instead of accessing the file twice, always read the file and\ncheck `errno` to see if the file doesn't exist.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 6 ++----\n 1 file changed, 2 insertions(+), 4 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex faab0b13e8..a961cf5a9b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -433,11 +433,9 @@ static int read_oneliner(struct strbuf *buf,\n {\n \tint orig_len = buf->len;\n \n-\tif (!file_exists(path))\n-\t\treturn 0;\n-\n \tif (strbuf_read_file(buf, path, 0) < 0) {\n-\t\twarning_errno(_(\"could not read '%s'\"), path);\n+\t\tif (errno != ENOENT && errno != ENOTDIR)\n+\t\t\twarning_errno(_(\"could not read '%s'\"), path);\n \t\treturn 0;\n \t}\n \n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394934","messageId":"76585e5b1367a3adf18d761b2af9356ee64b46fd.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 03/22] sequencer: stop leaking buf","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:50Z","receivedAt":"2020-04-07T14:28:27Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In read_populate_opts(), we call read_oneliner() _after_ calling\nstrbuf_release(). This means that `buf` is leaked at the end of the\nfunction.\n\nAlways clean up the strbuf by going to `done_rebase_i` whether or not\nwe return an error.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 13 +++++++++----\n 1 file changed, 9 insertions(+), 4 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex e528225e78..faab0b13e8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2485,6 +2485,7 @@ static int read_populate_opts(struct replay_opts *opts)\n {\n \tif (is_rebase_i(opts)) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tint ret = 0;\n \n \t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {\n \t\t\tif (!starts_with(buf.buf, \"-S\"))\n@@ -2525,7 +2526,7 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t\topts->keep_redundant_commits = 1;\n \n \t\tread_strategy_opts(opts, &buf);\n-\t\tstrbuf_release(&buf);\n+\t\tstrbuf_reset(&buf);\n \n \t\tif (read_oneliner(&opts->current_fixups,\n \t\t\t\t  rebase_path_current_fixups(), 1)) {\n@@ -2538,12 +2539,16 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t}\n \n \t\tif (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {\n-\t\t\tif (get_oid_hex(buf.buf, &opts->squash_onto) < 0)\n-\t\t\t\treturn error(_(\"unusable squash-onto\"));\n+\t\t\tif (get_oid_hex(buf.buf, &opts->squash_onto) < 0) {\n+\t\t\t\tret = error(_(\"unusable squash-onto\"));\n+\t\t\t\tgoto done_rebase_i;\n+\t\t\t}\n \t\t\topts->have_squash_onto = 1;\n \t\t}\n \n-\t\treturn 0;\n+done_rebase_i:\n+\t\tstrbuf_release(&buf);\n+\t\treturn ret;\n \t}\n \n \tif (!file_exists(git_path_opts_file()))\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394935","messageId":"f30ad07823c6e8f47c217ac24a06e02d3e0af554.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 05/22] sequencer: make read_oneliner() accept flags","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:52Z","receivedAt":"2020-04-07T14:28:29Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future commit, we will need read_oneliner() to accept flags other\nthan just `skip_if_empty`. Instead of having an argument for each flag,\nteach read_oneliner() to accept the bitfield `flags` instead. For now,\nonly recognize the `READ_ONELINER_SKIP_IF_EMPTY` flag. More flags will\nbe added in a future commit.\n\nThe result of this is that parallel topics which introduce invocations\nof read_oneliner() will still be compatible with this new function\nsignature since, instead of passing 1 or 0 for `skip_if_empty`, they'll\nbe passing 1 or 0 to `flags`, which gives equivalent behavior.\n\nMechanically fix up invocations of read_oneliner() with the following\nspatch\n\n\t@@\n\texpression a, b;\n\t@@\n\t  read_oneliner(a, b,\n\t- 1\n\t+ READ_ONELINER_SKIP_IF_EMPTY\n\t  )\n\nand manually break up long lines in the result.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 21 ++++++++++++++-------\n 1 file changed, 14 insertions(+), 7 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a961cf5a9b..6c4e8743ef 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -419,6 +419,8 @@ static int write_message(const void *buf, size_t len, const char *filename,\n \treturn 0;\n }\n \n+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n+\n /*\n  * Reads a file that was presumably written by a shell script, i.e. with an\n  * end-of-line marker that needs to be stripped.\n@@ -429,7 +431,7 @@ static int write_message(const void *buf, size_t len, const char *filename,\n  * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n  */\n static int read_oneliner(struct strbuf *buf,\n-\tconst char *path, int skip_if_empty)\n+\tconst char *path, unsigned flags)\n {\n \tint orig_len = buf->len;\n \n@@ -445,7 +447,7 @@ static int read_oneliner(struct strbuf *buf,\n \t\tbuf->buf[buf->len] = '\\0';\n \t}\n \n-\tif (skip_if_empty && buf->len == orig_len)\n+\tif ((flags & READ_ONELINER_SKIP_IF_EMPTY) && buf->len == orig_len)\n \t\treturn 0;\n \n \treturn 1;\n@@ -2485,7 +2487,8 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tint ret = 0;\n \n-\t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {\n+\t\tif (read_oneliner(&buf, rebase_path_gpg_sign_opt(),\n+\t\t\t\t  READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\t\tif (!starts_with(buf.buf, \"-S\"))\n \t\t\t\tstrbuf_reset(&buf);\n \t\t\telse {\n@@ -2495,7 +2498,8 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t\tstrbuf_reset(&buf);\n \t\t}\n \n-\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {\n+\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(),\n+\t\t\t\t  READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n \t\t\t\topts->allow_rerere_auto = RERERE_AUTOUPDATE;\n \t\t\telse if (!strcmp(buf.buf, \"--no-rerere-autoupdate\"))\n@@ -2527,7 +2531,8 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\tstrbuf_reset(&buf);\n \n \t\tif (read_oneliner(&opts->current_fixups,\n-\t\t\t\t  rebase_path_current_fixups(), 1)) {\n+\t\t\t\t  rebase_path_current_fixups(),\n+\t\t\t\t  READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\t\tconst char *p = opts->current_fixups.buf;\n \t\t\topts->current_fixup_count = 1;\n \t\t\twhile ((p = strchr(p, '\\n'))) {\n@@ -3668,7 +3673,8 @@ static int apply_autostash(struct replay_opts *opts)\n \tstruct child_process child = CHILD_PROCESS_INIT;\n \tint ret = 0;\n \n-\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {\n+\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(),\n+\t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\tstrbuf_release(&stash_sha1);\n \t\treturn 0;\n \t}\n@@ -4292,7 +4298,8 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)\n \t\tstruct strbuf buf = STRBUF_INIT;\n \t\tstruct object_id oid;\n \n-\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&\n+\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(),\n+\t\t\t\t  READ_ONELINER_SKIP_IF_EMPTY) &&\n \t\t    !get_oid_committish(buf.buf, &oid))\n \t\t\trecord_in_rewritten(&oid, peek_command(&todo_list, 0));\n \t\tstrbuf_release(&buf);\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394936","messageId":"8c504427e376470fb0d939b9c9da315e7fabf36f.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 06/22] sequencer: configurably warn on non-existent files","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:53Z","receivedAt":"2020-04-07T14:28:30Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we plan on externing read_oneliner(). Future users of\nread_oneliner() will want the ability to output warnings in the event\nthat the `path` doesn't exist. Introduce the\n`READ_ONELINER_WARN_MISSING` flag which, if active, would issue a\nwarning when a file doesn't exist by always executing warning_errno()\nin the case where strbuf_read_file() fails.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 6c4e8743ef..32cc289da3 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename,\n }\n \n #define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n+#define READ_ONELINER_WARN_MISSING (1 << 1)\n \n /*\n  * Reads a file that was presumably written by a shell script, i.e. with an\n@@ -436,7 +437,8 @@ static int read_oneliner(struct strbuf *buf,\n \tint orig_len = buf->len;\n \n \tif (strbuf_read_file(buf, path, 0) < 0) {\n-\t\tif (errno != ENOENT && errno != ENOTDIR)\n+\t\tif ((flags & READ_ONELINER_WARN_MISSING) ||\n+\t\t    (errno != ENOENT && errno != ENOTDIR))\n \t\t\twarning_errno(_(\"could not read '%s'\"), path);\n \t\treturn 0;\n \t}\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394937","messageId":"50e93770e76863b05e51c25192a7173dc38e0ffe.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 07/22] sequencer: make read_oneliner() extern","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:54Z","receivedAt":"2020-04-07T14:28:33Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"The function read_oneliner() is a generally useful util function.\nInstead of hiding it as a static function within sequencer.c, extern it\nso that it can be reused by others.\n\nThis patch is best viewed with --color-moved.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 14 +-------------\n sequencer.h | 14 ++++++++++++++\n 2 files changed, 15 insertions(+), 13 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 32cc289da3..0d98ddc33c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -419,19 +419,7 @@ static int write_message(const void *buf, size_t len, const char *filename,\n \treturn 0;\n }\n \n-#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n-#define READ_ONELINER_WARN_MISSING (1 << 1)\n-\n-/*\n- * Reads a file that was presumably written by a shell script, i.e. with an\n- * end-of-line marker that needs to be stripped.\n- *\n- * Note that only the last end-of-line marker is stripped, consistent with the\n- * behavior of \"$(cat path)\" in a shell script.\n- *\n- * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n- */\n-static int read_oneliner(struct strbuf *buf,\n+int read_oneliner(struct strbuf *buf,\n \tconst char *path, unsigned flags)\n {\n \tint orig_len = buf->len;\ndiff --git a/sequencer.h b/sequencer.h\nindex 718a07426d..ab43717fd6 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -198,6 +198,20 @@ void print_commit_summary(struct repository *repo,\n \t\t\t  const struct object_id *oid,\n \t\t\t  unsigned int flags);\n \n+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n+#define READ_ONELINER_WARN_MISSING (1 << 1)\n+\n+/*\n+ * Reads a file that was presumably written by a shell script, i.e. with an\n+ * end-of-line marker that needs to be stripped.\n+ *\n+ * Note that only the last end-of-line marker is stripped, consistent with the\n+ * behavior of \"$(cat path)\" in a shell script.\n+ *\n+ * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n+ */\n+int read_oneliner(struct strbuf *buf,\n+\tconst char *path, unsigned flags);\n int read_author_script(const char *path, char **name, char **email, char **date,\n \t\t       int allow_missing);\n void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394938","messageId":"0cc279fc14dd27ed2606064d236115aa72551dce.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 08/22] rebase: use read_oneliner()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:55Z","receivedAt":"2020-04-07T14:28:36Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Since in sequencer.c, read_one() basically duplicates the functionality\nof read_oneliner(), reduce code duplication by replacing read_one() with\nread_oneliner().\n\nThis was done with the following Coccinelle script\n\n\t@@\n\texpression a, b;\n\t@@\n\t- read_one(a, b)\n\t+ !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)\n\nand long lines were manually broken up.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 37 +++++++++++++++++--------------------\n 1 file changed, 17 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex bff53d5d16..57dbfd2a4f 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -586,15 +586,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o\n \treturn path.buf;\n }\n \n-/* Read one file, then strip line endings */\n-static int read_one(const char *path, struct strbuf *buf)\n-{\n-\tif (strbuf_read_file(buf, path, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'\"), path);\n-\tstrbuf_trim_trailing_newline(buf);\n-\treturn 0;\n-}\n-\n /* Initialize the rebase options from the state directory. */\n static int read_basic_state(struct rebase_options *opts)\n {\n@@ -602,8 +593,10 @@ static int read_basic_state(struct rebase_options *opts)\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct object_id oid;\n \n-\tif (read_one(state_dir_path(\"head-name\", opts), &head_name) ||\n-\t    read_one(state_dir_path(\"onto\", opts), &buf))\n+\tif (!read_oneliner(&head_name, state_dir_path(\"head-name\", opts),\n+\t\t\t   READ_ONELINER_WARN_MISSING) ||\n+\t    !read_oneliner(&buf, state_dir_path(\"onto\", opts),\n+\t\t\t   READ_ONELINER_WARN_MISSING))\n \t\treturn -1;\n \topts->head_name = starts_with(head_name.buf, \"refs/\") ?\n \t\txstrdup(head_name.buf) : NULL;\n@@ -619,9 +612,11 @@ static int read_basic_state(struct rebase_options *opts)\n \t */\n \tstrbuf_reset(&buf);\n \tif (file_exists(state_dir_path(\"orig-head\", opts))) {\n-\t\tif (read_one(state_dir_path(\"orig-head\", opts), &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"orig-head\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_MISSING))\n \t\t\treturn -1;\n-\t} else if (read_one(state_dir_path(\"head\", opts), &buf))\n+\t} else if (!read_oneliner(&buf, state_dir_path(\"head\", opts),\n+\t\t\t\t  READ_ONELINER_WARN_MISSING))\n \t\treturn -1;\n \tif (get_oid(buf.buf, &opts->orig_head))\n \t\treturn error(_(\"invalid orig-head: '%s'\"), buf.buf);\n@@ -641,8 +636,8 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tif (file_exists(state_dir_path(\"allow_rerere_autoupdate\", opts))) {\n \t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"allow_rerere_autoupdate\", opts),\n-\t\t\t    &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"allow_rerere_autoupdate\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_MISSING))\n \t\t\treturn -1;\n \t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n \t\t\topts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;\n@@ -655,8 +650,8 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tif (file_exists(state_dir_path(\"gpg_sign_opt\", opts))) {\n \t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"gpg_sign_opt\", opts),\n-\t\t\t    &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"gpg_sign_opt\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_MISSING))\n \t\t\treturn -1;\n \t\tfree(opts->gpg_sign_opt);\n \t\topts->gpg_sign_opt = xstrdup(buf.buf);\n@@ -664,7 +659,8 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tif (file_exists(state_dir_path(\"strategy\", opts))) {\n \t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"strategy\", opts), &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_MISSING))\n \t\t\treturn -1;\n \t\tfree(opts->strategy);\n \t\topts->strategy = xstrdup(buf.buf);\n@@ -672,7 +668,8 @@ static int read_basic_state(struct rebase_options *opts)\n \n \tif (file_exists(state_dir_path(\"strategy_opts\", opts))) {\n \t\tstrbuf_reset(&buf);\n-\t\tif (read_one(state_dir_path(\"strategy_opts\", opts), &buf))\n+\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy_opts\", opts),\n+\t\t\t\t   READ_ONELINER_WARN_MISSING))\n \t\t\treturn -1;\n \t\tfree(opts->strategy_opts);\n \t\topts->strategy_opts = xstrdup(buf.buf);\n@@ -724,7 +721,7 @@ static int apply_autostash(struct rebase_options *opts)\n \tif (!file_exists(path))\n \t\treturn 0;\n \n-\tif (read_one(path, &autostash))\n+\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))\n \t\treturn error(_(\"Could not read '%s'\"), path);\n \t/* Ensure that the hash is not mistaken for a number */\n \tstrbuf_addstr(&autostash, \"^0\");\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394939","messageId":"98a7f5280c12a3244d0c5d92d1b65d6de7be3cb5.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 11/22] rebase: use apply_autostash() from sequencer.c","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:58Z","receivedAt":"2020-04-07T14:28:37Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"The apply_autostash() function in builtin/rebase.c is similar enough to\nthe apply_autostash() function in sequencer.c that they are almost\ninterchangeable, except for the type of arg they accept. Make the\nsequencer.c version extern and use it in rebase.\n\nThe rebase version was introduced in 6defce2b02 (builtin rebase: support\n`--autostash` option, 2018-09-04) as part of the shell to C conversion.\nIt opted to duplicate the function because, at the time, there was\nanother in-progress project converting interactive rebase from shell to\nC as well and they did not want to clash with them by refactoring\nsequencer.c version of apply_autostash(). Since both efforts are long\ndone, we can freely combine them together now.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 49 ++----------------------------------------------\n sequencer.c      |  2 +-\n sequencer.h      |  2 ++\n 3 files changed, 5 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 57dbfd2a4f..cb0bdfc3e4 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -712,51 +712,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)\n \treturn 0;\n }\n \n-static int apply_autostash(struct rebase_options *opts)\n-{\n-\tconst char *path = state_dir_path(\"autostash\", opts);\n-\tstruct strbuf autostash = STRBUF_INIT;\n-\tstruct child_process stash_apply = CHILD_PROCESS_INIT;\n-\n-\tif (!file_exists(path))\n-\t\treturn 0;\n-\n-\tif (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))\n-\t\treturn error(_(\"Could not read '%s'\"), path);\n-\t/* Ensure that the hash is not mistaken for a number */\n-\tstrbuf_addstr(&autostash, \"^0\");\n-\targv_array_pushl(&stash_apply.args,\n-\t\t\t \"stash\", \"apply\", autostash.buf, NULL);\n-\tstash_apply.git_cmd = 1;\n-\tstash_apply.no_stderr = stash_apply.no_stdout =\n-\t\tstash_apply.no_stdin = 1;\n-\tif (!run_command(&stash_apply))\n-\t\tprintf(_(\"Applied autostash.\\n\"));\n-\telse {\n-\t\tstruct argv_array args = ARGV_ARRAY_INIT;\n-\t\tint res = 0;\n-\n-\t\targv_array_pushl(&args,\n-\t\t\t\t \"stash\", \"store\", \"-m\", \"autostash\", \"-q\",\n-\t\t\t\t autostash.buf, NULL);\n-\t\tif (run_command_v_opt(args.argv, RUN_GIT_CMD))\n-\t\t\tres = error(_(\"Cannot store %s\"), autostash.buf);\n-\t\targv_array_clear(&args);\n-\t\tstrbuf_release(&autostash);\n-\t\tif (res)\n-\t\t\treturn res;\n-\n-\t\tfprintf(stderr,\n-\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n-\t\t\t  \"Your changes are safe in the stash.\\n\"\n-\t\t\t  \"You can run \\\"git stash pop\\\" or \\\"git stash drop\\\" \"\n-\t\t\t  \"at any time.\\n\"));\n-\t}\n-\n-\tstrbuf_release(&autostash);\n-\treturn 0;\n-}\n-\n static int finish_rebase(struct rebase_options *opts)\n {\n \tstruct strbuf dir = STRBUF_INIT;\n@@ -764,7 +719,7 @@ static int finish_rebase(struct rebase_options *opts)\n \tint ret = 0;\n \n \tdelete_ref(NULL, \"REBASE_HEAD\", NULL, REF_NO_DEREF);\n-\tapply_autostash(opts);\n+\tapply_autostash(state_dir_path(\"autostash\", opts));\n \tclose_object_store(the_repository->objects);\n \t/*\n \t * We ignore errors in 'gc --auto', since the\n@@ -1209,7 +1164,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)\n \t} else if (status == 2) {\n \t\tstruct strbuf dir = STRBUF_INIT;\n \n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(state_dir_path(\"autostash\", opts));\n \t\tstrbuf_addstr(&dir, opts->state_dir);\n \t\tremove_dir_recursively(&dir, 0);\n \t\tstrbuf_release(&dir);\ndiff --git a/sequencer.c b/sequencer.c\nindex 4a6812e90e..f5bb1cc1fb 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3657,7 +3657,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n-static int apply_autostash(const char *path)\n+int apply_autostash(const char *path)\n {\n \tstruct strbuf stash_oid = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\ndiff --git a/sequencer.h b/sequencer.h\nindex ab43717fd6..ab686146d1 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,\n \t\t\t const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n \n+int apply_autostash(const char *path);\n+\n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n void print_commit_summary(struct repository *repo,\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394940","messageId":"67f3b5c2256650411441c9328da2f9b9ba3b0bab.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 12/22] rebase: generify reset_head()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:59Z","receivedAt":"2020-04-07T14:28:38Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we plan on lib-ifying reset_head() so we need it to\nbe more generic. Make it more generic by making it accept a\n`struct repository` argument instead of implicitly using the non-repo\nfunctions. Also, make it accept a `const char *default_reflog_action`\nargument so that the default action of \"rebase\" isn't hardcoded in.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 64 ++++++++++++++++++++++++++++--------------------\n 1 file changed, 37 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex cb0bdfc3e4..5532286142 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -28,6 +28,8 @@\n #include \"sequencer.h\"\n #include \"rebase-interactive.h\"\n \n+#define DEFAULT_REFLOG_ACTION \"rebase\"\n+\n static char const * const builtin_rebase_usage[] = {\n \tN_(\"git rebase [-i] [options] [--exec <cmd>] \"\n \t\t\"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]\"),\n@@ -772,9 +774,10 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)\n #define RESET_HEAD_REFS_ONLY (1<<3)\n #define RESET_ORIG_HEAD (1<<4)\n \n-static int reset_head(struct object_id *oid, const char *action,\n+static int reset_head(struct repository *r, struct object_id *oid, const char *action,\n \t\t      const char *switch_to_branch, unsigned flags,\n-\t\t      const char *reflog_orig_head, const char *reflog_head)\n+\t\t      const char *reflog_orig_head, const char *reflog_head,\n+\t\t      const char *default_reflog_action)\n {\n \tunsigned detach_head = flags & RESET_HEAD_DETACH;\n \tunsigned reset_hard = flags & RESET_HEAD_HARD;\n@@ -796,7 +799,7 @@ static int reset_head(struct object_id *oid, const char *action,\n \tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n \t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n \n-\tif (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {\n+\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n \t\tret = -1;\n \t\tgoto leave_reset_head;\n \t}\n@@ -815,26 +818,26 @@ static int reset_head(struct object_id *oid, const char *action,\n \tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n \tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n \tunpack_tree_opts.head_idx = 1;\n-\tunpack_tree_opts.src_index = the_repository->index;\n-\tunpack_tree_opts.dst_index = the_repository->index;\n+\tunpack_tree_opts.src_index = r->index;\n+\tunpack_tree_opts.dst_index = r->index;\n \tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n \tunpack_tree_opts.update = 1;\n \tunpack_tree_opts.merge = 1;\n \tif (!detach_head)\n \t\tunpack_tree_opts.reset = 1;\n \n-\tif (repo_read_index_unmerged(the_repository) < 0) {\n+\tif (repo_read_index_unmerged(r) < 0) {\n \t\tret = error(_(\"could not read index\"));\n \t\tgoto leave_reset_head;\n \t}\n \n-\tif (!reset_hard && !fill_tree_descriptor(the_repository, &desc[nr++], &head_oid)) {\n+\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n \t\tret = error(_(\"failed to find tree of %s\"),\n \t\t\t    oid_to_hex(&head_oid));\n \t\tgoto leave_reset_head;\n \t}\n \n-\tif (!fill_tree_descriptor(the_repository, &desc[nr++], oid)) {\n+\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n \t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n \t\tgoto leave_reset_head;\n \t}\n@@ -845,16 +848,16 @@ static int reset_head(struct object_id *oid, const char *action,\n \t}\n \n \ttree = parse_tree_indirect(oid);\n-\tprime_cache_tree(the_repository, the_repository->index, tree);\n+\tprime_cache_tree(r, r->index, tree);\n \n-\tif (write_locked_index(the_repository->index, &lock, COMMIT_LOCK) < 0) {\n+\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n \t\tret = error(_(\"could not write index\"));\n \t\tgoto leave_reset_head;\n \t}\n \n reset_head_refs:\n \treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n-\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : \"rebase\");\n+\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n \tprefix_len = msg.len;\n \n \tif (update_orig_head) {\n@@ -916,8 +919,10 @@ static int move_to_original_branch(struct rebase_options *opts)\n \t\t    opts->head_name, oid_to_hex(&opts->onto->object.oid));\n \tstrbuf_addf(&head_reflog, \"rebase finished: returning to %s\",\n \t\t    opts->head_name);\n-\tret = reset_head(NULL, \"\", opts->head_name, RESET_HEAD_REFS_ONLY,\n-\t\t\t orig_head_reflog.buf, head_reflog.buf);\n+\tret = reset_head(the_repository, NULL, \"\", opts->head_name,\n+\t\t\t RESET_HEAD_REFS_ONLY,\n+\t\t\t orig_head_reflog.buf, head_reflog.buf,\n+\t\t\t DEFAULT_REFLOG_ACTION);\n \n \tstrbuf_release(&orig_head_reflog);\n \tstrbuf_release(&head_reflog);\n@@ -1005,8 +1010,9 @@ static int run_am(struct rebase_options *opts)\n \t\tfree(rebased_patches);\n \t\targv_array_clear(&am.args);\n \n-\t\treset_head(&opts->orig_head, \"checkout\", opts->head_name, 0,\n-\t\t\t   \"HEAD\", NULL);\n+\t\treset_head(the_repository, &opts->orig_head, \"checkout\",\n+\t\t\t   opts->head_name, 0,\n+\t\t\t   \"HEAD\", NULL, DEFAULT_REFLOG_ACTION);\n \t\terror(_(\"\\ngit encountered an error while preparing the \"\n \t\t\t\"patches to replay\\n\"\n \t\t\t\"these revisions:\\n\"\n@@ -1661,8 +1667,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\trerere_clear(the_repository, &merge_rr);\n \t\tstring_list_clear(&merge_rr, 1);\n \n-\t\tif (reset_head(NULL, \"reset\", NULL, RESET_HEAD_HARD,\n-\t\t\t       NULL, NULL) < 0)\n+\t\tif (reset_head(the_repository, NULL, \"reset\", NULL, RESET_HEAD_HARD,\n+\t\t\t       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)\n \t\t\tdie(_(\"could not discard worktree changes\"));\n \t\tremove_branch_state(the_repository, 0);\n \t\tif (read_basic_state(&options))\n@@ -1679,9 +1685,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \t\tif (read_basic_state(&options))\n \t\t\texit(1);\n-\t\tif (reset_head(&options.orig_head, \"reset\",\n+\t\tif (reset_head(the_repository, &options.orig_head, \"reset\",\n \t\t\t       options.head_name, RESET_HEAD_HARD,\n-\t\t\t       NULL, NULL) < 0)\n+\t\t\t       NULL, NULL, DEFAULT_REFLOG_ACTION) < 0)\n \t\t\tdie(_(\"could not move back to %s\"),\n \t\t\t    oid_to_hex(&options.orig_head));\n \t\tremove_branch_state(the_repository, 0);\n@@ -2073,8 +2079,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\t    options.state_dir);\n \t\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n \t\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\t\tif (reset_head(NULL, \"reset --hard\",\n-\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL) < 0)\n+\t\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n+\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n+\t\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n \t\t\t\tdie(_(\"could not reset --hard\"));\n \n \t\t\tif (discard_index(the_repository->index) < 0 ||\n@@ -2114,10 +2121,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t\t\tstrbuf_addf(&buf, \"%s: checkout %s\",\n \t\t\t\t\t    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),\n \t\t\t\t\t    options.switch_to);\n-\t\t\t\tif (reset_head(&options.orig_head, \"checkout\",\n+\t\t\t\tif (reset_head(the_repository,\n+\t\t\t\t\t       &options.orig_head, \"checkout\",\n \t\t\t\t\t       options.head_name,\n \t\t\t\t\t       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,\n-\t\t\t\t\t       NULL, buf.buf) < 0) {\n+\t\t\t\t\t       NULL, buf.buf,\n+\t\t\t\t\t       DEFAULT_REFLOG_ACTION) < 0) {\n \t\t\t\t\tret = !!error(_(\"could not switch to \"\n \t\t\t\t\t\t\t\"%s\"),\n \t\t\t\t\t\t      options.switch_to);\n@@ -2189,10 +2198,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \n \tstrbuf_addf(&msg, \"%s: checkout %s\",\n \t\t    getenv(GIT_REFLOG_ACTION_ENVIRONMENT), options.onto_name);\n-\tif (reset_head(&options.onto->object.oid, \"checkout\", NULL,\n+\tif (reset_head(the_repository, &options.onto->object.oid, \"checkout\", NULL,\n \t\t       RESET_HEAD_DETACH | RESET_ORIG_HEAD |\n \t\t       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,\n-\t\t       NULL, msg.buf))\n+\t\t       NULL, msg.buf, DEFAULT_REFLOG_ACTION))\n \t\tdie(_(\"Could not detach HEAD\"));\n \tstrbuf_release(&msg);\n \n@@ -2207,8 +2216,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addf(&msg, \"rebase finished: %s onto %s\",\n \t\t\toptions.head_name ? options.head_name : \"detached HEAD\",\n \t\t\toid_to_hex(&options.onto->object.oid));\n-\t\treset_head(NULL, \"Fast-forwarded\", options.head_name,\n-\t\t\t   RESET_HEAD_REFS_ONLY, \"HEAD\", msg.buf);\n+\t\treset_head(the_repository, NULL, \"Fast-forwarded\", options.head_name,\n+\t\t\t   RESET_HEAD_REFS_ONLY, \"HEAD\", msg.buf,\n+\t\t\t   DEFAULT_REFLOG_ACTION);\n \t\tstrbuf_release(&msg);\n \t\tret = !!finish_rebase(&options);\n \t\tgoto cleanup;\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394941","messageId":"114d9a765531d2e232dbc94484f5ef544bda8170.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 14/22] rebase: extract create_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:28:01Z","receivedAt":"2020-04-07T14:28:39Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In a future commit, we will lib-ify this code. In preparation for\nthis, extract the code into the create_autostash() function so that it\ncan be cleaned up before it is finally lib-ified.\n\nThis patch is best viewed with `--color-moved` and\n`--color-moved-ws=allow-indentation-change`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 94 +++++++++++++++++++++++++-----------------------\n 1 file changed, 50 insertions(+), 44 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 48ef36bd10..581ad3af7b 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1274,6 +1274,55 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n+static void create_autostash(struct rebase_options *options)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct lock_file lock_file = LOCK_INIT;\n+\tint fd;\n+\n+\tfd = hold_locked_index(&lock_file, 0);\n+\trefresh_cache(REFRESH_QUIET);\n+\tif (0 <= fd)\n+\t\trepo_update_index_if_able(the_repository, &lock_file);\n+\trollback_lock_file(&lock_file);\n+\n+\tif (has_unstaged_changes(the_repository, 1) ||\n+\t    has_uncommitted_changes(the_repository, 1)) {\n+\t\tconst char *autostash =\n+\t\t\tstate_dir_path(\"autostash\", options);\n+\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n+\t\tstruct object_id oid;\n+\n+\t\targv_array_pushl(&stash.args,\n+\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n+\t\tstash.git_cmd = 1;\n+\t\tstash.no_stdin = 1;\n+\t\tstrbuf_reset(&buf);\n+\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n+\t\t\tdie(_(\"Cannot autostash\"));\n+\t\tstrbuf_trim_trailing_newline(&buf);\n+\t\tif (get_oid(buf.buf, &oid))\n+\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n+\t\t\t    buf.buf);\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n+\n+\t\tif (safe_create_leading_directories_const(autostash))\n+\t\t\tdie(_(\"Could not create directory for '%s'\"),\n+\t\t\t    options->state_dir);\n+\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n+\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n+\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n+\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n+\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n+\t\t\tdie(_(\"could not reset --hard\"));\n+\n+\t\tif (discard_index(the_repository->index) < 0 ||\n+\t\t\trepo_read_index(the_repository) < 0)\n+\t\t\tdie(_(\"could not read index\"));\n+\t}\n+\tstrbuf_release(&buf);\n+}\n \n int cmd_rebase(int argc, const char **argv, const char *prefix)\n {\n@@ -1907,50 +1956,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read index\"));\n \n \tif (options.autostash) {\n-\t\tstruct lock_file lock_file = LOCK_INIT;\n-\t\tint fd;\n-\n-\t\tfd = hold_locked_index(&lock_file, 0);\n-\t\trefresh_cache(REFRESH_QUIET);\n-\t\tif (0 <= fd)\n-\t\t\trepo_update_index_if_able(the_repository, &lock_file);\n-\t\trollback_lock_file(&lock_file);\n-\n-\t\tif (has_unstaged_changes(the_repository, 1) ||\n-\t\t    has_uncommitted_changes(the_repository, 1)) {\n-\t\t\tconst char *autostash =\n-\t\t\t\tstate_dir_path(\"autostash\", &options);\n-\t\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n-\t\t\tstruct object_id oid;\n-\n-\t\t\targv_array_pushl(&stash.args,\n-\t\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n-\t\t\tstash.git_cmd = 1;\n-\t\t\tstash.no_stdin = 1;\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n-\t\t\t\tdie(_(\"Cannot autostash\"));\n-\t\t\tstrbuf_trim_trailing_newline(&buf);\n-\t\t\tif (get_oid(buf.buf, &oid))\n-\t\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n-\t\t\t\t    buf.buf);\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n-\n-\t\t\tif (safe_create_leading_directories_const(autostash))\n-\t\t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t\t    options.state_dir);\n-\t\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n-\t\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n-\t\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n-\t\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n-\t\t\t\tdie(_(\"could not reset --hard\"));\n-\n-\t\t\tif (discard_index(the_repository->index) < 0 ||\n-\t\t\t\trepo_read_index(the_repository) < 0)\n-\t\t\t\tdie(_(\"could not read index\"));\n-\t\t}\n+\t\tcreate_autostash(&options);\n \t}\n \n \tif (require_clean_work_tree(the_repository, \"rebase\",\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394942","messageId":"c2ed4cd39d6ec2c7ba095a5a75bf28334be476c8.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 15/22] rebase: generify create_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:28:02Z","receivedAt":"2020-04-07T14:28:40Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In the future, we plan on lib-ifying create_autostash() so we need it to\nbe more generic. Make it more generic by making it accept a\n`struct repository` argument instead of implicitly using the non-repo\nfunctions and `the_repository`. Also, make it accept a `path` argument\nso that we no longer rely have to rely on `struct rebase_options`.\nFinally, make it accept a `default_reflog_action` argument so we no\nlonger have to rely on `DEFAULT_REFLOG_ACTION`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 32 ++++++++++++++++----------------\n 1 file changed, 16 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 581ad3af7b..ae345c9e57 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1274,22 +1274,21 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n-static void create_autostash(struct rebase_options *options)\n+static void create_autostash(struct repository *r, const char *path,\n+\t\t\t     const char *default_reflog_action)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct lock_file lock_file = LOCK_INIT;\n \tint fd;\n \n-\tfd = hold_locked_index(&lock_file, 0);\n-\trefresh_cache(REFRESH_QUIET);\n+\tfd = repo_hold_locked_index(r, &lock_file, 0);\n+\trefresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);\n \tif (0 <= fd)\n-\t\trepo_update_index_if_able(the_repository, &lock_file);\n+\t\trepo_update_index_if_able(r, &lock_file);\n \trollback_lock_file(&lock_file);\n \n-\tif (has_unstaged_changes(the_repository, 1) ||\n-\t    has_uncommitted_changes(the_repository, 1)) {\n-\t\tconst char *autostash =\n-\t\t\tstate_dir_path(\"autostash\", options);\n+\tif (has_unstaged_changes(r, 1) ||\n+\t    has_uncommitted_changes(r, 1)) {\n \t\tstruct child_process stash = CHILD_PROCESS_INIT;\n \t\tstruct object_id oid;\n \n@@ -1307,18 +1306,18 @@ static void create_autostash(struct rebase_options *options)\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n \n-\t\tif (safe_create_leading_directories_const(autostash))\n+\t\tif (safe_create_leading_directories_const(path))\n \t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t    options->state_dir);\n-\t\twrite_file(autostash, \"%s\", oid_to_hex(&oid));\n+\t\t\t    path);\n+\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n \t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\tif (reset_head(the_repository, NULL, \"reset --hard\",\n+\t\tif (reset_head(r, NULL, \"reset --hard\",\n \t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n-\t\t\t       DEFAULT_REFLOG_ACTION) < 0)\n+\t\t\t       default_reflog_action) < 0)\n \t\t\tdie(_(\"could not reset --hard\"));\n \n-\t\tif (discard_index(the_repository->index) < 0 ||\n-\t\t\trepo_read_index(the_repository) < 0)\n+\t\tif (discard_index(r->index) < 0 ||\n+\t\t\trepo_read_index(r) < 0)\n \t\t\tdie(_(\"could not read index\"));\n \t}\n \tstrbuf_release(&buf);\n@@ -1956,7 +1955,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"could not read index\"));\n \n \tif (options.autostash) {\n-\t\tcreate_autostash(&options);\n+\t\tcreate_autostash(the_repository, state_dir_path(\"autostash\", &options),\n+\t\t\t\t DEFAULT_REFLOG_ACTION);\n \t}\n \n \tif (require_clean_work_tree(the_repository, \"rebase\",\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394943","messageId":"af5e80866790d42cd639cf1f2d51ddad0f844748.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 13/22] reset: extract reset_head() from rebase","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:28:00Z","receivedAt":"2020-04-07T14:28:40Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Continue the process of lib-ifying the autostash code. In a future\ncommit, this will be used to implement `--autostash` in other builtins.\n\nThis patch is best viewed with `--color-moved`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Makefile         |   1 +\n builtin/rebase.c | 139 +---------------------------------------------\n reset.c          | 140 +++++++++++++++++++++++++++++++++++++++++++++++\n reset.h          |  20 +++++++\n 4 files changed, 162 insertions(+), 138 deletions(-)\n create mode 100644 reset.c\n create mode 100644 reset.h\n\ndiff --git a/Makefile b/Makefile\nindex f709fc3f77..338fb55b73 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -970,6 +970,7 @@ LIB_OBJS += replace-object.o\n LIB_OBJS += repo-settings.o\n LIB_OBJS += repository.o\n LIB_OBJS += rerere.o\n+LIB_OBJS += reset.o\n LIB_OBJS += resolve-undo.o\n LIB_OBJS += revision.o\n LIB_OBJS += run-command.o\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 5532286142..48ef36bd10 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -27,6 +27,7 @@\n #include \"branch.h\"\n #include \"sequencer.h\"\n #include \"rebase-interactive.h\"\n+#include \"reset.h\"\n \n #define DEFAULT_REFLOG_ACTION \"rebase\"\n \n@@ -766,144 +767,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)\n \t}\n }\n \n-#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n-\n-#define RESET_HEAD_DETACH (1<<0)\n-#define RESET_HEAD_HARD (1<<1)\n-#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n-#define RESET_HEAD_REFS_ONLY (1<<3)\n-#define RESET_ORIG_HEAD (1<<4)\n-\n-static int reset_head(struct repository *r, struct object_id *oid, const char *action,\n-\t\t      const char *switch_to_branch, unsigned flags,\n-\t\t      const char *reflog_orig_head, const char *reflog_head,\n-\t\t      const char *default_reflog_action)\n-{\n-\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n-\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n-\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n-\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n-\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n-\tstruct object_id head_oid;\n-\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n-\tstruct lock_file lock = LOCK_INIT;\n-\tstruct unpack_trees_options unpack_tree_opts;\n-\tstruct tree *tree;\n-\tconst char *reflog_action;\n-\tstruct strbuf msg = STRBUF_INIT;\n-\tsize_t prefix_len;\n-\tstruct object_id *orig = NULL, oid_orig,\n-\t\t*old_orig = NULL, oid_old_orig;\n-\tint ret = 0, nr = 0;\n-\n-\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n-\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n-\n-\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n-\t\tret = -1;\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n-\t\tret = error(_(\"could not determine HEAD revision\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!oid)\n-\t\toid = &head_oid;\n-\n-\tif (refs_only)\n-\t\tgoto reset_head_refs;\n-\n-\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n-\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n-\tunpack_tree_opts.head_idx = 1;\n-\tunpack_tree_opts.src_index = r->index;\n-\tunpack_tree_opts.dst_index = r->index;\n-\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n-\tunpack_tree_opts.update = 1;\n-\tunpack_tree_opts.merge = 1;\n-\tif (!detach_head)\n-\t\tunpack_tree_opts.reset = 1;\n-\n-\tif (repo_read_index_unmerged(r) < 0) {\n-\t\tret = error(_(\"could not read index\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n-\t\tret = error(_(\"failed to find tree of %s\"),\n-\t\t\t    oid_to_hex(&head_oid));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n-\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n-\t\tret = -1;\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-\ttree = parse_tree_indirect(oid);\n-\tprime_cache_tree(r, r->index, tree);\n-\n-\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n-\t\tret = error(_(\"could not write index\"));\n-\t\tgoto leave_reset_head;\n-\t}\n-\n-reset_head_refs:\n-\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n-\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n-\tprefix_len = msg.len;\n-\n-\tif (update_orig_head) {\n-\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n-\t\t\told_orig = &oid_old_orig;\n-\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n-\t\t\torig = &oid_orig;\n-\t\t\tif (!reflog_orig_head) {\n-\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n-\t\t\t\treflog_orig_head = msg.buf;\n-\t\t\t}\n-\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n-\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n-\t\t} else if (old_orig)\n-\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n-\t}\n-\n-\tif (!reflog_head) {\n-\t\tstrbuf_setlen(&msg, prefix_len);\n-\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n-\t\treflog_head = msg.buf;\n-\t}\n-\tif (!switch_to_branch)\n-\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n-\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n-\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n-\telse {\n-\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n-\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n-\t\tif (!ret)\n-\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n-\t\t\t\t\t    reflog_head);\n-\t}\n-\tif (run_hook)\n-\t\trun_hook_le(NULL, \"post-checkout\",\n-\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n-\t\t\t    oid_to_hex(oid), \"1\", NULL);\n-\n-leave_reset_head:\n-\tstrbuf_release(&msg);\n-\trollback_lock_file(&lock);\n-\twhile (nr)\n-\t\tfree((void *)desc[--nr].buffer);\n-\treturn ret;\n-}\n-\n static int move_to_original_branch(struct rebase_options *opts)\n {\n \tstruct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;\ndiff --git a/reset.c b/reset.c\nnew file mode 100644\nindex 0000000000..79b683bffa\n--- /dev/null\n+++ b/reset.c\n@@ -0,0 +1,140 @@\n+#include \"git-compat-util.h\"\n+#include \"cache-tree.h\"\n+#include \"lockfile.h\"\n+#include \"refs.h\"\n+#include \"reset.h\"\n+#include \"run-command.h\"\n+#include \"tree-walk.h\"\n+#include \"tree.h\"\n+#include \"unpack-trees.h\"\n+\n+int reset_head(struct repository *r, struct object_id *oid, const char *action,\n+\t       const char *switch_to_branch, unsigned flags,\n+\t       const char *reflog_orig_head, const char *reflog_head,\n+\t       const char *default_reflog_action)\n+{\n+\tunsigned detach_head = flags & RESET_HEAD_DETACH;\n+\tunsigned reset_hard = flags & RESET_HEAD_HARD;\n+\tunsigned run_hook = flags & RESET_HEAD_RUN_POST_CHECKOUT_HOOK;\n+\tunsigned refs_only = flags & RESET_HEAD_REFS_ONLY;\n+\tunsigned update_orig_head = flags & RESET_ORIG_HEAD;\n+\tstruct object_id head_oid;\n+\tstruct tree_desc desc[2] = { { NULL }, { NULL } };\n+\tstruct lock_file lock = LOCK_INIT;\n+\tstruct unpack_trees_options unpack_tree_opts;\n+\tstruct tree *tree;\n+\tconst char *reflog_action;\n+\tstruct strbuf msg = STRBUF_INIT;\n+\tsize_t prefix_len;\n+\tstruct object_id *orig = NULL, oid_orig,\n+\t\t*old_orig = NULL, oid_old_orig;\n+\tint ret = 0, nr = 0;\n+\n+\tif (switch_to_branch && !starts_with(switch_to_branch, \"refs/\"))\n+\t\tBUG(\"Not a fully qualified branch: '%s'\", switch_to_branch);\n+\n+\tif (!refs_only && repo_hold_locked_index(r, &lock, LOCK_REPORT_ON_ERROR) < 0) {\n+\t\tret = -1;\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif ((!oid || !reset_hard) && get_oid(\"HEAD\", &head_oid)) {\n+\t\tret = error(_(\"could not determine HEAD revision\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!oid)\n+\t\toid = &head_oid;\n+\n+\tif (refs_only)\n+\t\tgoto reset_head_refs;\n+\n+\tmemset(&unpack_tree_opts, 0, sizeof(unpack_tree_opts));\n+\tsetup_unpack_trees_porcelain(&unpack_tree_opts, action);\n+\tunpack_tree_opts.head_idx = 1;\n+\tunpack_tree_opts.src_index = r->index;\n+\tunpack_tree_opts.dst_index = r->index;\n+\tunpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge;\n+\tunpack_tree_opts.update = 1;\n+\tunpack_tree_opts.merge = 1;\n+\tif (!detach_head)\n+\t\tunpack_tree_opts.reset = 1;\n+\n+\tif (repo_read_index_unmerged(r) < 0) {\n+\t\tret = error(_(\"could not read index\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!reset_hard && !fill_tree_descriptor(r, &desc[nr++], &head_oid)) {\n+\t\tret = error(_(\"failed to find tree of %s\"),\n+\t\t\t    oid_to_hex(&head_oid));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (!fill_tree_descriptor(r, &desc[nr++], oid)) {\n+\t\tret = error(_(\"failed to find tree of %s\"), oid_to_hex(oid));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\tif (unpack_trees(nr, desc, &unpack_tree_opts)) {\n+\t\tret = -1;\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+\ttree = parse_tree_indirect(oid);\n+\tprime_cache_tree(r, r->index, tree);\n+\n+\tif (write_locked_index(r->index, &lock, COMMIT_LOCK) < 0) {\n+\t\tret = error(_(\"could not write index\"));\n+\t\tgoto leave_reset_head;\n+\t}\n+\n+reset_head_refs:\n+\treflog_action = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);\n+\tstrbuf_addf(&msg, \"%s: \", reflog_action ? reflog_action : default_reflog_action);\n+\tprefix_len = msg.len;\n+\n+\tif (update_orig_head) {\n+\t\tif (!get_oid(\"ORIG_HEAD\", &oid_old_orig))\n+\t\t\told_orig = &oid_old_orig;\n+\t\tif (!get_oid(\"HEAD\", &oid_orig)) {\n+\t\t\torig = &oid_orig;\n+\t\t\tif (!reflog_orig_head) {\n+\t\t\t\tstrbuf_addstr(&msg, \"updating ORIG_HEAD\");\n+\t\t\t\treflog_orig_head = msg.buf;\n+\t\t\t}\n+\t\t\tupdate_ref(reflog_orig_head, \"ORIG_HEAD\", orig,\n+\t\t\t\t   old_orig, 0, UPDATE_REFS_MSG_ON_ERR);\n+\t\t} else if (old_orig)\n+\t\t\tdelete_ref(NULL, \"ORIG_HEAD\", old_orig, 0);\n+\t}\n+\n+\tif (!reflog_head) {\n+\t\tstrbuf_setlen(&msg, prefix_len);\n+\t\tstrbuf_addstr(&msg, \"updating HEAD\");\n+\t\treflog_head = msg.buf;\n+\t}\n+\tif (!switch_to_branch)\n+\t\tret = update_ref(reflog_head, \"HEAD\", oid, orig,\n+\t\t\t\t detach_head ? REF_NO_DEREF : 0,\n+\t\t\t\t UPDATE_REFS_MSG_ON_ERR);\n+\telse {\n+\t\tret = update_ref(reflog_head, switch_to_branch, oid,\n+\t\t\t\t NULL, 0, UPDATE_REFS_MSG_ON_ERR);\n+\t\tif (!ret)\n+\t\t\tret = create_symref(\"HEAD\", switch_to_branch,\n+\t\t\t\t\t    reflog_head);\n+\t}\n+\tif (run_hook)\n+\t\trun_hook_le(NULL, \"post-checkout\",\n+\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n+\t\t\t    oid_to_hex(oid), \"1\", NULL);\n+\n+leave_reset_head:\n+\tstrbuf_release(&msg);\n+\trollback_lock_file(&lock);\n+\twhile (nr)\n+\t\tfree((void *)desc[--nr].buffer);\n+\treturn ret;\n+\n+}\ndiff --git a/reset.h b/reset.h\nnew file mode 100644\nindex 0000000000..12f83c78e2\n--- /dev/null\n+++ b/reset.h\n@@ -0,0 +1,20 @@\n+#ifndef RESET_H\n+#define RESET_H\n+\n+#include \"hash.h\"\n+#include \"repository.h\"\n+\n+#define GIT_REFLOG_ACTION_ENVIRONMENT \"GIT_REFLOG_ACTION\"\n+\n+#define RESET_HEAD_DETACH (1<<0)\n+#define RESET_HEAD_HARD (1<<1)\n+#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2)\n+#define RESET_HEAD_REFS_ONLY (1<<3)\n+#define RESET_ORIG_HEAD (1<<4)\n+\n+int reset_head(struct repository *r, struct object_id *oid, const char *action,\n+\t       const char *switch_to_branch, unsigned flags,\n+\t       const char *reflog_orig_head, const char *reflog_head,\n+\t       const char *default_reflog_action);\n+\n+#endif\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394944","messageId":"937b4964d14cd2f41521722d370cf0d89b3b9f24.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 10/22] sequencer: rename stash_sha1 to stash_oid","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:57Z","receivedAt":"2020-04-07T14:28:42Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"The preferred terminology is to refer to object identifiers as \"OIDs\".\nRename the `stash_sha1` variable to `stash_oid` in order to conform to\nthis.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 4917c70a86..4a6812e90e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3659,23 +3659,23 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \n static int apply_autostash(const char *path)\n {\n-\tstruct strbuf stash_sha1 = STRBUF_INIT;\n+\tstruct strbuf stash_oid = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\n \tint ret = 0;\n \n-\tif (!read_oneliner(&stash_sha1, path,\n+\tif (!read_oneliner(&stash_oid, path,\n \t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n-\t\tstrbuf_release(&stash_sha1);\n+\t\tstrbuf_release(&stash_oid);\n \t\treturn 0;\n \t}\n-\tstrbuf_trim(&stash_sha1);\n+\tstrbuf_trim(&stash_oid);\n \n \tchild.git_cmd = 1;\n \tchild.no_stdout = 1;\n \tchild.no_stderr = 1;\n \targv_array_push(&child.args, \"stash\");\n \targv_array_push(&child.args, \"apply\");\n-\targv_array_push(&child.args, stash_sha1.buf);\n+\targv_array_push(&child.args, stash_oid.buf);\n \tif (!run_command(&child))\n \t\tfprintf(stderr, _(\"Applied autostash.\\n\"));\n \telse {\n@@ -3687,9 +3687,9 @@ static int apply_autostash(const char *path)\n \t\targv_array_push(&store.args, \"-m\");\n \t\targv_array_push(&store.args, \"autostash\");\n \t\targv_array_push(&store.args, \"-q\");\n-\t\targv_array_push(&store.args, stash_sha1.buf);\n+\t\targv_array_push(&store.args, stash_oid.buf);\n \t\tif (run_command(&store))\n-\t\t\tret = error(_(\"cannot store %s\"), stash_sha1.buf);\n+\t\t\tret = error(_(\"cannot store %s\"), stash_oid.buf);\n \t\telse\n \t\t\tfprintf(stderr,\n \t\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n@@ -3698,7 +3698,7 @@ static int apply_autostash(const char *path)\n \t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"));\n \t}\n \n-\tstrbuf_release(&stash_sha1);\n+\tstrbuf_release(&stash_oid);\n \treturn ret;\n }\n \n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394945","messageId":"3dbed2a832a713cba677bb482668a39ffd7f5ebf.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 09/22] sequencer: make apply_autostash() accept a path","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:27:56Z","receivedAt":"2020-04-07T14:28:42Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In order to make apply_autostash() more generic for future extraction, make\nit accept a `path` argument so that the location from where to read the\nreference to the autostash commit can be customized. Remove the `opts`\nargument since it was unused before anyway.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 14 +++++++-------\n 1 file changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 0d98ddc33c..4917c70a86 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3657,13 +3657,13 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n-static int apply_autostash(struct replay_opts *opts)\n+static int apply_autostash(const char *path)\n {\n \tstruct strbuf stash_sha1 = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\n \tint ret = 0;\n \n-\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(),\n+\tif (!read_oneliner(&stash_sha1, path,\n \t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n \t\tstrbuf_release(&stash_sha1);\n \t\treturn 0;\n@@ -3756,7 +3756,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,\n \t\treturn error(_(\"%s: not a valid OID\"), orig_head);\n \n \tif (run_git_checkout(r, opts, oid_to_hex(onto), action)) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \t\treturn error(_(\"could not detach HEAD\"));\n \t}\n@@ -4070,7 +4070,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\trun_command(&hook);\n \t\t\t}\n \t\t}\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \n \t\tif (!opts->quiet) {\n \t\t\tif (!opts->verbose)\n@@ -5079,7 +5079,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\ttodo_list_add_exec_commands(todo_list, commands);\n \n \tif (count_commands(todo_list) == 0) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \n \t\treturn error(_(\"nothing to do\"));\n@@ -5090,12 +5090,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (res == -1)\n \t\treturn -1;\n \telse if (res == -2) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \n \t\treturn -1;\n \t} else if (res == -3) {\n-\t\tapply_autostash(opts);\n+\t\tapply_autostash(rebase_path_autostash());\n \t\tsequencer_remove_state(opts);\n \t\ttodo_list_release(&new_todo);\n \n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394946","messageId":"3be4a27dfe0f91c6f0730d0b3a5d1883e4b0eac4.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 16/22] sequencer: extract perform_autostash() from rebase","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:28:03Z","receivedAt":"2020-04-07T14:28:46Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Lib-ify the autostash code by extracting perform_autostash() from rebase\ninto sequencer. In a future commit, this will be used to implement\n`--autostash` in other builtins.\n\nThis patch is best viewed with `--color-moved`.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n builtin/rebase.c | 49 -----------------------------------------------\n sequencer.c      | 50 ++++++++++++++++++++++++++++++++++++++++++++++++\n sequencer.h      |  2 ++\n 3 files changed, 52 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex ae345c9e57..08b89869b3 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1274,55 +1274,6 @@ static int check_exec_cmd(const char *cmd)\n \treturn 0;\n }\n \n-static void create_autostash(struct repository *r, const char *path,\n-\t\t\t     const char *default_reflog_action)\n-{\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct lock_file lock_file = LOCK_INIT;\n-\tint fd;\n-\n-\tfd = repo_hold_locked_index(r, &lock_file, 0);\n-\trefresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);\n-\tif (0 <= fd)\n-\t\trepo_update_index_if_able(r, &lock_file);\n-\trollback_lock_file(&lock_file);\n-\n-\tif (has_unstaged_changes(r, 1) ||\n-\t    has_uncommitted_changes(r, 1)) {\n-\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n-\t\tstruct object_id oid;\n-\n-\t\targv_array_pushl(&stash.args,\n-\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n-\t\tstash.git_cmd = 1;\n-\t\tstash.no_stdin = 1;\n-\t\tstrbuf_reset(&buf);\n-\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n-\t\t\tdie(_(\"Cannot autostash\"));\n-\t\tstrbuf_trim_trailing_newline(&buf);\n-\t\tif (get_oid(buf.buf, &oid))\n-\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n-\t\t\t    buf.buf);\n-\t\tstrbuf_reset(&buf);\n-\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n-\n-\t\tif (safe_create_leading_directories_const(path))\n-\t\t\tdie(_(\"Could not create directory for '%s'\"),\n-\t\t\t    path);\n-\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n-\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n-\t\tif (reset_head(r, NULL, \"reset --hard\",\n-\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n-\t\t\t       default_reflog_action) < 0)\n-\t\t\tdie(_(\"could not reset --hard\"));\n-\n-\t\tif (discard_index(r->index) < 0 ||\n-\t\t\trepo_read_index(r) < 0)\n-\t\t\tdie(_(\"could not read index\"));\n-\t}\n-\tstrbuf_release(&buf);\n-}\n-\n int cmd_rebase(int argc, const char **argv, const char *prefix)\n {\n \tstruct rebase_options options = REBASE_OPTIONS_INIT;\ndiff --git a/sequencer.c b/sequencer.c\nindex f5bb1cc1fb..e0b8262521 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -32,6 +32,7 @@\n #include \"alias.h\"\n #include \"commit-reach.h\"\n #include \"rebase-interactive.h\"\n+#include \"reset.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -3657,6 +3658,55 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n+void create_autostash(struct repository *r, const char *path,\n+\t\t      const char *default_reflog_action)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct lock_file lock_file = LOCK_INIT;\n+\tint fd;\n+\n+\tfd = repo_hold_locked_index(r, &lock_file, 0);\n+\trefresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);\n+\tif (0 <= fd)\n+\t\trepo_update_index_if_able(r, &lock_file);\n+\trollback_lock_file(&lock_file);\n+\n+\tif (has_unstaged_changes(r, 1) ||\n+\t    has_uncommitted_changes(r, 1)) {\n+\t\tstruct child_process stash = CHILD_PROCESS_INIT;\n+\t\tstruct object_id oid;\n+\n+\t\targv_array_pushl(&stash.args,\n+\t\t\t\t \"stash\", \"create\", \"autostash\", NULL);\n+\t\tstash.git_cmd = 1;\n+\t\tstash.no_stdin = 1;\n+\t\tstrbuf_reset(&buf);\n+\t\tif (capture_command(&stash, &buf, GIT_MAX_HEXSZ))\n+\t\t\tdie(_(\"Cannot autostash\"));\n+\t\tstrbuf_trim_trailing_newline(&buf);\n+\t\tif (get_oid(buf.buf, &oid))\n+\t\t\tdie(_(\"Unexpected stash response: '%s'\"),\n+\t\t\t    buf.buf);\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);\n+\n+\t\tif (safe_create_leading_directories_const(path))\n+\t\t\tdie(_(\"Could not create directory for '%s'\"),\n+\t\t\t    path);\n+\t\twrite_file(path, \"%s\", oid_to_hex(&oid));\n+\t\tprintf(_(\"Created autostash: %s\\n\"), buf.buf);\n+\t\tif (reset_head(r, NULL, \"reset --hard\",\n+\t\t\t       NULL, RESET_HEAD_HARD, NULL, NULL,\n+\t\t\t       default_reflog_action) < 0)\n+\t\t\tdie(_(\"could not reset --hard\"));\n+\n+\t\tif (discard_index(r->index) < 0 ||\n+\t\t\trepo_read_index(r) < 0)\n+\t\t\tdie(_(\"could not read index\"));\n+\t}\n+\tstrbuf_release(&buf);\n+}\n+\n int apply_autostash(const char *path)\n {\n \tstruct strbuf stash_oid = STRBUF_INIT;\ndiff --git a/sequencer.h b/sequencer.h\nindex ab686146d1..9d1fe0ccfb 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,\n \t\t\t const struct commit *current_head,\n \t\t\t const struct object_id *new_head);\n \n+void create_autostash(struct repository *r, const char *path,\n+\t\t      const char *default_reflog_action);\n int apply_autostash(const char *path);\n \n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394947","messageId":"129d5d97aefdc5b75046f0b9051c5fe9378f54ac.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 17/22] sequencer: unlink autostash in apply_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:28:04Z","receivedAt":"2020-04-07T14:28:47Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Explicitly remove autostash file in apply_autostash() once it has been\napplied successfully.\n\nThis is currently a no-op because the only users of this function will unlink\nthe state (including the autostash file) after this function runs.\nHowever, in the future, we will introduce a user of the function that\ndoes not explicitly remove the state so we do it here.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex e0b8262521..645bc47ce3 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3748,6 +3748,7 @@ int apply_autostash(const char *path)\n \t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"));\n \t}\n \n+\tunlink(path);\n \tstrbuf_release(&stash_oid);\n \treturn ret;\n }\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394948","messageId":"13f3fadbef41476a7a7715574621729eb8f6e200.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 21/22] t5520: make test_pull_autostash() accept expect_parent_num","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:28:08Z","receivedAt":"2020-04-07T14:28:48Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Before, test_pull_autostash() was hardcoded to run\n`test_cmp_rev HEAD^ copy` to test that a rebase happened. However, in a\nfuture patch, we plan on testing merging as well. Make\ntest_pull_autostash() accept a parent number as an argument so that, in\nthe future, we can test if a merge happened in addition to a rebase.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n t/t5520-pull.sh | 14 ++++++++------\n 1 file changed, 8 insertions(+), 6 deletions(-)\n\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex 2f86fca042..f610dc14de 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -10,11 +10,13 @@ modify () {\n }\n \n test_pull_autostash () {\n+\texpect_parent_num=\"$1\" &&\n+\tshift &&\n \tgit reset --hard before-rebase &&\n \techo dirty >new_file &&\n \tgit add new_file &&\n \tgit pull \"$@\" . copy &&\n-\ttest_cmp_rev HEAD^ copy &&\n+\ttest_cmp_rev HEAD^\"$expect_parent_num\" copy &&\n \techo dirty >expect &&\n \ttest_cmp expect new_file &&\n \techo \"modified again\" >expect &&\n@@ -369,22 +371,22 @@ test_expect_success '--rebase fails with multiple branches' '\n \n test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' '\n \ttest_config rebase.autostash true &&\n-\ttest_pull_autostash --rebase\n+\ttest_pull_autostash 1 --rebase\n '\n \n test_expect_success 'pull --rebase --autostash & rebase.autostash=true' '\n \ttest_config rebase.autostash true &&\n-\ttest_pull_autostash --rebase --autostash\n+\ttest_pull_autostash 1 --rebase --autostash\n '\n \n test_expect_success 'pull --rebase --autostash & rebase.autostash=false' '\n \ttest_config rebase.autostash false &&\n-\ttest_pull_autostash --rebase --autostash\n+\ttest_pull_autostash 1 --rebase --autostash\n '\n \n test_expect_success 'pull --rebase --autostash & rebase.autostash unset' '\n \ttest_unconfig rebase.autostash &&\n-\ttest_pull_autostash --rebase --autostash\n+\ttest_pull_autostash 1 --rebase --autostash\n '\n \n test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '\n@@ -422,7 +424,7 @@ test_expect_success 'pull.rebase' '\n \n test_expect_success 'pull --autostash & pull.rebase=true' '\n \ttest_config pull.rebase true &&\n-\ttest_pull_autostash --autostash\n+\ttest_pull_autostash 1 --autostash\n '\n \n test_expect_success 'pull --no-autostash & pull.rebase=true' '\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394949","messageId":"7e04ce8d8e8ec8308efda05ea7f0cd31f9612ea0.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 18/22] sequencer: implement save_autostash()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:28:05Z","receivedAt":"2020-04-07T14:28:49Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Extract common functionality of apply_autostash() into\napply_save_autostash() and use it to implement save_autostash(). This\nfunction will be used in a future commit.\n\nThe difference between save_autostash() and apply_autostash() is that\nthe former does not try to apply the stash. It skips that step and\njust stores the created entry in the stash reflog.\n\nThis is useful in the case where we abort an operation when an autostash\nis present but we don't want to dirty the worktree with the application\nof the stash. For example, in a future commit, we will implement\n`git merge --autostash`. Since merges can be aborted using\n`git reset --hard`, we'd make use of save_autostash() to save the\nautostash entry instead of applying it to the worktree thus keeping the\nworktree undirtied.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 37 +++++++++++++++++++++++++++----------\n sequencer.h |  1 +\n 2 files changed, 28 insertions(+), 10 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 645bc47ce3..084ea4117e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3707,7 +3707,7 @@ void create_autostash(struct repository *r, const char *path,\n \tstrbuf_release(&buf);\n }\n \n-int apply_autostash(const char *path)\n+static int apply_save_autostash(const char *path, int attempt_apply)\n {\n \tstruct strbuf stash_oid = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\n@@ -3720,13 +3720,17 @@ int apply_autostash(const char *path)\n \t}\n \tstrbuf_trim(&stash_oid);\n \n-\tchild.git_cmd = 1;\n-\tchild.no_stdout = 1;\n-\tchild.no_stderr = 1;\n-\targv_array_push(&child.args, \"stash\");\n-\targv_array_push(&child.args, \"apply\");\n-\targv_array_push(&child.args, stash_oid.buf);\n-\tif (!run_command(&child))\n+\tif (attempt_apply) {\n+\t\tchild.git_cmd = 1;\n+\t\tchild.no_stdout = 1;\n+\t\tchild.no_stderr = 1;\n+\t\targv_array_push(&child.args, \"stash\");\n+\t\targv_array_push(&child.args, \"apply\");\n+\t\targv_array_push(&child.args, stash_oid.buf);\n+\t\tret = run_command(&child);\n+\t}\n+\n+\tif (attempt_apply && !ret)\n \t\tfprintf(stderr, _(\"Applied autostash.\\n\"));\n \telse {\n \t\tstruct child_process store = CHILD_PROCESS_INIT;\n@@ -3742,10 +3746,13 @@ int apply_autostash(const char *path)\n \t\t\tret = error(_(\"cannot store %s\"), stash_oid.buf);\n \t\telse\n \t\t\tfprintf(stderr,\n-\t\t\t\t_(\"Applying autostash resulted in conflicts.\\n\"\n+\t\t\t\t_(\"%s\\n\"\n \t\t\t\t  \"Your changes are safe in the stash.\\n\"\n \t\t\t\t  \"You can run \\\"git stash pop\\\" or\"\n-\t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"));\n+\t\t\t\t  \" \\\"git stash drop\\\" at any time.\\n\"),\n+\t\t\t\tattempt_apply ?\n+\t\t\t\t_(\"Applying autostash resulted in conflicts.\") :\n+\t\t\t\t_(\"Autostash exists; creating a new stash entry.\"));\n \t}\n \n \tunlink(path);\n@@ -3753,6 +3760,16 @@ int apply_autostash(const char *path)\n \treturn ret;\n }\n \n+int save_autostash(const char *path)\n+{\n+\treturn apply_save_autostash(path, 0);\n+}\n+\n+int apply_autostash(const char *path)\n+{\n+\treturn apply_save_autostash(path, 1);\n+}\n+\n static const char *reflog_message(struct replay_opts *opts,\n \tconst char *sub_action, const char *fmt, ...)\n {\ndiff --git a/sequencer.h b/sequencer.h\nindex 9d1fe0ccfb..306d4cecfb 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -193,6 +193,7 @@ void commit_post_rewrite(struct repository *r,\n \n void create_autostash(struct repository *r, const char *path,\n \t\t      const char *default_reflog_action);\n+int save_autostash(const char *path);\n int apply_autostash(const char *path);\n \n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394950","messageId":"732b3f99455066ae780dd8a0e662173f1bd02e60.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 19/22] sequencer: implement apply_autostash_oid()","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:28:06Z","receivedAt":"2020-04-07T14:28:50Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Split apply_save_autostash() into apply_autostash_oid() and\napply_save_autostash() where the former operates on an OID string and\nthe latter reads the OID from a file before passing it into\napply_save_autostash_oid().\n\nThis function is required for a future commmit which will rely on being\nable to apply an autostash whose OID is stored as a string.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n sequencer.c | 38 ++++++++++++++++++++++++++------------\n sequencer.h |  1 +\n 2 files changed, 27 insertions(+), 12 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 084ea4117e..f1eba4cb1f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3707,26 +3707,18 @@ void create_autostash(struct repository *r, const char *path,\n \tstrbuf_release(&buf);\n }\n \n-static int apply_save_autostash(const char *path, int attempt_apply)\n+static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)\n {\n-\tstruct strbuf stash_oid = STRBUF_INIT;\n \tstruct child_process child = CHILD_PROCESS_INIT;\n \tint ret = 0;\n \n-\tif (!read_oneliner(&stash_oid, path,\n-\t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n-\t\tstrbuf_release(&stash_oid);\n-\t\treturn 0;\n-\t}\n-\tstrbuf_trim(&stash_oid);\n-\n \tif (attempt_apply) {\n \t\tchild.git_cmd = 1;\n \t\tchild.no_stdout = 1;\n \t\tchild.no_stderr = 1;\n \t\targv_array_push(&child.args, \"stash\");\n \t\targv_array_push(&child.args, \"apply\");\n-\t\targv_array_push(&child.args, stash_oid.buf);\n+\t\targv_array_push(&child.args, stash_oid);\n \t\tret = run_command(&child);\n \t}\n \n@@ -3741,9 +3733,9 @@ static int apply_save_autostash(const char *path, int attempt_apply)\n \t\targv_array_push(&store.args, \"-m\");\n \t\targv_array_push(&store.args, \"autostash\");\n \t\targv_array_push(&store.args, \"-q\");\n-\t\targv_array_push(&store.args, stash_oid.buf);\n+\t\targv_array_push(&store.args, stash_oid);\n \t\tif (run_command(&store))\n-\t\t\tret = error(_(\"cannot store %s\"), stash_oid.buf);\n+\t\t\tret = error(_(\"cannot store %s\"), stash_oid);\n \t\telse\n \t\t\tfprintf(stderr,\n \t\t\t\t_(\"%s\\n\"\n@@ -3755,6 +3747,23 @@ static int apply_save_autostash(const char *path, int attempt_apply)\n \t\t\t\t_(\"Autostash exists; creating a new stash entry.\"));\n \t}\n \n+\treturn ret;\n+}\n+\n+static int apply_save_autostash(const char *path, int attempt_apply)\n+{\n+\tstruct strbuf stash_oid = STRBUF_INIT;\n+\tint ret = 0;\n+\n+\tif (!read_oneliner(&stash_oid, path,\n+\t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n+\t\tstrbuf_release(&stash_oid);\n+\t\treturn 0;\n+\t}\n+\tstrbuf_trim(&stash_oid);\n+\n+\tret = apply_save_autostash_oid(stash_oid.buf, attempt_apply);\n+\n \tunlink(path);\n \tstrbuf_release(&stash_oid);\n \treturn ret;\n@@ -3770,6 +3779,11 @@ int apply_autostash(const char *path)\n \treturn apply_save_autostash(path, 1);\n }\n \n+int apply_autostash_oid(const char *stash_oid)\n+{\n+\treturn apply_save_autostash_oid(stash_oid, 1);\n+}\n+\n static const char *reflog_message(struct replay_opts *opts,\n \tconst char *sub_action, const char *fmt, ...)\n {\ndiff --git a/sequencer.h b/sequencer.h\nindex 306d4cecfb..5a85eeef92 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -195,6 +195,7 @@ void create_autostash(struct repository *r, const char *path,\n \t\t      const char *default_reflog_action);\n int save_autostash(const char *path);\n int apply_autostash(const char *path);\n+int apply_autostash_oid(const char *stash_oid);\n \n #define SUMMARY_INITIAL_COMMIT   (1 << 0)\n #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394951","messageId":"f9f698b79a1d3205c6f5f2f977c1ed37ece2266c.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 20/22] merge: teach --autostash option","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:28:07Z","receivedAt":"2020-04-07T14:28:51Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"In rebase, one can pass the `--autostash` option to cause the worktree\nto be automatically stashed before continuing with the rebase. This\noption is missing in merge, however.\n\nImplement the `--autostash` option and corresponding `merge.autoStash`\noption in merge which stashes before merging and then pops after.\n\nThis option is useful when a developer has some local changes on a topic\nbranch but they realize that their work depends on another branch.\nPreviously, they had to run something like\n\n\tgit fetch ...\n\tgit stash push\n\tgit merge FETCH_HEAD\n\tgit stash pop\n\nbut now, that is reduced to\n\n\tgit fetch ...\n\tgit merge --autostash FETCH_HEAD\n\nWhen an autostash is generated, it is automatically reapplied to the\nworktree only in three explicit situations:\n\n\t1. An incomplete merge is commit using `git commit`.\n\t2. A merge completes successfully.\n\t3. A merge is aborted using `git merge --abort`.\n\nIn all other situations where the merge state is removed using\nremove_merge_branch_state() such as aborting a merge via\n`git reset --hard`, the autostash is saved into the stash reflog\ninstead keeping the worktree clean.\n\nHelped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\nSuggested-by: Alban Gruin <alban.gruin@gmail.com>\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/config/merge.txt  |  10 +++\n Documentation/git-merge.txt     |  11 ++-\n Documentation/merge-options.txt |   8 ++\n branch.c                        |   1 +\n builtin/commit.c                |   2 +\n builtin/merge.c                 |  25 ++++++\n builtin/rebase.c                |   3 +-\n parse-options.h                 |   1 +\n path.c                          |   1 +\n path.h                          |   4 +-\n t/t3033-merge-toplevel.sh       |  22 ++++++\n t/t7600-merge.sh                | 132 ++++++++++++++++++++++++++++++++\n 12 files changed, 214 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt\nindex 6a313937f8..cb2ed58907 100644\n--- a/Documentation/config/merge.txt\n+++ b/Documentation/config/merge.txt\n@@ -70,6 +70,16 @@ merge.stat::\n \tWhether to print the diffstat between ORIG_HEAD and the merge result\n \tat the end of the merge.  True by default.\n \n+merge.autoStash::\n+\tWhen set to true, automatically create a temporary stash entry\n+\tbefore the operation begins, and apply it after the operation\n+\tends.  This means that you can run merge on a dirty worktree.\n+\tHowever, use with care: the final stash application after a\n+\tsuccessful merge might result in non-trivial conflicts.\n+\tThis option can be overridden by the `--no-autostash` and\n+\t`--autostash` options of linkgit:git-merge[1].\n+\tDefaults to false.\n+\n merge.tool::\n \tControls which merge tool is used by linkgit:git-mergetool[1].\n \tThe list below shows the valid built-in values.\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 092529c619..ec06b2f8c2 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -94,7 +94,8 @@ will be appended to the specified message.\n \n --abort::\n \tAbort the current conflict resolution process, and\n-\ttry to reconstruct the pre-merge state.\n+\ttry to reconstruct the pre-merge state. If an autostash entry is\n+\tpresent, apply it to the worktree.\n +\n If there were uncommitted worktree changes present when the merge\n started, 'git merge --abort' will in some cases be unable to\n@@ -102,11 +103,15 @@ reconstruct these changes. It is therefore recommended to always\n commit or stash your changes before running 'git merge'.\n +\n 'git merge --abort' is equivalent to 'git reset --merge' when\n-`MERGE_HEAD` is present.\n+`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present in\n+which case 'git merge --abort' applies the stash entry to the worktree\n+whereas 'git reset --merge' will save the stashed changes in the stash\n+reflog.\n \n --quit::\n \tForget about the current merge in progress. Leave the index\n-\tand the working tree as-is.\n+\tand the working tree as-is. If `MERGE_AUTOSTASH` is present, the\n+\tstash entry will be saved to the stash reflog.\n \n --continue::\n \tAfter a 'git merge' stops due to conflicts you can conclude the\ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 40dc4f5e8c..3985e6d4a9 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -155,6 +155,14 @@ ifndef::git-pull[]\n \tNote that not all merge strategies may support progress\n \treporting.\n \n+--autostash::\n+--no-autostash::\n+\tAutomatically create a temporary stash entry before the operation\n+\tbegins, and apply it after the operation ends.  This means\n+\tthat you can run the operation on a dirty worktree.  However, use\n+\twith care: the final stash application after a successful\n+\tmerge might result in non-trivial conflicts.\n+\n endif::git-pull[]\n \n --allow-unrelated-histories::\ndiff --git a/branch.c b/branch.c\nindex 579494738a..2d9e7675a6 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)\n \tunlink(git_path_merge_rr(r));\n \tunlink(git_path_merge_msg(r));\n \tunlink(git_path_merge_mode(r));\n+\tsave_autostash(git_path_merge_autostash(r));\n }\n \n void remove_branch_state(struct repository *r, int verbose)\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 7ba33a3bec..c1b25d2954 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1713,6 +1713,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)\n \t\t\t\t     &oid, flags);\n \t}\n \n+\tapply_autostash(git_path_merge_autostash(the_repository));\n+\n \tUNLEAK(err);\n \tUNLEAK(sb);\n \treturn 0;\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex d127d2225f..f86ae90bfc 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -82,6 +82,7 @@ static int show_progress = -1;\n static int default_to_upstream = 1;\n static int signoff;\n static const char *sign_commit;\n+static int autostash;\n static int no_verify;\n \n static struct strategy all_strategy[] = {\n@@ -286,6 +287,7 @@ static struct option builtin_merge_options[] = {\n \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n+\tOPT_AUTOSTASH(&autostash),\n \tOPT_BOOL(0, \"overwrite-ignore\", &overwrite_ignore, N_(\"update ignored files (default)\")),\n \tOPT_BOOL(0, \"signoff\", &signoff, N_(\"add Signed-off-by:\")),\n \tOPT_BOOL(0, \"no-verify\", &no_verify, N_(\"bypass pre-merge-commit and commit-msg hooks\")),\n@@ -475,6 +477,7 @@ static void finish(struct commit *head_commit,\n \t/* Run a post-merge hook */\n \trun_hook_le(NULL, \"post-merge\", squash ? \"1\" : \"0\", NULL);\n \n+\tapply_autostash(git_path_merge_autostash(the_repository));\n \tstrbuf_release(&reflog_message);\n }\n \n@@ -634,6 +637,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)\n \t\treturn 0;\n \t} else if (!strcmp(k, \"gpg.mintrustlevel\")) {\n \t\tcheck_trust_level = 0;\n+\t} else if (!strcmp(k, \"merge.autostash\")) {\n+\t\tautostash = git_config_bool(k, v);\n+\t\treturn 0;\n \t}\n \n \tstatus = fmt_merge_msg_config(k, v, cb);\n@@ -1281,6 +1287,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (abort_current_merge) {\n \t\tint nargc = 2;\n \t\tconst char *nargv[] = {\"reset\", \"--merge\", NULL};\n+\t\tstruct strbuf stash_oid = STRBUF_INIT;\n \n \t\tif (orig_argc != 2)\n \t\t\tusage_msg_opt(_(\"--abort expects no arguments\"),\n@@ -1289,8 +1296,17 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\tif (!file_exists(git_path_merge_head(the_repository)))\n \t\t\tdie(_(\"There is no merge to abort (MERGE_HEAD missing).\"));\n \n+\t\tif (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),\n+\t\t    READ_ONELINER_SKIP_IF_EMPTY))\n+\t\t\tunlink(git_path_merge_autostash(the_repository));\n+\n \t\t/* Invoke 'git reset --merge' */\n \t\tret = cmd_reset(nargc, nargv, prefix);\n+\n+\t\tif (stash_oid.len)\n+\t\t\tapply_autostash_oid(stash_oid.buf);\n+\n+\t\tstrbuf_release(&stash_oid);\n \t\tgoto done;\n \t}\n \n@@ -1513,6 +1529,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\t\tgoto done;\n \t\t}\n \n+\t\tif (autostash)\n+\t\t\tcreate_autostash(the_repository,\n+\t\t\t\t\t git_path_merge_autostash(the_repository),\n+\t\t\t\t\t \"merge\");\n \t\tif (checkout_fast_forward(the_repository,\n \t\t\t\t\t  &head_commit->object.oid,\n \t\t\t\t\t  &commit->object.oid,\n@@ -1579,6 +1599,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \tif (fast_forward == FF_ONLY)\n \t\tdie(_(\"Not possible to fast-forward, aborting.\"));\n \n+\tif (autostash)\n+\t\tcreate_autostash(the_repository,\n+\t\t\t\t git_path_merge_autostash(the_repository),\n+\t\t\t\t \"merge\");\n+\n \t/* We are going to make a new commit. */\n \tgit_committer_info(IDENT_STRICT);\n \ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 08b89869b3..bc4fc69906 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -1376,8 +1376,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)\n \t\t{ OPTION_STRING, 'S', \"gpg-sign\", &gpg_sign, N_(\"key-id\"),\n \t\t\tN_(\"GPG-sign commits\"),\n \t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n-\t\tOPT_BOOL(0, \"autostash\", &options.autostash,\n-\t\t\t N_(\"automatically stash/stash pop before and after\")),\n+\t\tOPT_AUTOSTASH(&options.autostash),\n \t\tOPT_STRING_LIST('x', \"exec\", &exec, N_(\"exec\"),\n \t\t\t\tN_(\"add exec lines after each commit of the \"\n \t\t\t\t   \"editable list\")),\ndiff --git a/parse-options.h b/parse-options.h\nindex fece5ba628..46af942093 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -336,5 +336,6 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);\n #define OPT_CLEANUP(v) OPT_STRING(0, \"cleanup\", v, N_(\"mode\"), N_(\"how to strip spaces and #comments from message\"))\n #define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, \"pathspec-from-file\", v, N_(\"read pathspec from file\"))\n #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\"))\n+#define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n \n #endif\ndiff --git a/path.c b/path.c\nindex 88cf593007..d764738146 100644\n--- a/path.c\n+++ b/path.c\n@@ -1535,5 +1535,6 @@ REPO_GIT_PATH_FUNC(merge_msg, \"MERGE_MSG\")\n REPO_GIT_PATH_FUNC(merge_rr, \"MERGE_RR\")\n REPO_GIT_PATH_FUNC(merge_mode, \"MERGE_MODE\")\n REPO_GIT_PATH_FUNC(merge_head, \"MERGE_HEAD\")\n+REPO_GIT_PATH_FUNC(merge_autostash, \"MERGE_AUTOSTASH\")\n REPO_GIT_PATH_FUNC(fetch_head, \"FETCH_HEAD\")\n REPO_GIT_PATH_FUNC(shallow, \"shallow\")\ndiff --git a/path.h b/path.h\nindex 14d6dcad16..1f1bf8f87a 100644\n--- a/path.h\n+++ b/path.h\n@@ -177,11 +177,12 @@ struct path_cache {\n \tconst char *merge_rr;\n \tconst char *merge_mode;\n \tconst char *merge_head;\n+\tconst char *merge_autostash;\n \tconst char *fetch_head;\n \tconst char *shallow;\n };\n \n-#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n+#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n \n const char *git_path_cherry_pick_head(struct repository *r);\n const char *git_path_revert_head(struct repository *r);\n@@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);\n const char *git_path_merge_rr(struct repository *r);\n const char *git_path_merge_mode(struct repository *r);\n const char *git_path_merge_head(struct repository *r);\n+const char *git_path_merge_autostash(struct repository *r);\n const char *git_path_fetch_head(struct repository *r);\n const char *git_path_shallow(struct repository *r);\n \ndiff --git a/t/t3033-merge-toplevel.sh b/t/t3033-merge-toplevel.sh\nindex d314599428..e29c284b9b 100755\n--- a/t/t3033-merge-toplevel.sh\n+++ b/t/t3033-merge-toplevel.sh\n@@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' '\n \ttest_must_fail git merge five\n '\n \n+test_expect_success 'refuse two-project merge by default, quit before --autostash happens' '\n+\tt3033_reset &&\n+\tgit reset --hard four &&\n+\techo change >>one.t &&\n+\tgit diff >expect &&\n+\ttest_must_fail git merge --autostash five 2>err &&\n+\ttest_i18ngrep ! \"stash\" err &&\n+\tgit diff >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_expect_success 'two-project merge with --allow-unrelated-histories' '\n \tt3033_reset &&\n \tgit reset --hard four &&\n@@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' '\n \tgit diff --exit-code five\n '\n \n+test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' '\n+\tt3033_reset &&\n+\tgit reset --hard four &&\n+\techo change >>one.t &&\n+\tgit diff one.t >expect &&\n+\tgit merge --allow-unrelated-histories --autostash five 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit diff one.t >actual &&\n+\ttest_cmp expect actual\n+'\n+\n test_done\ndiff --git a/t/t7600-merge.sh b/t/t7600-merge.sh\nindex 4fa0ef8e3b..5883a6adc3 100755\n--- a/t/t7600-merge.sh\n+++ b/t/t7600-merge.sh\n@@ -30,13 +30,17 @@ Testing basic merge operations/option parsing.\n . \"$TEST_DIRECTORY\"/lib-gpg.sh\n \n test_write_lines 1 2 3 4 5 6 7 8 9 >file\n+cp file file.orig\n test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1\n+test_write_lines 1 2 '3 X' 4 5 6 7 8 9 >file.3\n test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5\n test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9\n test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y\n test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1\n test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5\n+test_write_lines '1 X' 2 3 4 5 6 7 8 '9 X' >result.1-9\n test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9\n+test_write_lines '1 X' 2 '3 X' 4 '5 X' 6 7 8 '9 X' >result.1-3-5-9\n test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z\n \n create_merge_msgs () {\n@@ -675,6 +679,134 @@ test_expect_success 'refresh the index before merging' '\n \tgit merge c3\n '\n \n+test_expect_success 'merge with --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit merge --autostash c2 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'merge with merge.autoStash' '\n+\ttest_config merge.autoStash true &&\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit merge c2 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'fast-forward merge with --autostash' '\n+\tgit reset --hard c0 &&\n+\tgit merge-file file file.orig file.5 &&\n+\tgit merge --autostash c1 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\ttest_cmp result.1-5 file\n+'\n+\n+test_expect_success 'octopus merge with --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.3 &&\n+\tgit merge --autostash c2 c3 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5-9 merge-result &&\n+\ttest_cmp result.1-3-5-9 file\n+'\n+\n+test_expect_success 'conflicted merge with --autostash, --abort restores stash' '\n+\tgit reset --hard c3 &&\n+\tcp file.1 file &&\n+\ttest_must_fail git merge --autostash c7 &&\n+\tgit merge --abort 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\ttest_cmp file.1 file\n+'\n+\n+test_expect_success 'completed merge (git commit) with --no-commit and --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit diff >expect &&\n+\tgit merge --no-commit --autostash c2 &&\n+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n+\ttest_cmp expect actual &&\n+\tgit commit 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'completed merge (git merge --continue) with --no-commit and --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit diff >expect &&\n+\tgit merge --no-commit --autostash c2 &&\n+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n+\ttest_cmp expect actual &&\n+\tgit merge --continue 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-5 merge-result &&\n+\ttest_cmp result.1-5-9 file\n+'\n+\n+test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit diff >expect &&\n+\tgit merge --no-commit --autostash c2 &&\n+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n+\ttest_cmp expect actual &&\n+\tgit merge --abort 2>err &&\n+\ttest_i18ngrep \"Applied autostash.\" err &&\n+\tgit diff >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit diff >expect &&\n+\tgit merge --no-commit --autostash c2 &&\n+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n+\ttest_cmp expect actual &&\n+\tgit reset --hard 2>err &&\n+\ttest_i18ngrep \"Autostash exists; creating a new stash entry.\" err &&\n+\tgit diff --exit-code\n+'\n+\n+test_expect_success 'quit merge with --no-commit and --autostash' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9 &&\n+\tgit diff >expect &&\n+\tgit merge --no-commit --autostash c2 &&\n+\tgit stash show -p MERGE_AUTOSTASH >actual &&\n+\ttest_cmp expect actual &&\n+\tgit diff HEAD >expect &&\n+\tgit merge --quit 2>err &&\n+\ttest_i18ngrep \"Autostash exists; creating a new stash entry.\" err &&\n+\tgit diff HEAD >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'merge with conflicted --autostash changes' '\n+\tgit reset --hard c1 &&\n+\tgit merge-file file file.orig file.9y &&\n+\tgit diff >expect &&\n+\ttest_when_finished \"test_might_fail git stash drop\" &&\n+\tgit merge --autostash c3 2>err &&\n+\ttest_i18ngrep \"Applying autostash resulted in conflicts.\" err &&\n+\tgit show HEAD:file >merge-result &&\n+\ttest_cmp result.1-9 merge-result &&\n+\tgit stash show -p >actual &&\n+\ttest_cmp expect actual\n+'\n+\n cat >expected.branch <<\\EOF\n Merge branch 'c5-branch' (early part)\n EOF\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394952","messageId":"f4fec1e780555900be78531890c2a70e0e03eabf.1586269543.git.liu.denton@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"[PATCH v5 22/22] pull: pass --autostash to merge","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2020-04-07T14:28:09Z","receivedAt":"2020-04-07T14:28:54Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Before, `--autostash` only worked with `git pull --rebase`. However, in\nthe last patch, merge learned `--autostash` as well so there's no reason\nwhy we should have this restriction anymore. Teach pull to pass\n`--autostash` to merge, just like it did for rebase.\n\nSigned-off-by: Denton Liu <liu.denton@gmail.com>\n---\n Documentation/git-pull.txt      |  9 -------\n Documentation/merge-options.txt |  4 +--\n builtin/pull.c                  |  9 ++++---\n t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------\n 4 files changed, 42 insertions(+), 23 deletions(-)\n\ndiff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt\nindex dfb901f8b8..ba3772de9f 100644\n--- a/Documentation/git-pull.txt\n+++ b/Documentation/git-pull.txt\n@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.\n --no-rebase::\n \tOverride earlier --rebase.\n \n---autostash::\n---no-autostash::\n-\tBefore starting rebase, stash local modifications away (see\n-\tlinkgit:git-stash[1]) if needed, and apply the stash entry when\n-\tdone. `--no-autostash` is useful to override the `rebase.autoStash`\n-\tconfiguration variable (see linkgit:git-config[1]).\n-+\n-This option is only valid when \"--rebase\" is used.\n-\n Options related to fetching\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~\n \ndiff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt\nindex 3985e6d4a9..48bfcda084 100644\n--- a/Documentation/merge-options.txt\n+++ b/Documentation/merge-options.txt\n@@ -155,6 +155,8 @@ ifndef::git-pull[]\n \tNote that not all merge strategies may support progress\n \treporting.\n \n+endif::git-pull[]\n+\n --autostash::\n --no-autostash::\n \tAutomatically create a temporary stash entry before the operation\n@@ -163,8 +165,6 @@ ifndef::git-pull[]\n \twith care: the final stash application after a successful\n \tmerge might result in non-trivial conflicts.\n \n-endif::git-pull[]\n-\n --allow-unrelated-histories::\n \tBy default, `git merge` command refuses to merge histories\n \tthat do not share a common ancestor.  This option can be\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 3e624d1e00..9beb4841d1 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -163,7 +163,7 @@ static struct option pull_options[] = {\n \t\tN_(\"verify that the named commit has a valid GPG signature\"),\n \t\tPARSE_OPT_NOARG),\n \tOPT_BOOL(0, \"autostash\", &opt_autostash,\n-\t\tN_(\"automatically stash/stash pop before and after rebase\")),\n+\t\tN_(\"automatically stash/stash pop before and after\")),\n \tOPT_PASSTHRU_ARGV('s', \"strategy\", &opt_strategies, N_(\"strategy\"),\n \t\tN_(\"merge strategy to use\"),\n \t\t0),\n@@ -661,6 +661,10 @@ static int run_merge(void)\n \targv_array_pushv(&args, opt_strategy_opts.argv);\n \tif (opt_gpg_sign)\n \t\targv_array_push(&args, opt_gpg_sign);\n+\tif (opt_autostash == 0)\n+\t\targv_array_push(&args, \"--no-autostash\");\n+\telse if (opt_autostash == 1)\n+\t\targv_array_push(&args, \"--autostash\");\n \tif (opt_allow_unrelated_histories > 0)\n \t\targv_array_push(&args, \"--allow-unrelated-histories\");\n \n@@ -908,9 +912,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \tif (get_oid(\"HEAD\", &orig_head))\n \t\toidclr(&orig_head);\n \n-\tif (!opt_rebase && opt_autostash != -1)\n-\t\tdie(_(\"--[no-]autostash option is only valid with --rebase.\"));\n-\n \tautostash = config_autostash;\n \tif (opt_rebase) {\n \t\tif (opt_autostash != -1)\ndiff --git a/t/t5520-pull.sh b/t/t5520-pull.sh\nindex f610dc14de..37535d63a9 100755\n--- a/t/t5520-pull.sh\n+++ b/t/t5520-pull.sh\n@@ -28,7 +28,7 @@ test_pull_autostash_fail () {\n \techo dirty >new_file &&\n \tgit add new_file &&\n \ttest_must_fail git pull \"$@\" . copy 2>err &&\n-\ttest_i18ngrep \"uncommitted changes.\" err\n+\ttest_i18ngrep \"\\(uncommitted changes.\\)\\|\\(overwritten by merge:\\)\" err\n }\n \n test_expect_success setup '\n@@ -404,13 +404,40 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '\n \ttest_pull_autostash_fail --rebase --no-autostash\n '\n \n-for i in --autostash --no-autostash\n-do\n-\ttest_expect_success \"pull $i (without --rebase) is illegal\" '\n-\t\ttest_must_fail git pull $i . copy 2>err &&\n-\t\ttest_i18ngrep \"only valid with --rebase\" err\n-\t'\n-done\n+test_expect_success 'pull succeeds with dirty working directory and merge.autostash set' '\n+\ttest_config merge.autostash true &&\n+\ttest_pull_autostash 2\n+'\n+\n+test_expect_success 'pull --autostash & merge.autostash=true' '\n+\ttest_config merge.autostash true &&\n+\ttest_pull_autostash 2 --autostash\n+'\n+\n+test_expect_success 'pull --autostash & merge.autostash=false' '\n+\ttest_config merge.autostash false &&\n+\ttest_pull_autostash 2 --autostash\n+'\n+\n+test_expect_success 'pull --autostash & merge.autostash unset' '\n+\ttest_unconfig merge.autostash &&\n+\ttest_pull_autostash 2 --autostash\n+'\n+\n+test_expect_success 'pull --no-autostash & merge.autostash=true' '\n+\ttest_config merge.autostash true &&\n+\ttest_pull_autostash_fail --no-autostash\n+'\n+\n+test_expect_success 'pull --no-autostash & merge.autostash=false' '\n+\ttest_config merge.autostash false &&\n+\ttest_pull_autostash_fail --no-autostash\n+'\n+\n+test_expect_success 'pull --no-autostash & merge.autostash unset' '\n+\ttest_unconfig merge.autostash &&\n+\ttest_pull_autostash_fail --no-autostash\n+'\n \n test_expect_success 'pull.rebase' '\n \tgit reset --hard before-rebase &&\n-- \n2.26.0.159.g23e2136ad0\n\n"},{"id":"394961","messageId":"0b112a11-ef46-10e0-e0ae-ab895eb5fab0@gmail.com","threadId":"52048","inReplyTo":"20200407130946.GA2503067@generichostname","subject":"Re: [PATCH v4 21/23] merge: teach --autostash option","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-07T15:06:31Z","receivedAt":"2020-04-07T15:06:36Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 07/04/2020 14:09, Denton Liu wrote:\n> Hi Phillip,\n> \n> Thanks for the review. The documentation rewordings are particularly\n> helpful.\n\nThanks I'm glad they were useful\n\n> On Mon, Apr 06, 2020 at 04:20:24PM +0100, Phillip Wood wrote:\n>>>    --quit::\n>>>    \tForget about the current merge in progress. Leave the index\n>>\n>> Should we be saving the stash if the user runs 'git merge --quit'?\n> \n> Yes, this is its current behaviour, although it's undocumented. In my\n> next reroll, I've added a test case and documentation for this. This is\n> similar to how `git rebase --quit` behaves.\n\nThat's great - I'll try and read through v5 later in the week, it looks \nlike there aren't too many changes\n\n> Now that I think about it, when `git rebase --quit` happens, the\n> autostash is just dropped out of existence, which is probably\n> undesirable behaviour. I'll probably submit a later series to save that\n> autostash so that it's not lost forever.\n\nThat's a good idea, Thanks\n\nPhillip\n\n> Thanks,\n> \n> Denton\n> \n"},{"id":"395030","messageId":"xmqqh7xurg5s.fsf@gitster.c.googlers.com","threadId":"52048","inReplyTo":"dc4674d222317fa2f503e6e1b3cad6a78dd70676.1586269543.git.liu.denton@gmail.com","subject":"Re: [PATCH v5 04/22] sequencer: make file exists check more efficient","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-04-08T00:10:23Z","receivedAt":"2020-04-08T00:10:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> We currently check whether a file exists and return early before reading\n> the file. Instead of accessing the file twice, always read the file and\n> check `errno` to see if the file doesn't exist.\n>\n> Signed-off-by: Denton Liu <liu.denton@gmail.com>\n> ---\n>  sequencer.c | 6 ++----\n>  1 file changed, 2 insertions(+), 4 deletions(-)\n\nIt's not likely for us to break strbuf_read_file() in such a way\nthat it would clobber errno when it sees a failure from open, so\nthis should be OK.  If we were racing with somebody else who is\ntrying to remove 'path', we may have said \"ah, the file is there,\nlet's try to read --- oops, we cannot read it after all\" with the\ncurrent code, but the updated code would take advantage of the\natomicity of open() to avoid such race.  If we can open it, we would\nread it unless there is an I/O error.  If we failed to open it, we\nknow the file wasn't there when we attempted to read.  Good.\n\n> diff --git a/sequencer.c b/sequencer.c\n> index faab0b13e8..a961cf5a9b 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -433,11 +433,9 @@ static int read_oneliner(struct strbuf *buf,\n>  {\n>  \tint orig_len = buf->len;\n>  \n> -\tif (!file_exists(path))\n> -\t\treturn 0;\n> -\n>  \tif (strbuf_read_file(buf, path, 0) < 0) {\n> -\t\twarning_errno(_(\"could not read '%s'\"), path);\n> +\t\tif (errno != ENOENT && errno != ENOTDIR)\n> +\t\t\twarning_errno(_(\"could not read '%s'\"), path);\n>  \t\treturn 0;\n>  \t}\n"},{"id":"395031","messageId":"xmqqd08irg3d.fsf@gitster.c.googlers.com","threadId":"52048","inReplyTo":"f30ad07823c6e8f47c217ac24a06e02d3e0af554.1586269543.git.liu.denton@gmail.com","subject":"Re: [PATCH v5 05/22] sequencer: make read_oneliner() accept flags","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-04-08T00:11:50Z","receivedAt":"2020-04-08T00:11:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> In a future commit, we will need read_oneliner() to accept flags other\n> than just `skip_if_empty`. Instead of having an argument for each flag,\n> teach read_oneliner() to accept the bitfield `flags` instead. For now,\n> only recognize the `READ_ONELINER_SKIP_IF_EMPTY` flag. More flags will\n> be added in a future commit.\n\nGood move.  Looked sensible.\n\nThanks.\n"},{"id":"395032","messageId":"xmqq8sj6rffj.fsf@gitster.c.googlers.com","threadId":"52048","inReplyTo":"0cc279fc14dd27ed2606064d236115aa72551dce.1586269543.git.liu.denton@gmail.com","subject":"Re: [PATCH v5 08/22] rebase: use read_oneliner()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-04-08T00:26:08Z","receivedAt":"2020-04-08T00:26:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Denton Liu <liu.denton@gmail.com> writes:\n\n> Since in sequencer.c, read_one() basically duplicates the functionality\n> of read_oneliner(), reduce code duplication by replacing read_one() with\n> read_oneliner().\n>\n> This was done with the following Coccinelle script\n>\n> \t@@\n> \texpression a, b;\n> \t@@\n> \t- read_one(a, b)\n> \t+ !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)\n\nWARN_MISSING, you mean?\n\n> -/* Read one file, then strip line endings */\n> -static int read_one(const char *path, struct strbuf *buf)\n> -{\n> -\tif (strbuf_read_file(buf, path, 0) < 0)\n> -\t\treturn error_errno(_(\"could not read '%s'\"), path);\n> -\tstrbuf_trim_trailing_newline(buf);\n> -\treturn 0;\n> -}\n> -\n\nOK.  Both functions trim the end-of-line marker from the end of the\nstring, so this is truly a no-op that reduces the code.  Good.\n"},{"id":"395194","messageId":"eaf864e6-8441-c5a6-3c15-b71c522ec43d@gmail.com","threadId":"52048","inReplyTo":"cover.1586269542.git.liu.denton@gmail.com","subject":"Re: [PATCH v5 00/22] merge: learn --autostash","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2020-04-10T15:34:46Z","receivedAt":"2020-04-10T15:34:54Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Denton\n\nOn 07/04/2020 15:27, Denton Liu wrote:\n > [...]\n> Chagnes since v4:\n> \n> * Drop 'sequencer: reuse strbuf_trim() in read_oneliner()'\n> \n> * Fix commit message in 'sequencer: make apply_autostash() accept a path'\n> \n> * Add more detail in commit message 'sequencer: implement save_autostash()'\n> \n> * In 'merge: teach --autostash option', improve documentation, remove\n>    builtin/reset.c changes, add a `git merge --quit` test case\n> [...]\n> Range-diff against v4:\n>   1:  c26771e934 =  1:  c26771e934 Makefile: ASCII-sort += lists\n>   2:  059e0e8e43 =  2:  059e0e8e43 t7600: use test_write_lines()\n>   3:  76585e5b13 =  3:  76585e5b13 sequencer: stop leaking buf\n>   4:  c7a3cfa200 <  -:  ---------- sequencer: reuse strbuf_trim() in read_oneliner()\n\nI think it makes sense to drop that one, it was more trouble than it was \nworth in the end\n\n>   5:  58da8e9555 !  4:  dc4674d222 sequencer: make file exists check more efficient\n>      @@ Commit message\n>           check `errno` to see if the file doesn't exist.\n>       \n>        ## sequencer.c ##\n>      -@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,\n>      - static int read_oneliner(struct strbuf *buf,\n>      - \tconst char *path, int skip_if_empty)\n>      +@@ sequencer.c: static int read_oneliner(struct strbuf *buf,\n>        {\n>      --\n>      + \tint orig_len = buf->len;\n>      +\n>       -\tif (!file_exists(path))\n>       -\t\treturn 0;\n>       -\n>      - \tstrbuf_reset(buf);\n>        \tif (strbuf_read_file(buf, path, 0) < 0) {\n>       -\t\twarning_errno(_(\"could not read '%s'\"), path);\n>       +\t\tif (errno != ENOENT && errno != ENOTDIR)\n>   6:  6ead240957 !  5:  f30ad07823 sequencer: make read_oneliner() accept flags\n>      @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n>       +#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)\n>       +\n>        /*\n>      -  * Resets a strbuf then reads a file that was presumably written by a shell\n>      -  * script, i.e. with an end-of-line marker that needs to be stripped.\n>      +  * Reads a file that was presumably written by a shell script, i.e. with an\n>      +  * end-of-line marker that needs to be stripped.\n>       @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,\n>         * Returns 1 if the file was read, 0 if it could not be read or does not exist.\n>         */\n>      @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n>       -\tconst char *path, int skip_if_empty)\n>       +\tconst char *path, unsigned flags)\n>        {\n>      - \tstrbuf_reset(buf);\n>      - \tif (strbuf_read_file(buf, path, 0) < 0) {\n>      + \tint orig_len = buf->len;\n>      +\n>       @@ sequencer.c: static int read_oneliner(struct strbuf *buf,\n>      + \t\tbuf->buf[buf->len] = '\\0';\n>      + \t}\n>        \n>      - \tstrbuf_trim(buf);\n>      -\n>      --\tif (skip_if_empty && !buf->len)\n>      -+\tif ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !buf->len)\n>      +-\tif (skip_if_empty && buf->len == orig_len)\n>      ++\tif ((flags & READ_ONELINER_SKIP_IF_EMPTY) && buf->len == orig_len)\n>        \t\treturn 0;\n>        \n>        \treturn 1;\n>      @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n>        \t\t\t\tstrbuf_reset(&buf);\n>        \t\t\telse {\n>       @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n>      - \t\t\t}\n>      + \t\t\tstrbuf_reset(&buf);\n>        \t\t}\n>        \n>       -\t\tif (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {\n>      @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n>        \t\t\t\topts->allow_rerere_auto = RERERE_AUTOUPDATE;\n>        \t\t\telse if (!strcmp(buf.buf, \"--no-rerere-autoupdate\"))\n>       @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)\n>      - \t\tread_strategy_opts(opts, &buf);\n>      + \t\tstrbuf_reset(&buf);\n>        \n>        \t\tif (read_oneliner(&opts->current_fixups,\n>       -\t\t\t\t  rebase_path_current_fixups(), 1)) {\n>   7:  2e7922b259 !  6:  8c504427e3 sequencer: configurably warn on non-existent files\n>      @@ Commit message\n>           read_oneliner() will want the ability to output warnings in the event\n>           that the `path` doesn't exist. Introduce the\n>           `READ_ONELINER_WARN_MISSING` flag which, if active, would issue a\n>      -    warning when a file doesn't exist by skipping the `!file_exists()` check\n>      -    and letting `strbuf_read_file()` handle that case.\n>      +    warning when a file doesn't exist by always executing warning_errno()\n>      +    in the case where strbuf_read_file() fails.\n>       \n>        ## sequencer.c ##\n>       @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,\n>      @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n>       +#define READ_ONELINER_WARN_MISSING (1 << 1)\n>        \n>        /*\n>      -  * Resets a strbuf then reads a file that was presumably written by a shell\n>      +  * Reads a file that was presumably written by a shell script, i.e. with an\n>       @@ sequencer.c: static int read_oneliner(struct strbuf *buf,\n>      - {\n>      - \tstrbuf_reset(buf);\n>      + \tint orig_len = buf->len;\n>      +\n>        \tif (strbuf_read_file(buf, path, 0) < 0) {\n>       -\t\tif (errno != ENOENT && errno != ENOTDIR)\n>       +\t\tif ((flags & READ_ONELINER_WARN_MISSING) ||\n>      -+\t\t\t\t(errno != ENOENT && errno != ENOTDIR))\n>      ++\t\t    (errno != ENOENT && errno != ENOTDIR))\n>        \t\t\twarning_errno(_(\"could not read '%s'\"), path);\n>        \t\treturn 0;\n>        \t}\n>   8:  6c3c37994b !  7:  50e93770e7 sequencer: make read_oneliner() extern\n>      @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n>       -#define READ_ONELINER_WARN_MISSING (1 << 1)\n>       -\n>       -/*\n>      -- * Resets a strbuf then reads a file that was presumably written by a shell\n>      -- * script, i.e. with an end-of-line marker that needs to be stripped.\n>      +- * Reads a file that was presumably written by a shell script, i.e. with an\n>      +- * end-of-line marker that needs to be stripped.\n>       - *\n>       - * Note that only the last end-of-line marker is stripped, consistent with the\n>       - * behavior of \"$(cat path)\" in a shell script.\n>      @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f\n>       +int read_oneliner(struct strbuf *buf,\n>        \tconst char *path, unsigned flags)\n>        {\n>      - \tstrbuf_reset(buf);\n>      + \tint orig_len = buf->len;\n>       \n>        ## sequencer.h ##\n>       @@ sequencer.h: void print_commit_summary(struct repository *repo,\n>      @@ sequencer.h: void print_commit_summary(struct repository *repo,\n>       +#define READ_ONELINER_WARN_MISSING (1 << 1)\n>       +\n>       +/*\n>      -+ * Resets a strbuf then reads a file that was presumably written by a shell\n>      -+ * script, i.e. with an end-of-line marker that needs to be stripped.\n>      ++ * Reads a file that was presumably written by a shell script, i.e. with an\n>      ++ * end-of-line marker that needs to be stripped.\n>       + *\n>       + * Note that only the last end-of-line marker is stripped, consistent with the\n>       + * behavior of \"$(cat path)\" in a shell script.\n>   9:  689f34a2a5 !  8:  0cc279fc14 rebase: use read_oneliner()\n>      @@ Commit message\n>       \n>           Since in sequencer.c, read_one() basically duplicates the functionality\n>           of read_oneliner(), reduce code duplication by replacing read_one() with\n>      -    read_oneliner(). Also, delete strbuf_reset() calls prior to\n>      -    read_oneliner() as read_oneliner() already resets the strbuf.\n>      +    read_oneliner().\n>       \n>      -    This was mostly done with the following Coccinelle script\n>      +    This was done with the following Coccinelle script\n>       \n>                   @@\n>                   expression a, b;\n>      @@ Commit message\n>                   - read_one(a, b)\n>                   + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)\n>       \n>      -    Long lines were broken up and strbuf_reset()s were deleted manually.\n>      +    and long lines were manually broken up.\n>       \n>        ## builtin/rebase.c ##\n>       @@ builtin/rebase.c: static const char *state_dir_path(const char *filename, struct rebase_options *o\n>      @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n>        \topts->head_name = starts_with(head_name.buf, \"refs/\") ?\n>        \t\txstrdup(head_name.buf) : NULL;\n>       @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n>      - \t * head. Fall back to reading from head to cover for the case that the\n>      - \t * user upgraded git with an ongoing interactive rebase.\n>        \t */\n>      --\tstrbuf_reset(&buf);\n>      + \tstrbuf_reset(&buf);\n>        \tif (file_exists(state_dir_path(\"orig-head\", opts))) {\n>       -\t\tif (read_one(state_dir_path(\"orig-head\", opts), &buf))\n>       +\t\tif (!read_oneliner(&buf, state_dir_path(\"orig-head\", opts),\n>      @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n>        \tif (get_oid(buf.buf, &opts->orig_head))\n>        \t\treturn error(_(\"invalid orig-head: '%s'\"), buf.buf);\n>       @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n>      - \t}\n>        \n>        \tif (file_exists(state_dir_path(\"allow_rerere_autoupdate\", opts))) {\n>      --\t\tstrbuf_reset(&buf);\n>      + \t\tstrbuf_reset(&buf);\n>       -\t\tif (read_one(state_dir_path(\"allow_rerere_autoupdate\", opts),\n>       -\t\t\t    &buf))\n>       +\t\tif (!read_oneliner(&buf, state_dir_path(\"allow_rerere_autoupdate\", opts),\n>      @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n>        \t\tif (!strcmp(buf.buf, \"--rerere-autoupdate\"))\n>        \t\t\topts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;\n>       @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n>      - \t}\n>        \n>        \tif (file_exists(state_dir_path(\"gpg_sign_opt\", opts))) {\n>      --\t\tstrbuf_reset(&buf);\n>      + \t\tstrbuf_reset(&buf);\n>       -\t\tif (read_one(state_dir_path(\"gpg_sign_opt\", opts),\n>       -\t\t\t    &buf))\n>       +\t\tif (!read_oneliner(&buf, state_dir_path(\"gpg_sign_opt\", opts),\n>      @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n>        \t\t\treturn -1;\n>        \t\tfree(opts->gpg_sign_opt);\n>        \t\topts->gpg_sign_opt = xstrdup(buf.buf);\n>      - \t}\n>      +@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n>        \n>        \tif (file_exists(state_dir_path(\"strategy\", opts))) {\n>      --\t\tstrbuf_reset(&buf);\n>      + \t\tstrbuf_reset(&buf);\n>       -\t\tif (read_one(state_dir_path(\"strategy\", opts), &buf))\n>       +\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy\", opts),\n>       +\t\t\t\t   READ_ONELINER_WARN_MISSING))\n>        \t\t\treturn -1;\n>        \t\tfree(opts->strategy);\n>        \t\topts->strategy = xstrdup(buf.buf);\n>      - \t}\n>      +@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)\n>        \n>        \tif (file_exists(state_dir_path(\"strategy_opts\", opts))) {\n>      --\t\tstrbuf_reset(&buf);\n>      + \t\tstrbuf_reset(&buf);\n>       -\t\tif (read_one(state_dir_path(\"strategy_opts\", opts), &buf))\n>       +\t\tif (!read_oneliner(&buf, state_dir_path(\"strategy_opts\", opts),\n>       +\t\t\t\t   READ_ONELINER_WARN_MISSING))\n\nSo up to here I think all the changes since v4 are due to the dropped \npatch bar the rewording of the message in patch 7 that drops the \nreference to file_exists()\n\nJunio pointed out a typo in the message to patch 9, apart from that it \nall looks good to me\n\n> 10:  e0b8e409af !  9:  3dbed2a832 sequencer: make apply_rebase() accept a path\n>      @@ Metadata\n>       Author: Denton Liu <liu.denton@gmail.com>\n>       \n>        ## Commit message ##\n>      -    sequencer: make apply_rebase() accept a path\n>      +    sequencer: make apply_autostash() accept a path\n>       \n>      -    In order to make apply_rebase() more generic for future extraction, make\n>      +    In order to make apply_autostash() more generic for future extraction, make\n\nThanks for fixing that\n\n>           it accept a `path` argument so that the location from where to read the\n>           reference to the autostash commit can be customized. Remove the `opts`\n>           argument since it was unused before anyway.\n> 11:  a52583beff = 10:  937b4964d1 sequencer: rename stash_sha1 to stash_oid\n> 12:  389b17df33 = 11:  98a7f5280c rebase: use apply_autostash() from sequencer.c\n> 13:  fe57e21dbc = 12:  67f3b5c225 rebase: generify reset_head()\n> 14:  045668620d = 13:  af5e808667 reset: extract reset_head() from rebase\n> 15:  d5f51cd80e = 14:  114d9a7655 rebase: extract create_autostash()\n> 16:  9ab10d23d4 = 15:  c2ed4cd39d rebase: generify create_autostash()\n> 17:  26cca49be6 = 16:  3be4a27dfe sequencer: extract perform_autostash() from rebase\n> 18:  e703022fda = 17:  129d5d97ae sequencer: unlink autostash in apply_autostash()\n> 19:  75dc3f10a1 ! 18:  7e04ce8d8e sequencer: implement save_autostash()\n>      @@ Commit message\n>           function will be used in a future commit.\n>       \n>           The difference between save_autostash() and apply_autostash() is that\n>      -    the latter does not try to apply the stash. It skips that step and\n>      +    the former does not try to apply the stash. It skips that step and\n>           just stores the created entry in the stash reflog.\n>       \n>      +    This is useful in the case where we abort an operation when an autostash\n>      +    is present but we don't want to dirty the worktree with the application\n>      +    of the stash. For example, in a future commit, we will implement\n>      +    `git merge --autostash`. Since merges can be aborted using\n>      +    `git reset --hard`, we'd make use of save_autostash() to save the\n>      +    autostash entry instead of applying it to the worktree thus keeping the\n>      +    worktree undirtied.\n\nThe reason for the change is much clearer now thanks\n\n>      +\n>        ## sequencer.c ##\n>       @@ sequencer.c: void create_autostash(struct repository *r, const char *path,\n>        \tstrbuf_release(&buf);\n> 20:  598ddea6c1 = 19:  732b3f9945 sequencer: implement apply_autostash_oid()\n> 21:  7adf794192 ! 20:  f9f698b79a merge: teach --autostash option\n>      @@ Commit message\n>           `git reset --hard`, the autostash is saved into the stash reflog\n>           instead keeping the worktree clean.\n>       \n>      +    Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>\n>           Suggested-by: Alban Gruin <alban.gruin@gmail.com>\n>       \n>        ## Documentation/config/merge.txt ##\n>      @@ Documentation/git-merge.txt: will be appended to the specified message.\n>        \tAbort the current conflict resolution process, and\n>       -\ttry to reconstruct the pre-merge state.\n>       +\ttry to reconstruct the pre-merge state. If an autostash entry is\n>      -+\tpresent, apply it back to the worktree.\n>      ++\tpresent, apply it to the worktree.\n>        +\n>        If there were uncommitted worktree changes present when the merge\n>        started, 'git merge --abort' will in some cases be unable to\n>      @@ Documentation/git-merge.txt: reconstruct these changes. It is therefore recommen\n>        +\n>        'git merge --abort' is equivalent to 'git reset --merge' when\n>       -`MERGE_HEAD` is present.\n>      -+`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present where\n>      -+the stash entry is applied to the worktree.\n>      ++`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present in\n>      ++which case 'git merge --abort' applies the stash entry to the worktree\n>      ++whereas 'git reset --merge' will save the stashed changes in the stash\n>      ++reflog.\n>        \n>        --quit::\n>        \tForget about the current merge in progress. Leave the index\n>      +-\tand the working tree as-is.\n>      ++\tand the working tree as-is. If `MERGE_AUTOSTASH` is present, the\n>      ++\tstash entry will be saved to the stash reflog.\n\nThanks for adding that\n\n>      +\n>      + --continue::\n>      + \tAfter a 'git merge' stops due to conflicts you can conclude the\n>       \n>        ## Documentation/merge-options.txt ##\n>       @@ Documentation/merge-options.txt: ifndef::git-pull[]\n>      @@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)\n>        \t\t\tdie(_(\"There is no merge to abort (MERGE_HEAD missing).\"));\n>        \n>       +\t\tif (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),\n>      -+\t\t\t   READ_ONELINER_SKIP_IF_EMPTY)) {\n>      -+\t\t\tstrbuf_trim(&stash_oid);\n\nMakes sense, we are in control of the file so don't need to trim the \ncontents\n\n>      ++\t\t    READ_ONELINER_SKIP_IF_EMPTY))\n>       +\t\t\tunlink(git_path_merge_autostash(the_repository));\n>      -+\t\t}\n>       +\n>        \t\t/* Invoke 'git reset --merge' */\n>        \t\tret = cmd_reset(nargc, nargv, prefix);\n>      @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix\n>        \t\t\t\tN_(\"add exec lines after each commit of the \"\n>        \t\t\t\t   \"editable list\")),\n>       \n>      - ## builtin/reset.c ##\n>      -@@\n>      - #include \"cache-tree.h\"\n>      - #include \"submodule.h\"\n>      - #include \"submodule-config.h\"\n>      -+#include \"sequencer.h\"\n>      -\n>      - #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)\n>      -\n>      -@@ builtin/reset.c: int cmd_reset(int argc, const char **argv, const char *prefix)\n>      - \t\tif (reset_type == HARD && !update_ref_status && !quiet)\n>      - \t\t\tprint_new_head_line(lookup_commit_reference(the_repository, &oid));\n>      - \t}\n>      --\tif (!pathspec.nr)\n>      -+\tif (!pathspec.nr) {\n>      - \t\tremove_branch_state(the_repository, 0);\n>      -+\t}\n>      -\n>      - \treturn update_ref_status;\n>      - }\n>      -\n>        ## parse-options.h ##\n>       @@ parse-options.h: int parse_opt_passthru_argv(const struct option *, const char *, int);\n>        #define OPT_CLEANUP(v) OPT_STRING(0, \"cleanup\", v, N_(\"mode\"), N_(\"how to strip spaces and #comments from message\"))\n>      @@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '\n>       +\tgit diff --exit-code\n>       +'\n>       +\n>      ++test_expect_success 'quit merge with --no-commit and --autostash' '\n>      ++\tgit reset --hard c1 &&\n>      ++\tgit merge-file file file.orig file.9 &&\n>      ++\tgit diff >expect &&\n>      ++\tgit merge --no-commit --autostash c2 &&\n>      ++\tgit stash show -p MERGE_AUTOSTASH >actual &&\n>      ++\ttest_cmp expect actual &&\n>      ++\tgit diff HEAD >expect &&\n>      ++\tgit merge --quit 2>err &&\n>      ++\ttest_i18ngrep \"Autostash exists; creating a new stash entry.\" err &&\n>      ++\tgit diff HEAD >actual &&\n>      ++\ttest_cmp expect actual\n>      ++'\n>      ++\n\nThanks for the new test\n\nLooking at the range-diff this series looks fine to me now apart from \nthe typo Junio pointed out.\n\nThanks\n\nPhillip\n\n>       +test_expect_success 'merge with conflicted --autostash changes' '\n>       +\tgit reset --hard c1 &&\n>       +\tgit merge-file file file.orig file.9y &&\n> 22:  f1e54622fb = 21:  13f3fadbef t5520: make test_pull_autostash() accept expect_parent_num\n> 23:  177c7e537b = 22:  f4fec1e780 pull: pass --autostash to merge\n> \n"},{"id":"395200","messageId":"xmqqsghbjohn.fsf@gitster.c.googlers.com","threadId":"52048","inReplyTo":"eaf864e6-8441-c5a6-3c15-b71c522ec43d@gmail.com","subject":"Re: [PATCH v5 00/22] merge: learn --autostash","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-04-10T16:26:44Z","receivedAt":"2020-04-10T16:26:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n> Looking at the range-diff this series looks fine to me now apart from\n> the typo Junio pointed out.\n\nIt is quicker for me to do s/NON_EXISTENCE/MISSING/ locally if there\nis nothing else.  Thanks for giving the series another round of\nlook-over.\n\n\n"}]}