git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / mirror / code / Atom feed
* [RFC PATCH 0/7] merge: learn --autostash
@ 2019-10-16 17:26 Denton Liu
  2019-10-16 17:26 ` [RFC PATCH 1/7] Makefile: alphabetically sort += lists Denton Liu
                   ` (7 more replies)
  0 siblings, 8 replies; 152+ messages in thread
From: Denton Liu @ 2019-10-16 17:26 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

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

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

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

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

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

 Makefile         |  75 +++++++--------
 autostash.c      | 239 ++++++++++++++++++++++++++++++++++++++++++++++
 autostash.h      |  24 +++++
 builtin/merge.c  |  13 +++
 builtin/pull.c   |   9 +-
 builtin/rebase.c | 240 +----------------------------------------------
 t/t5520-pull.sh  |   8 --
 7 files changed, 323 insertions(+), 285 deletions(-)
 create mode 100644 autostash.c
 create mode 100644 autostash.h

-- 
2.23.0.897.g0a19638b1e


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [RFC PATCH 1/7] Makefile: alphabetically sort += lists
  2019-10-16 17:26 [RFC PATCH 0/7] merge: learn --autostash Denton Liu
@ 2019-10-16 17:26 ` Denton Liu
  2019-10-17 18:07   ` Junio C Hamano
  2019-10-16 17:26 ` [RFC PATCH 2/7] autostash: extract read_one() from rebase Denton Liu
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2019-10-16 17:26 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Makefile | 75 ++++++++++++++++++++++++++++----------------------------
 1 file changed, 38 insertions(+), 37 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [RFC PATCH 2/7] autostash: extract read_one() from rebase
  2019-10-16 17:26 [RFC PATCH 0/7] merge: learn --autostash Denton Liu
  2019-10-16 17:26 ` [RFC PATCH 1/7] Makefile: alphabetically sort += lists Denton Liu
@ 2019-10-16 17:26 ` Denton Liu
  2019-10-18  9:04   ` Phillip Wood
  2019-10-16 17:26 ` [RFC PATCH 3/7] autostash: extract apply_autostash() " Denton Liu
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2019-10-16 17:26 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 autostash.c      | 12 ++++++++++++
 autostash.h      |  9 +++++++++
 builtin/rebase.c | 10 +---------
 3 files changed, 22 insertions(+), 9 deletions(-)
 create mode 100644 autostash.c
 create mode 100644 autostash.h

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [RFC PATCH 3/7] autostash: extract apply_autostash() from rebase
  2019-10-16 17:26 [RFC PATCH 0/7] merge: learn --autostash Denton Liu
  2019-10-16 17:26 ` [RFC PATCH 1/7] Makefile: alphabetically sort += lists Denton Liu
  2019-10-16 17:26 ` [RFC PATCH 2/7] autostash: extract read_one() from rebase Denton Liu
@ 2019-10-16 17:26 ` Denton Liu
  2019-10-16 17:26 ` [RFC PATCH 4/7] autostash: extract reset_head() " Denton Liu
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-10-16 17:26 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 autostash.c      | 46 +++++++++++++++++++++++++++++++++++++++++++++
 autostash.h      |  2 ++
 builtin/rebase.c | 49 ++----------------------------------------------
 3 files changed, 50 insertions(+), 47 deletions(-)

diff --git a/autostash.c b/autostash.c
index a6898e0fda..62ec7a7c80 100644
--- a/autostash.c
+++ b/autostash.c
@@ -1,6 +1,8 @@
 #include "git-compat-util.h"
 #include "autostash.h"
+#include "dir.h"
 #include "gettext.h"
+#include "run-command.h"
 #include "strbuf.h"
 
 int read_one(const char *path, struct strbuf *buf)
@@ -10,3 +12,47 @@ int read_one(const char *path, struct strbuf *buf)
 	strbuf_trim_trailing_newline(buf);
 	return 0;
 }
+
+int apply_autostash(const char *path)
+{
+	struct strbuf autostash = STRBUF_INIT;
+	struct child_process stash_apply = CHILD_PROCESS_INIT;
+
+	if (!file_exists(path))
+		return 0;
+
+	if (read_one(path, &autostash))
+		return error(_("Could not read '%s'"), path);
+	/* Ensure that the hash is not mistaken for a number */
+	strbuf_addstr(&autostash, "^0");
+	argv_array_pushl(&stash_apply.args,
+			 "stash", "apply", autostash.buf, NULL);
+	stash_apply.git_cmd = 1;
+	stash_apply.no_stderr = stash_apply.no_stdout =
+		stash_apply.no_stdin = 1;
+	if (!run_command(&stash_apply))
+		printf(_("Applied autostash.\n"));
+	else {
+		struct argv_array args = ARGV_ARRAY_INIT;
+		int res = 0;
+
+		argv_array_pushl(&args,
+				 "stash", "store", "-m", "autostash", "-q",
+				 autostash.buf, NULL);
+		if (run_command_v_opt(args.argv, RUN_GIT_CMD))
+			res = error(_("Cannot store %s"), autostash.buf);
+		argv_array_clear(&args);
+		strbuf_release(&autostash);
+		if (res)
+			return res;
+
+		fprintf(stderr,
+			_("Applying autostash resulted in conflicts.\n"
+			  "Your changes are safe in the stash.\n"
+			  "You can run \"git stash pop\" or \"git stash drop\" "
+			  "at any time.\n"));
+	}
+
+	strbuf_release(&autostash);
+	return 0;
+}
diff --git a/autostash.h b/autostash.h
index 4a8f504f12..5f4e4bd22c 100644
--- a/autostash.h
+++ b/autostash.h
@@ -6,4 +6,6 @@
 /* Read one file, then strip line endings */
 int read_one(const char *path, struct strbuf *buf);
 
+int apply_autostash(const char *path);
+
 #endif
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 9fd7de6b2f..661928d427 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -682,51 +682,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)
 	return 0;
 }
 
-static int apply_autostash(struct rebase_options *opts)
-{
-	const char *path = state_dir_path("autostash", opts);
-	struct strbuf autostash = STRBUF_INIT;
-	struct child_process stash_apply = CHILD_PROCESS_INIT;
-
-	if (!file_exists(path))
-		return 0;
-
-	if (read_one(path, &autostash))
-		return error(_("Could not read '%s'"), path);
-	/* Ensure that the hash is not mistaken for a number */
-	strbuf_addstr(&autostash, "^0");
-	argv_array_pushl(&stash_apply.args,
-			 "stash", "apply", autostash.buf, NULL);
-	stash_apply.git_cmd = 1;
-	stash_apply.no_stderr = stash_apply.no_stdout =
-		stash_apply.no_stdin = 1;
-	if (!run_command(&stash_apply))
-		printf(_("Applied autostash.\n"));
-	else {
-		struct argv_array args = ARGV_ARRAY_INIT;
-		int res = 0;
-
-		argv_array_pushl(&args,
-				 "stash", "store", "-m", "autostash", "-q",
-				 autostash.buf, NULL);
-		if (run_command_v_opt(args.argv, RUN_GIT_CMD))
-			res = error(_("Cannot store %s"), autostash.buf);
-		argv_array_clear(&args);
-		strbuf_release(&autostash);
-		if (res)
-			return res;
-
-		fprintf(stderr,
-			_("Applying autostash resulted in conflicts.\n"
-			  "Your changes are safe in the stash.\n"
-			  "You can run \"git stash pop\" or \"git stash drop\" "
-			  "at any time.\n"));
-	}
-
-	strbuf_release(&autostash);
-	return 0;
-}
-
 static int finish_rebase(struct rebase_options *opts)
 {
 	struct strbuf dir = STRBUF_INIT;
@@ -734,7 +689,7 @@ static int finish_rebase(struct rebase_options *opts)
 	int ret = 0;
 
 	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
-	apply_autostash(opts);
+	apply_autostash(state_dir_path("autostash", opts));
 	close_object_store(the_repository->objects);
 	/*
 	 * We ignore errors in 'gc --auto', since the
@@ -1181,7 +1136,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
 	} else if (status == 2) {
 		struct strbuf dir = STRBUF_INIT;
 
-		apply_autostash(opts);
+		apply_autostash(state_dir_path("autostash", opts));
 		strbuf_addstr(&dir, opts->state_dir);
 		remove_dir_recursively(&dir, 0);
 		strbuf_release(&dir);
-- 
2.23.0.897.g0a19638b1e


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [RFC PATCH 4/7] autostash: extract reset_head() from rebase
  2019-10-16 17:26 [RFC PATCH 0/7] merge: learn --autostash Denton Liu
                   ` (2 preceding siblings ...)
  2019-10-16 17:26 ` [RFC PATCH 3/7] autostash: extract apply_autostash() " Denton Liu
@ 2019-10-16 17:26 ` Denton Liu
  2019-10-18  9:37   ` Phillip Wood
  2019-10-16 17:26 ` [RFC PATCH 5/7] autostash: extract perform_autostash() " Denton Liu
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2019-10-16 17:26 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 autostash.c      | 137 +++++++++++++++++++++++++++++++++++++++++++++++
 autostash.h      |  12 +++++
 builtin/rebase.c | 137 -----------------------------------------------
 3 files changed, 149 insertions(+), 137 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [RFC PATCH 5/7] autostash: extract perform_autostash() from rebase
  2019-10-16 17:26 [RFC PATCH 0/7] merge: learn --autostash Denton Liu
                   ` (3 preceding siblings ...)
  2019-10-16 17:26 ` [RFC PATCH 4/7] autostash: extract reset_head() " Denton Liu
@ 2019-10-16 17:26 ` Denton Liu
  2019-10-21 19:00   ` Johannes Schindelin
  2019-10-16 17:26 ` [RFC PATCH 6/7] autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS Denton Liu
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2019-10-16 17:26 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 autostash.c      | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 autostash.h      |  1 +
 builtin/rebase.c | 44 +-------------------------------------------
 3 files changed, 48 insertions(+), 43 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [RFC PATCH 6/7] autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS
  2019-10-16 17:26 [RFC PATCH 0/7] merge: learn --autostash Denton Liu
                   ` (4 preceding siblings ...)
  2019-10-16 17:26 ` [RFC PATCH 5/7] autostash: extract perform_autostash() " Denton Liu
@ 2019-10-16 17:26 ` Denton Liu
  2019-10-18  9:41   ` Phillip Wood
  2019-10-16 17:26 ` [RFC PATCH 7/7] merge: teach --autostash option Denton Liu
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
  7 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2019-10-16 17:26 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 autostash.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [RFC PATCH 7/7] merge: teach --autostash option
  2019-10-16 17:26 [RFC PATCH 0/7] merge: learn --autostash Denton Liu
                   ` (5 preceding siblings ...)
  2019-10-16 17:26 ` [RFC PATCH 6/7] autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS Denton Liu
@ 2019-10-16 17:26 ` Denton Liu
  2019-10-18  9:46   ` Phillip Wood
  2019-10-21 19:10   ` Johannes Schindelin
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
  7 siblings, 2 replies; 152+ messages in thread
From: Denton Liu @ 2019-10-16 17:26 UTC (permalink / raw)
  To: Git Mailing List; +Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

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

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

Reported-by: Alban Gruin <alban.gruin@gmail.com>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/merge.c | 13 +++++++++++++
 builtin/pull.c  |  9 +++++----
 t/t5520-pull.sh |  8 --------
 3 files changed, 18 insertions(+), 12 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
  2019-10-16 17:26 ` [RFC PATCH 1/7] Makefile: alphabetically sort += lists Denton Liu
@ 2019-10-17 18:07   ` Junio C Hamano
  2019-10-21 18:44     ` Johannes Schindelin
  0 siblings, 1 reply; 152+ messages in thread
From: Junio C Hamano @ 2019-10-17 18:07 UTC (permalink / raw)
  To: Denton Liu; +Cc: Git Mailing List, Alban Gruin, Johannes Schindelin

Denton Liu <liu.denton@gmail.com> writes:

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

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

>  LIB_OBJS += combine-diff.o
> -LIB_OBJS += commit.o
>  LIB_OBJS += commit-graph.o
>  LIB_OBJS += commit-reach.o
> +LIB_OBJS += commit.o

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

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

Thanks.

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 2/7] autostash: extract read_one() from rebase
  2019-10-16 17:26 ` [RFC PATCH 2/7] autostash: extract read_one() from rebase Denton Liu
@ 2019-10-18  9:04   ` Phillip Wood
  2019-10-21 18:46     ` Johannes Schindelin
  0 siblings, 1 reply; 152+ messages in thread
From: Phillip Wood @ 2019-10-18  9:04 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 16/10/2019 18:26, Denton Liu wrote:
> Begin the process of lib-ifying the autostash code. In a future commit,
> this will be used to implement `--autostash` in other builtins.
> 
> This patch is best viewed with `--color-moved` and
> `--color-moved-ws=allow-indentation-change`.
> 
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>   autostash.c      | 12 ++++++++++++
>   autostash.h      |  9 +++++++++
>   builtin/rebase.c | 10 +---------
>   3 files changed, 22 insertions(+), 9 deletions(-)
>   create mode 100644 autostash.c
>   create mode 100644 autostash.h
> 
> diff --git a/autostash.c b/autostash.c
> new file mode 100644
> index 0000000000..a6898e0fda
> --- /dev/null
> +++ b/autostash.c
> @@ -0,0 +1,12 @@
> +#include "git-compat-util.h"
> +#include "autostash.h"
> +#include "gettext.h"
> +#include "strbuf.h"
> +
> +int read_one(const char *path, struct strbuf *buf)
> +{
> +	if (strbuf_read_file(buf, path, 0) < 0)
> +		return error_errno(_("could not read '%s'"), path);
> +	strbuf_trim_trailing_newline(buf);
> +	return 0;
> +}

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

Best Wishes

Phillip


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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 4/7] autostash: extract reset_head() from rebase
  2019-10-16 17:26 ` [RFC PATCH 4/7] autostash: extract reset_head() " Denton Liu
@ 2019-10-18  9:37   ` Phillip Wood
  2019-10-21 18:56     ` Johannes Schindelin
  0 siblings, 1 reply; 152+ messages in thread
From: Phillip Wood @ 2019-10-18  9:37 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

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

On 16/10/2019 18:26, Denton Liu wrote:
> Begin the process of lib-ifying the autostash code. In a future commit,
> 
> This patch is best viewed with `--color-moved` and
> `--color-moved-ws=allow-indentation-change`.
> this will be used to implement `--autostash` in other builtins.
> 
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>   autostash.c      | 137 +++++++++++++++++++++++++++++++++++++++++++++++
>   autostash.h      |  12 +++++
>   builtin/rebase.c | 137 -----------------------------------------------
>   3 files changed, 149 insertions(+), 137 deletions(-)
> 
> diff --git a/autostash.c b/autostash.c
> index 62ec7a7c80..eb58e0c8a4 100644
> --- a/autostash.c
> +++ b/autostash.c
> @@ -1,9 +1,17 @@
> +#define USE_THE_INDEX_COMPATIBILITY_MACROS

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

Best Wishes

Phillip

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 6/7] autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS
  2019-10-16 17:26 ` [RFC PATCH 6/7] autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS Denton Liu
@ 2019-10-18  9:41   ` Phillip Wood
  0 siblings, 0 replies; 152+ messages in thread
From: Phillip Wood @ 2019-10-18  9:41 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 16/10/2019 18:26, Denton Liu wrote:
> Since autostash.c was recently introduced, we should avoid
> USE_THE_INDEX_COMPATIBILITY_MACROS since we are trying to move away from
> this in the rest of the codebase. Rewrite the autostash code to not need
> it and remove its definition.
> 
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>   autostash.c | 8 +++-----
>   1 file changed, 3 insertions(+), 5 deletions(-)
> 
> diff --git a/autostash.c b/autostash.c
> index 722cf78b12..0a1f00d2e5 100644
> --- a/autostash.c
> +++ b/autostash.c
> @@ -1,5 +1,3 @@
> -#define USE_THE_INDEX_COMPATIBILITY_MACROS
> -
>   #include "git-compat-util.h"
>   #include "autostash.h"
>   #include "cache-tree.h"
> @@ -46,7 +44,7 @@ int reset_head(struct object_id *oid, const char *action,
>   	if (switch_to_branch && !starts_with(switch_to_branch, "refs/"))
>   		BUG("Not a fully qualified branch: '%s'", switch_to_branch);
>   
> -	if (!refs_only && hold_locked_index(&lock, LOCK_REPORT_ON_ERROR) < 0) {
> +	if (!refs_only && repo_hold_locked_index(the_repository, &lock, LOCK_REPORT_ON_ERROR) < 0) {

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

Best Wishes

Phillip
>   		ret = -1;
>   		goto leave_reset_head;
>   	}
> @@ -157,8 +155,8 @@ void perform_autostash(const char *path)
>   	struct lock_file lock_file = LOCK_INIT;
>   	int fd;
>   
> -	fd = hold_locked_index(&lock_file, 0);
> -	refresh_cache(REFRESH_QUIET);
> +	fd = repo_hold_locked_index(the_repository, &lock_file, 0);
> +	refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
>   	if (0 <= fd)
>   		repo_update_index_if_able(the_repository, &lock_file);
>   	rollback_lock_file(&lock_file);
> 

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 7/7] merge: teach --autostash option
  2019-10-16 17:26 ` [RFC PATCH 7/7] merge: teach --autostash option Denton Liu
@ 2019-10-18  9:46   ` Phillip Wood
  2019-12-24  9:58     ` Denton Liu
  2019-10-21 19:10   ` Johannes Schindelin
  1 sibling, 1 reply; 152+ messages in thread
From: Phillip Wood @ 2019-10-18  9:46 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 16/10/2019 18:26, Denton Liu wrote:
> In rebase, one can pass the `--autostash` option to cause the worktree
> to be automatically stashed before continuing with the rebase. This
> option is missing in merge, however.

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

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

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

     automatically stash/stash pop before and after pulling

Best Wishes

Phillip

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
  2019-10-17 18:07   ` Junio C Hamano
@ 2019-10-21 18:44     ` Johannes Schindelin
  2019-10-21 18:53       ` Denton Liu
  2019-10-21 19:49       ` Junio C Hamano
  0 siblings, 2 replies; 152+ messages in thread
From: Johannes Schindelin @ 2019-10-21 18:44 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Denton Liu, Git Mailing List, Alban Gruin

Hi Junio,

On Fri, 18 Oct 2019, Junio C Hamano wrote:

> Denton Liu <liu.denton@gmail.com> writes:
>
> > There are many += lists in the Makefile and, over time, they have gotten
> > slightly out of order, alphabetically. Alphabetically sort all += lists
> > to bring them back in order.
> > ...
>
> Hmm.  I like the general thrust, but ...
>
> >  LIB_OBJS += combine-diff.o
> > -LIB_OBJS += commit.o
> >  LIB_OBJS += commit-graph.o
> >  LIB_OBJS += commit-reach.o
> > +LIB_OBJS += commit.o
>
> ... I do not particularly see this change (there may be similar
> ones) desirable.  I'd find it it be much more natural to sort
> "commit-anything" after "commit", and that is true with or without
> the common extension ".o" added to these entries.
>
> In short, flipping these entries because '.' sorts later than '-' is
> making the result look "less sorted", at least to me.

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

So Denton's patch does the correct thing.

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 2/7] autostash: extract read_one() from rebase
  2019-10-18  9:04   ` Phillip Wood
@ 2019-10-21 18:46     ` Johannes Schindelin
  0 siblings, 0 replies; 152+ messages in thread
From: Johannes Schindelin @ 2019-10-21 18:46 UTC (permalink / raw)
  To: phillip.wood; +Cc: Denton Liu, Git Mailing List, Alban Gruin, Junio C Hamano

Hi,

On Fri, 18 Oct 2019, Phillip Wood wrote:

> Hi Denton
>
> On 16/10/2019 18:26, Denton Liu wrote:
> > Begin the process of lib-ifying the autostash code. In a future commit,
> > this will be used to implement `--autostash` in other builtins.
> >
> > This patch is best viewed with `--color-moved` and
> > `--color-moved-ws=allow-indentation-change`.
> >
> > Signed-off-by: Denton Liu <liu.denton@gmail.com>
> > ---
> >   autostash.c      | 12 ++++++++++++
> >   autostash.h      |  9 +++++++++
> >   builtin/rebase.c | 10 +---------
> >   3 files changed, 22 insertions(+), 9 deletions(-)
> >   create mode 100644 autostash.c
> >   create mode 100644 autostash.h
> >
> > diff --git a/autostash.c b/autostash.c
> > new file mode 100644
> > index 0000000000..a6898e0fda
> > --- /dev/null
> > +++ b/autostash.c
> > @@ -0,0 +1,12 @@
> > +#include "git-compat-util.h"
> > +#include "autostash.h"
> > +#include "gettext.h"
> > +#include "strbuf.h"
> > +
> > +int read_one(const char *path, struct strbuf *buf)
> > +{
> > +	if (strbuf_read_file(buf, path, 0) < 0)
> > +		return error_errno(_("could not read '%s'"), path);
> > +	strbuf_trim_trailing_newline(buf);
> > +	return 0;
> > +}
>
> This looks like it's doing a similar job to read_oneliner() in sequencer.c, is
> it possible to make that public and use it instead? (There may be a difference
> if the file is missing but that function already takes a flag so it could
> probably be modified easily enough.)

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

Thanks,
Dscho

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
  2019-10-21 18:44     ` Johannes Schindelin
@ 2019-10-21 18:53       ` Denton Liu
  2019-10-22 23:33         ` Johannes Schindelin
  2019-10-21 19:49       ` Junio C Hamano
  1 sibling, 1 reply; 152+ messages in thread
From: Denton Liu @ 2019-10-21 18:53 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Junio C Hamano, Git Mailing List, Alban Gruin

Hi Johannes,

On Mon, Oct 21, 2019 at 08:44:40PM +0200, Johannes Schindelin wrote:
> Hi Junio,
> 
> On Fri, 18 Oct 2019, Junio C Hamano wrote:
> 
> > Denton Liu <liu.denton@gmail.com> writes:
> >
> > > There are many += lists in the Makefile and, over time, they have gotten
> > > slightly out of order, alphabetically. Alphabetically sort all += lists
> > > to bring them back in order.
> > > ...
> >
> > Hmm.  I like the general thrust, but ...
> >
> > >  LIB_OBJS += combine-diff.o
> > > -LIB_OBJS += commit.o
> > >  LIB_OBJS += commit-graph.o
> > >  LIB_OBJS += commit-reach.o
> > > +LIB_OBJS += commit.o
> >
> > ... I do not particularly see this change (there may be similar
> > ones) desirable.  I'd find it it be much more natural to sort
> > "commit-anything" after "commit", and that is true with or without
> > the common extension ".o" added to these entries.
> >
> > In short, flipping these entries because '.' sorts later than '-' is
> > making the result look "less sorted", at least to me.
> 
> The problem with this argument is that it disagrees with ASCII, as `-`
> has code 0x2d while `.` has code 0x2e, i.e. it is lexicographically
> _larger_.
> 
> So Denton's patch does the correct thing.

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

Thanks,

Denton

> 
> Ciao,
> Dscho

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 4/7] autostash: extract reset_head() from rebase
  2019-10-18  9:37   ` Phillip Wood
@ 2019-10-21 18:56     ` Johannes Schindelin
  0 siblings, 0 replies; 152+ messages in thread
From: Johannes Schindelin @ 2019-10-21 18:56 UTC (permalink / raw)
  To: phillip.wood; +Cc: Denton Liu, Git Mailing List, Alban Gruin, Junio C Hamano

Hi,

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


On Fri, 18 Oct 2019, Phillip Wood wrote:

> Hi Denton
>
> It's great to see this being libified, I've had it in mind to do this so we
> can avoid forking 'git checkout' in sequencer.c
>
> On 16/10/2019 18:26, Denton Liu wrote:
> > Begin the process of lib-ifying the autostash code. In a future commit,
> >
> > This patch is best viewed with `--color-moved` and
> > `--color-moved-ws=allow-indentation-change`.
> > this will be used to implement `--autostash` in other builtins.
> >
> > Signed-off-by: Denton Liu <liu.denton@gmail.com>
> > ---
> >   autostash.c      | 137 +++++++++++++++++++++++++++++++++++++++++++++++
> >   autostash.h      |  12 +++++
> >   builtin/rebase.c | 137 -----------------------------------------------
> >   3 files changed, 149 insertions(+), 137 deletions(-)
> >
> > diff --git a/autostash.c b/autostash.c
> > index 62ec7a7c80..eb58e0c8a4 100644
> > --- a/autostash.c
> > +++ b/autostash.c
> > @@ -1,9 +1,17 @@
> > +#define USE_THE_INDEX_COMPATIBILITY_MACROS
>
> It might be nicer to have a preparatory step that fixes this by adding a
> 'struct repository *r' argument to the function in builtin/rebase.c before
> moving the function. You could also do the same for next patch and then move
> both functions together.

In addition to that, I think that `reset_head()`

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

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

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

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

Thanks,
Dscho


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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 5/7] autostash: extract perform_autostash() from rebase
  2019-10-16 17:26 ` [RFC PATCH 5/7] autostash: extract perform_autostash() " Denton Liu
@ 2019-10-21 19:00   ` Johannes Schindelin
  0 siblings, 0 replies; 152+ messages in thread
From: Johannes Schindelin @ 2019-10-21 19:00 UTC (permalink / raw)
  To: Denton Liu; +Cc: Git Mailing List, Alban Gruin, Junio C Hamano

Hi Denton,

On Wed, 16 Oct 2019, Denton Liu wrote:

> Continue the process of lib-ifying the autostash code. In a future
> commit, this will be used to implement `--autostash` in other builtins.
>
> This patch is best viewed with `--color-moved` and
> `--color-moved-ws=allow-indentation-change`.
>
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>  autostash.c      | 46 ++++++++++++++++++++++++++++++++++++++++++++++
>  autostash.h      |  1 +
>  builtin/rebase.c | 44 +-------------------------------------------
>  3 files changed, 48 insertions(+), 43 deletions(-)
>
> diff --git a/autostash.c b/autostash.c
> index eb58e0c8a4..722cf78b12 100644
> --- a/autostash.c
> +++ b/autostash.c
> @@ -12,6 +12,7 @@
>  #include "tree-walk.h"
>  #include "tree.h"
>  #include "unpack-trees.h"
> +#include "wt-status.h"
>
>  int read_one(const char *path, struct strbuf *buf)
>  {
> @@ -150,6 +151,51 @@ int reset_head(struct object_id *oid, const char *action,
>  	return ret;
>  }
>
> +void perform_autostash(const char *path)

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

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

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

Ciao,
Dscho

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 7/7] merge: teach --autostash option
  2019-10-16 17:26 ` [RFC PATCH 7/7] merge: teach --autostash option Denton Liu
  2019-10-18  9:46   ` Phillip Wood
@ 2019-10-21 19:10   ` Johannes Schindelin
  2019-12-24 10:05     ` Denton Liu
  1 sibling, 1 reply; 152+ messages in thread
From: Johannes Schindelin @ 2019-10-21 19:10 UTC (permalink / raw)
  To: Denton Liu; +Cc: Git Mailing List, Alban Gruin, Junio C Hamano

Hi Denton,

On Wed, 16 Oct 2019, Denton Liu wrote:

> In rebase, one can pass the `--autostash` option to cause the worktree
> to be automatically stashed before continuing with the rebase. This
> option is missing in merge, however.
>
> Implement the `--autostash` option and corresponding `merge.autoStash`
> option in merge which stashes before merging and then pops after.
>
> Reported-by: Alban Gruin <alban.gruin@gmail.com>

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

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

Should this not be guarded by `if (autostash)`?

>  	if (squash) {
>  		squash_message(head_commit, remoteheads);
>  	} else {
> @@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
>  	} else if (!strcmp(k, "commit.gpgsign")) {
>  		sign_commit = git_config_bool(k, v) ? "" : NULL;
>  		return 0;
> +	} else if (!strcmp(k, "merge.autostash")) {
> +		autostash = git_config_bool(k, v);
> +		return 0;
>  	}
>
>  	status = fmt_merge_msg_config(k, v, cb);
> @@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
>  		for (j = common; j; j = j->next)
>  			commit_list_insert(j->item, &reversed);
>
> +		if (autostash)
> +			perform_autostash(merge_autostash());
>  		hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
>  		clean = merge_recursive(&o, head,
>  				remoteheads->item, reversed, &result);
> @@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
>
>  		/* Invoke 'git reset --merge' */
>  		ret = cmd_reset(nargc, nargv, prefix);
> +		apply_autostash(merge_autostash());

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

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

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

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

Or shorter:

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

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

>  	if (opt_allow_unrelated_histories > 0)
>  		argv_array_push(&args, "--allow-unrelated-histories");
>
> @@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
>  	if (get_oid("HEAD", &orig_head))
>  		oidclr(&orig_head);
>
> -	if (!opt_rebase && opt_autostash != -1)
> -		die(_("--[no-]autostash option is only valid with --rebase."));
> -
>  	autostash = config_autostash;
>  	if (opt_rebase) {
>  		if (opt_autostash != -1)
> diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
> index cf4cc32fd0..75f162495a 100755
> --- a/t/t5520-pull.sh
> +++ b/t/t5520-pull.sh
> @@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
>  	test_pull_autostash_fail --rebase --no-autostash
>  '
>
> -for i in --autostash --no-autostash
> -do
> -	test_expect_success "pull $i (without --rebase) is illegal" '
> -		test_must_fail git pull $i . copy 2>err &&
> -		test_i18ngrep "only valid with --rebase" err
> -	'
> -done
> -

Nice!
Dscho

>  test_expect_success 'pull.rebase' '
>  	git reset --hard before-rebase &&
>  	test_config pull.rebase true &&
> --
> 2.23.0.897.g0a19638b1e
>
>

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
  2019-10-21 18:44     ` Johannes Schindelin
  2019-10-21 18:53       ` Denton Liu
@ 2019-10-21 19:49       ` Junio C Hamano
  2019-10-21 19:54         ` Jeff King
  1 sibling, 1 reply; 152+ messages in thread
From: Junio C Hamano @ 2019-10-21 19:49 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Denton Liu, Git Mailing List, Alban Gruin

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> ... I do not particularly see this change (there may be similar
>> ones) desirable.  I'd find it it be much more natural to sort
>> "commit-anything" after "commit", and that is true with or without
>> the common extension ".o" added to these entries.
>>
>> In short, flipping these entries because '.' sorts later than '-' is
>> making the result look "less sorted", at least to me.
>
> The problem with this argument is that it disagrees with ASCII, as `-`
> has code 0x2d while `.` has code 0x2e, i.e. it is lexicographically
> _larger_.

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

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

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

That does not make the patch right ;-)


^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
  2019-10-21 19:49       ` Junio C Hamano
@ 2019-10-21 19:54         ` Jeff King
  2019-10-22 11:34           ` Junio C Hamano
  0 siblings, 1 reply; 152+ messages in thread
From: Jeff King @ 2019-10-21 19:54 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, Denton Liu, Git Mailing List, Alban Gruin

On Tue, Oct 22, 2019 at 04:49:19AM +0900, Junio C Hamano wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> ... I do not particularly see this change (there may be similar
> >> ones) desirable.  I'd find it it be much more natural to sort
> >> "commit-anything" after "commit", and that is true with or without
> >> the common extension ".o" added to these entries.
> >>
> >> In short, flipping these entries because '.' sorts later than '-' is
> >> making the result look "less sorted", at least to me.
> >
> > The problem with this argument is that it disagrees with ASCII, as `-`
> > has code 0x2d while `.` has code 0x2e, i.e. it is lexicographically
> > _larger_.
> 
> I am saying that sorting these in ASCII order did not produce result
> that is easy to the eyes.
> 
> You are saying that Denton's patch sorted these lines in ASCII order.
> 
> I agree with you that it did correctly sort them in ASCII order.
> 
> That does not make the patch right ;-)

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

-Peff

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
  2019-10-21 19:54         ` Jeff King
@ 2019-10-22 11:34           ` Junio C Hamano
  0 siblings, 0 replies; 152+ messages in thread
From: Junio C Hamano @ 2019-10-22 11:34 UTC (permalink / raw)
  To: Jeff King; +Cc: Johannes Schindelin, Denton Liu, Git Mailing List, Alban Gruin

Jeff King <peff@peff.net> writes:

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

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

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
  2019-10-21 18:53       ` Denton Liu
@ 2019-10-22 23:33         ` Johannes Schindelin
  0 siblings, 0 replies; 152+ messages in thread
From: Johannes Schindelin @ 2019-10-22 23:33 UTC (permalink / raw)
  To: Denton Liu; +Cc: Junio C Hamano, Git Mailing List, Alban Gruin

Hi,

On Mon, 21 Oct 2019, Denton Liu wrote:

> Hi Johannes,
>
> On Mon, Oct 21, 2019 at 08:44:40PM +0200, Johannes Schindelin wrote:
> > Hi Junio,
> >
> > On Fri, 18 Oct 2019, Junio C Hamano wrote:
> >
> > > Denton Liu <liu.denton@gmail.com> writes:
> > >
> > > > There are many += lists in the Makefile and, over time, they have gotten
> > > > slightly out of order, alphabetically. Alphabetically sort all += lists
> > > > to bring them back in order.
> > > > ...
> > >
> > > Hmm.  I like the general thrust, but ...
> > >
> > > >  LIB_OBJS += combine-diff.o
> > > > -LIB_OBJS += commit.o
> > > >  LIB_OBJS += commit-graph.o
> > > >  LIB_OBJS += commit-reach.o
> > > > +LIB_OBJS += commit.o
> > >
> > > ... I do not particularly see this change (there may be similar
> > > ones) desirable.  I'd find it it be much more natural to sort
> > > "commit-anything" after "commit", and that is true with or without
> > > the common extension ".o" added to these entries.
> > >
> > > In short, flipping these entries because '.' sorts later than '-' is
> > > making the result look "less sorted", at least to me.
> >
> > The problem with this argument is that it disagrees with ASCII, as `-`
> > has code 0x2d while `.` has code 0x2e, i.e. it is lexicographically
> > _larger_.
> >
> > So Denton's patch does the correct thing.
>
> I actually agree with Junio on this one. Without the prefixes, "commit"
> would go before "commit-graph" so I think it would make more sense to
> order with the prefixes removed instead of taking the naive ordering by
> just sorting each block.

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

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

Ciao,
Dscho

>
> Thanks,
>
> Denton
>
> >
> > Ciao,
> > Dscho
>

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 7/7] merge: teach --autostash option
  2019-10-18  9:46   ` Phillip Wood
@ 2019-12-24  9:58     ` Denton Liu
  0 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24  9:58 UTC (permalink / raw)
  To: phillip.wood
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Phillip,

On Fri, Oct 18, 2019 at 10:46:37AM +0100, Phillip Wood wrote:
> Hi Denton
> 
> On 16/10/2019 18:26, Denton Liu wrote:
> > In rebase, one can pass the `--autostash` option to cause the worktree
> > to be automatically stashed before continuing with the rebase. This
> > option is missing in merge, however.
> 
> It might be helpful to say why this option is useful. I can see one might
> want to stash before doing `git pull` in the same way as one might before a
> rebase but what's the motivation for doing it before a normal merge?

Will do.

> 
> > 
> > Implement the `--autostash` option and corresponding `merge.autoStash`
> > option in merge which stashes before merging and then pops after.
> > 
> > Reported-by: Alban Gruin <alban.gruin@gmail.com>
> > Signed-off-by: Denton Liu <liu.denton@gmail.com>
> > ---
> >   builtin/merge.c | 13 +++++++++++++
> >   builtin/pull.c  |  9 +++++----
> >   t/t5520-pull.sh |  8 --------
> >   3 files changed, 18 insertions(+), 12 deletions(-)
> > 
> > diff --git a/builtin/merge.c b/builtin/merge.c
> > index 062e911441..d1a5eaad0d 100644
> > --- a/builtin/merge.c
> > +++ b/builtin/merge.c
> > @@ -40,6 +40,7 @@
> >   #include "branch.h"
> >   #include "commit-reach.h"
> >   #include "wt-status.h"
> > +#include "autostash.h"
> >   #define DEFAULT_TWOHEAD (1<<0)
> >   #define DEFAULT_OCTOPUS (1<<1)
> > @@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {
> >   	NULL
> >   };
> > +static GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
> > +
> >   static int show_diffstat = 1, shortlog_len = -1, squash;
> >   static int option_commit = -1;
> >   static int option_edit = -1;
> > @@ -81,6 +84,7 @@ static int show_progress = -1;
> >   static int default_to_upstream = 1;
> >   static int signoff;
> >   static const char *sign_commit;
> > +static int autostash;
> >   static int no_verify;
> >   static struct strategy all_strategy[] = {
> > @@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {
> >   	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
> >   	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
> >   	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
> > +	OPT_BOOL(0, "autostash", &autostash,
> > +	      N_("automatically stash/stash pop before and after")),
> >   	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
> >   	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
> >   	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
> > @@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,
> >   		strbuf_addf(&reflog_message, "%s: %s",
> >   			getenv("GIT_REFLOG_ACTION"), msg);
> >   	}
> > +	apply_autostash(merge_autostash());
> >   	if (squash) {
> >   		squash_message(head_commit, remoteheads);
> >   	} else {
> > @@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
> >   	} else if (!strcmp(k, "commit.gpgsign")) {
> >   		sign_commit = git_config_bool(k, v) ? "" : NULL;
> >   		return 0;
> > +	} else if (!strcmp(k, "merge.autostash")) {
> > +		autostash = git_config_bool(k, v);
> > +		return 0;
> >   	}
> >   	status = fmt_merge_msg_config(k, v, cb);
> > @@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
> >   		for (j = common; j; j = j->next)
> >   			commit_list_insert(j->item, &reversed);
> > +		if (autostash)
> > +			perform_autostash(merge_autostash());
> >   		hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
> >   		clean = merge_recursive(&o, head,
> >   				remoteheads->item, reversed, &result);
> > @@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
> >   		/* Invoke 'git reset --merge' */
> >   		ret = cmd_reset(nargc, nargv, prefix);
> > +		apply_autostash(merge_autostash());
> >   		goto done;
> >   	}
> > diff --git a/builtin/pull.c b/builtin/pull.c
> > index d25ff13a60..ee186781ae 100644
> > --- a/builtin/pull.c
> > +++ b/builtin/pull.c
> > @@ -183,7 +183,7 @@ static struct option pull_options[] = {
> >   		N_("verify that the named commit has a valid GPG signature"),
> >   		PARSE_OPT_NOARG),
> >   	OPT_BOOL(0, "autostash", &opt_autostash,
> > -		N_("automatically stash/stash pop before and after rebase")),
> > +		N_("automatically stash/stash pop before and after")),
> 
> I've not looked closely at the code in this patch but noticed this. I think
> it would read better if it said
> 
>     automatically stash/stash pop before and after pulling

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

Thanks,

Denton

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 7/7] merge: teach --autostash option
  2019-10-21 19:10   ` Johannes Schindelin
@ 2019-12-24 10:05     ` Denton Liu
  2019-12-24 10:12       ` Denton Liu
  0 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2019-12-24 10:05 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Git Mailing List, Alban Gruin, Junio C Hamano

Hi Dscho,

On Mon, Oct 21, 2019 at 09:10:09PM +0200, Johannes Schindelin wrote:
> Hi Denton,
> 
> On Wed, 16 Oct 2019, Denton Liu wrote:
> 
> > In rebase, one can pass the `--autostash` option to cause the worktree
> > to be automatically stashed before continuing with the rebase. This
> > option is missing in merge, however.
> >
> > Implement the `--autostash` option and corresponding `merge.autoStash`
> > option in merge which stashes before merging and then pops after.
> >
> > Reported-by: Alban Gruin <alban.gruin@gmail.com>
> 
> Maybe "Suggested-by" would be more accurate, it is not like this feature
> request was a bug report...

Makes sense.

> 
> > Signed-off-by: Denton Liu <liu.denton@gmail.com>
> > ---
> >  builtin/merge.c | 13 +++++++++++++
> >  builtin/pull.c  |  9 +++++----
> >  t/t5520-pull.sh |  8 --------
> >  3 files changed, 18 insertions(+), 12 deletions(-)
> >
> > diff --git a/builtin/merge.c b/builtin/merge.c
> > index 062e911441..d1a5eaad0d 100644
> > --- a/builtin/merge.c
> > +++ b/builtin/merge.c
> > @@ -40,6 +40,7 @@
> >  #include "branch.h"
> >  #include "commit-reach.h"
> >  #include "wt-status.h"
> > +#include "autostash.h"
> >
> >  #define DEFAULT_TWOHEAD (1<<0)
> >  #define DEFAULT_OCTOPUS (1<<1)
> > @@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {
> >  	NULL
> >  };
> >
> > +static GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
> > +
> >  static int show_diffstat = 1, shortlog_len = -1, squash;
> >  static int option_commit = -1;
> >  static int option_edit = -1;
> > @@ -81,6 +84,7 @@ static int show_progress = -1;
> >  static int default_to_upstream = 1;
> >  static int signoff;
> >  static const char *sign_commit;
> > +static int autostash;
> >  static int no_verify;
> >
> >  static struct strategy all_strategy[] = {
> > @@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {
> >  	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
> >  	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
> >  	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
> > +	OPT_BOOL(0, "autostash", &autostash,
> > +	      N_("automatically stash/stash pop before and after")),
> >  	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
> >  	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
> >  	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
> > @@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,
> >  		strbuf_addf(&reflog_message, "%s: %s",
> >  			getenv("GIT_REFLOG_ACTION"), msg);
> >  	}
> > +	apply_autostash(merge_autostash());
> 
> Should this not be guarded by `if (autostash)`?

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

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

> 
> >  	if (squash) {
> >  		squash_message(head_commit, remoteheads);
> >  	} else {
> > @@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
> >  	} else if (!strcmp(k, "commit.gpgsign")) {
> >  		sign_commit = git_config_bool(k, v) ? "" : NULL;
> >  		return 0;
> > +	} else if (!strcmp(k, "merge.autostash")) {
> > +		autostash = git_config_bool(k, v);
> > +		return 0;
> >  	}
> >
> >  	status = fmt_merge_msg_config(k, v, cb);
> > @@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
> >  		for (j = common; j; j = j->next)
> >  			commit_list_insert(j->item, &reversed);
> >
> > +		if (autostash)
> > +			perform_autostash(merge_autostash());
> >  		hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
> >  		clean = merge_recursive(&o, head,
> >  				remoteheads->item, reversed, &result);
> > @@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
> >
> >  		/* Invoke 'git reset --merge' */
> >  		ret = cmd_reset(nargc, nargv, prefix);
> > +		apply_autostash(merge_autostash());
> 
> Again, this should be guarded by `if (autostash)`, methinks.
> 
> >  		goto done;
> >  	}
> >
> > diff --git a/builtin/pull.c b/builtin/pull.c
> > index d25ff13a60..ee186781ae 100644
> > --- a/builtin/pull.c
> > +++ b/builtin/pull.c
> > @@ -183,7 +183,7 @@ static struct option pull_options[] = {
> >  		N_("verify that the named commit has a valid GPG signature"),
> >  		PARSE_OPT_NOARG),
> >  	OPT_BOOL(0, "autostash", &opt_autostash,
> > -		N_("automatically stash/stash pop before and after rebase")),
> > +		N_("automatically stash/stash pop before and after")),
> 
> Makes sense; this is now shared between the rebase and the merge modes.
> 
> >  	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
> >  		N_("merge strategy to use"),
> >  		0),
> > @@ -671,6 +671,10 @@ static int run_merge(void)
> >  	argv_array_pushv(&args, opt_strategy_opts.argv);
> >  	if (opt_gpg_sign)
> >  		argv_array_push(&args, opt_gpg_sign);
> > +	if (opt_autostash == 0)
> > +		argv_array_push(&args, "--no-autostash");
> > +	else if (opt_autostash == 1)
> > +		argv_array_push(&args, "--autostash");
> 
> Or shorter:
> 
> 	argv_array_pushf(&args, "%s-autostash", opt_autostash ? "-" : "--no");
> 
> Ah, but that would mishandle `-1`. I bet I will be puzzled by this
> again. Maybe it would make sense to mention in a code comment that it
> can be `-1` in which case we leave it to `rebase` to use the config
> settings to determine whether or not to autostash.

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

> 
> >  	if (opt_allow_unrelated_histories > 0)
> >  		argv_array_push(&args, "--allow-unrelated-histories");
> >
> > @@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
> >  	if (get_oid("HEAD", &orig_head))
> >  		oidclr(&orig_head);
> >
> > -	if (!opt_rebase && opt_autostash != -1)
> > -		die(_("--[no-]autostash option is only valid with --rebase."));
> > -
> >  	autostash = config_autostash;
> >  	if (opt_rebase) {
> >  		if (opt_autostash != -1)
> > diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
> > index cf4cc32fd0..75f162495a 100755
> > --- a/t/t5520-pull.sh
> > +++ b/t/t5520-pull.sh
> > @@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
> >  	test_pull_autostash_fail --rebase --no-autostash
> >  '
> >
> > -for i in --autostash --no-autostash
> > -do
> > -	test_expect_success "pull $i (without --rebase) is illegal" '
> > -		test_must_fail git pull $i . copy 2>err &&
> > -		test_i18ngrep "only valid with --rebase" err
> > -	'
> > -done
> > -
> 
> Nice!
> Dscho

Thanks,

Denton

> 
> >  test_expect_success 'pull.rebase' '
> >  	git reset --hard before-rebase &&
> >  	test_config pull.rebase true &&
> > --
> > 2.23.0.897.g0a19638b1e
> >
> >

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [RFC PATCH 7/7] merge: teach --autostash option
  2019-12-24 10:05     ` Denton Liu
@ 2019-12-24 10:12       ` Denton Liu
  0 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 10:12 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Git Mailing List, Alban Gruin, Junio C Hamano

On Tue, Dec 24, 2019 at 05:05:07AM -0500, Denton Liu wrote:
> > >  	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
> > >  		N_("merge strategy to use"),
> > >  		0),
> > > @@ -671,6 +671,10 @@ static int run_merge(void)
> > >  	argv_array_pushv(&args, opt_strategy_opts.argv);
> > >  	if (opt_gpg_sign)
> > >  		argv_array_push(&args, opt_gpg_sign);
> > > +	if (opt_autostash == 0)
> > > +		argv_array_push(&args, "--no-autostash");
> > > +	else if (opt_autostash == 1)
> > > +		argv_array_push(&args, "--autostash");
> > 
> > Or shorter:
> > 
> > 	argv_array_pushf(&args, "%s-autostash", opt_autostash ? "-" : "--no");
> > 
> > Ah, but that would mishandle `-1`. I bet I will be puzzled by this
> > again. Maybe it would make sense to mention in a code comment that it
> > can be `-1` in which case we leave it to `rebase` to use the config
> > settings to determine whether or not to autostash.
> 
> I copied this over from the rebase case. I'll add a comment there as
> well.

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 00/17] merge: learn --autostash
  2019-10-16 17:26 [RFC PATCH 0/7] merge: learn --autostash Denton Liu
                   ` (6 preceding siblings ...)
  2019-10-16 17:26 ` [RFC PATCH 7/7] merge: teach --autostash option Denton Liu
@ 2019-12-24 11:04 ` Denton Liu
  2019-12-24 11:04   ` [PATCH v2 01/17] Makefile: alphabetically sort += lists Denton Liu
                     ` (18 more replies)
  7 siblings, 19 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:04 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Changes since v1:

* Completely changed how the refactoring was done

* More tests and documentation

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

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

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

-- 
2.24.1.810.g65a2f617f4


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 01/17] Makefile: alphabetically sort += lists
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
@ 2019-12-24 11:04   ` Denton Liu
  2019-12-30 21:38     ` Junio C Hamano
  2019-12-24 11:04   ` [PATCH v2 02/17] t7600: use test_write_lines() Denton Liu
                     ` (17 subsequent siblings)
  18 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:04 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Makefile | 75 ++++++++++++++++++++++++++++----------------------------
 1 file changed, 38 insertions(+), 37 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 02/17] t7600: use test_write_lines()
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
  2019-12-24 11:04   ` [PATCH v2 01/17] Makefile: alphabetically sort += lists Denton Liu
@ 2019-12-24 11:04   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 03/17] sequencer: use file strbuf for read_oneliner() Denton Liu
                     ` (16 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:04 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 t/t7600-merge.sh | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 03/17] sequencer: use file strbuf for read_oneliner()
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
  2019-12-24 11:04   ` [PATCH v2 01/17] Makefile: alphabetically sort += lists Denton Liu
  2019-12-24 11:04   ` [PATCH v2 02/17] t7600: use test_write_lines() Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 04/17] sequencer: configurably warn on non-existent files Denton Liu
                     ` (15 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 763ccbbc45..5ba8b4383f 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -429,26 +429,28 @@ static int write_message(const void *buf, size_t len, const char *filename,
 static int read_oneliner(struct strbuf *buf,
 	const char *path, int skip_if_empty)
 {
-	int orig_len = buf->len;
+	int ret = 0;
+	struct strbuf file_buf = STRBUF_INIT;
 
 	if (!file_exists(path))
 		return 0;
 
-	if (strbuf_read_file(buf, path, 0) < 0) {
+	if (strbuf_read_file(&file_buf, path, 0) < 0) {
 		warning_errno(_("could not read '%s'"), path);
-		return 0;
+		goto done;
 	}
 
-	if (buf->len > orig_len && buf->buf[buf->len - 1] == '\n') {
-		if (--buf->len > orig_len && buf->buf[buf->len - 1] == '\r')
-			--buf->len;
-		buf->buf[buf->len] = '\0';
-	}
+	strbuf_trim_trailing_newline(&file_buf);
 
-	if (skip_if_empty && buf->len == orig_len)
-		return 0;
+	if (skip_if_empty && !file_buf.len)
+		goto done;
 
-	return 1;
+	strbuf_addbuf(buf, &file_buf);
+	ret = 1;
+
+done:
+	strbuf_release(&file_buf);
+	return ret;
 }
 
 static struct tree *empty_tree(struct repository *r)
-- 
2.24.1.810.g65a2f617f4


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 04/17] sequencer: configurably warn on non-existent files
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (2 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 03/17] sequencer: use file strbuf for read_oneliner() Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-26 20:39     ` Junio C Hamano
  2019-12-24 11:05   ` [PATCH v2 05/17] sequencer: make read_oneliner() extern Denton Liu
                     ` (14 subsequent siblings)
  18 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

	@@
	expression a, b, c;
	@@
	  read_oneliner(a, b, c
	+ , 0
	  )

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 05/17] sequencer: make read_oneliner() extern
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (3 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 04/17] sequencer: configurably warn on non-existent files Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 06/17] rebase: use read_oneliner() Denton Liu
                     ` (13 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 13 ++-----------
 sequencer.h | 11 +++++++++++
 2 files changed, 13 insertions(+), 11 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 06/17] rebase: use read_oneliner()
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (4 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 05/17] sequencer: make read_oneliner() extern Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 07/17] sequencer: make apply_rebase() accept a path Denton Liu
                     ` (12 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

This was done with the following Coccinelle script:

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 29 +++++++++--------------------
 1 file changed, 9 insertions(+), 20 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 07/17] sequencer: make apply_rebase() accept a path
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (5 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 06/17] rebase: use read_oneliner() Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 08/17] rebase: use apply_autostash() from sequencer.c Denton Liu
                     ` (11 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 08/17] rebase: use apply_autostash() from sequencer.c
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (6 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 07/17] sequencer: make apply_rebase() accept a path Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 09/17] rebase: generify reset_head() Denton Liu
                     ` (10 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 49 ++----------------------------------------------
 sequencer.c      |  2 +-
 sequencer.h      |  2 ++
 3 files changed, 5 insertions(+), 48 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 09/17] rebase: generify reset_head()
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (7 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 08/17] rebase: use apply_autostash() from sequencer.c Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 10/17] reset: extract reset_head() from rebase Denton Liu
                     ` (9 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 63 +++++++++++++++++++++++++++---------------------
 1 file changed, 36 insertions(+), 27 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 10/17] reset: extract reset_head() from rebase
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (8 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 09/17] rebase: generify reset_head() Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 11/17] rebase: extract create_autostash() Denton Liu
                     ` (8 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Makefile         |   1 +
 builtin/rebase.c | 139 +---------------------------------------------
 reset.c          | 140 +++++++++++++++++++++++++++++++++++++++++++++++
 reset.h          |  20 +++++++
 4 files changed, 162 insertions(+), 138 deletions(-)
 create mode 100644 reset.c
 create mode 100644 reset.h

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 11/17] rebase: extract create_autostash()
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (9 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 10/17] reset: extract reset_head() from rebase Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 12/17] rebase: generify create_autostash() Denton Liu
                     ` (7 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 94 +++++++++++++++++++++++++-----------------------
 1 file changed, 50 insertions(+), 44 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 12/17] rebase: generify create_autostash()
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (10 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 11/17] rebase: extract create_autostash() Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 13/17] sequencer: extract perform_autostash() from rebase Denton Liu
                     ` (6 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 13/17] sequencer: extract perform_autostash() from rebase
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (11 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 12/17] rebase: generify create_autostash() Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 14/17] sequencer: unlink autostash in apply_autostash() Denton Liu
                     ` (5 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 49 -----------------------------------------------
 sequencer.c      | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |  2 ++
 3 files changed, 52 insertions(+), 49 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index fea4ae754d..666d3f0181 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1232,55 +1232,6 @@ static int check_exec_cmd(const char *cmd)
 	return 0;
 }
 
-static void create_autostash(struct repository *r, const char *path,
-			     const char *default_reflog_action)
-{
-	struct strbuf buf = STRBUF_INIT;
-	struct lock_file lock_file = LOCK_INIT;
-	int fd;
-
-	fd = repo_hold_locked_index(r, &lock_file, 0);
-	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
-	if (0 <= fd)
-		repo_update_index_if_able(r, &lock_file);
-	rollback_lock_file(&lock_file);
-
-	if (has_unstaged_changes(r, 1) ||
-	    has_uncommitted_changes(r, 1)) {
-		struct child_process stash = CHILD_PROCESS_INIT;
-		struct object_id oid;
-
-		argv_array_pushl(&stash.args,
-				 "stash", "create", "autostash", NULL);
-		stash.git_cmd = 1;
-		stash.no_stdin = 1;
-		strbuf_reset(&buf);
-		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
-			die(_("Cannot autostash"));
-		strbuf_trim_trailing_newline(&buf);
-		if (get_oid(buf.buf, &oid))
-			die(_("Unexpected stash response: '%s'"),
-			    buf.buf);
-		strbuf_reset(&buf);
-		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
-
-		if (safe_create_leading_directories_const(path))
-			die(_("Could not create directory for '%s'"),
-			    path);
-		write_file(path, "%s", oid_to_hex(&oid));
-		printf(_("Created autostash: %s\n"), buf.buf);
-		if (reset_head(r, NULL, "reset --hard",
-			       NULL, RESET_HEAD_HARD, NULL, NULL,
-			       default_reflog_action) < 0)
-			die(_("could not reset --hard"));
-
-		if (discard_index(r->index) < 0 ||
-			repo_read_index(r) < 0)
-			die(_("could not read index"));
-	}
-	strbuf_release(&buf);
-}
-
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
 	struct rebase_options options = REBASE_OPTIONS_INIT;
diff --git a/sequencer.c b/sequencer.c
index 98e08dbbe0..ba04ee2de8 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -32,6 +32,7 @@
 #include "alias.h"
 #include "commit-reach.h"
 #include "rebase-interactive.h"
+#include "reset.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -3744,6 +3745,55 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
 	return -1;
 }
 
+void create_autostash(struct repository *r, const char *path,
+		      const char *default_reflog_action)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct lock_file lock_file = LOCK_INIT;
+	int fd;
+
+	fd = repo_hold_locked_index(r, &lock_file, 0);
+	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
+	if (0 <= fd)
+		repo_update_index_if_able(r, &lock_file);
+	rollback_lock_file(&lock_file);
+
+	if (has_unstaged_changes(r, 1) ||
+	    has_uncommitted_changes(r, 1)) {
+		struct child_process stash = CHILD_PROCESS_INIT;
+		struct object_id oid;
+
+		argv_array_pushl(&stash.args,
+				 "stash", "create", "autostash", NULL);
+		stash.git_cmd = 1;
+		stash.no_stdin = 1;
+		strbuf_reset(&buf);
+		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
+			die(_("Cannot autostash"));
+		strbuf_trim_trailing_newline(&buf);
+		if (get_oid(buf.buf, &oid))
+			die(_("Unexpected stash response: '%s'"),
+			    buf.buf);
+		strbuf_reset(&buf);
+		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
+
+		if (safe_create_leading_directories_const(path))
+			die(_("Could not create directory for '%s'"),
+			    path);
+		write_file(path, "%s", oid_to_hex(&oid));
+		printf(_("Created autostash: %s\n"), buf.buf);
+		if (reset_head(r, NULL, "reset --hard",
+			       NULL, RESET_HEAD_HARD, NULL, NULL,
+			       default_reflog_action) < 0)
+			die(_("could not reset --hard"));
+
+		if (discard_index(r->index) < 0 ||
+			repo_read_index(r) < 0)
+			die(_("could not read index"));
+	}
+	strbuf_release(&buf);
+}
+
 int apply_autostash(const char *path)
 {
 	struct strbuf stash_sha1 = STRBUF_INIT;
diff --git a/sequencer.h b/sequencer.h
index ecef2c144c..8beb1472f7 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -195,6 +195,8 @@ void commit_post_rewrite(struct repository *r,
 int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
 				 const char *commit);
 
+void create_autostash(struct repository *r, const char *path,
+		      const char *default_reflog_action);
 int apply_autostash(const char *path);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
-- 
2.24.1.810.g65a2f617f4


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 14/17] sequencer: unlink autostash in apply_autostash()
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (12 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 13/17] sequencer: extract perform_autostash() from rebase Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 15/17] merge: teach --autostash option Denton Liu
                     ` (4 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/sequencer.c b/sequencer.c
index ba04ee2de8..cc0391ca35 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -3834,6 +3834,7 @@ int apply_autostash(const char *path)
 				  " \"git stash drop\" at any time.\n"));
 	}
 
+	unlink(path);
 	strbuf_release(&stash_sha1);
 	return ret;
 }
-- 
2.24.1.810.g65a2f617f4


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 15/17] merge: teach --autostash option
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (13 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 14/17] sequencer: unlink autostash in apply_autostash() Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 16/17] t5520: make test_pull_autostash() accept expect_parent_num Denton Liu
                     ` (3 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

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

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

but now, that is reduced to

	git fetch ...
	git merge --autostash FETCH_HEAD

Suggested-by: Alban Gruin <alban.gruin@gmail.com>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Documentation/config/merge.txt  | 10 +++++
 Documentation/merge-options.txt |  8 ++++
 branch.c                        |  1 +
 builtin/commit.c                |  2 +
 builtin/merge.c                 | 17 +++++++++
 path.c                          |  1 +
 path.h                          |  4 +-
 t/t3033-merge-toplevel.sh       | 22 +++++++++++
 t/t7600-merge.sh                | 65 +++++++++++++++++++++++++++++++++
 9 files changed, 129 insertions(+), 1 deletion(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 16/17] t5520: make test_pull_autostash() accept expect_parent_num
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (14 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 15/17] merge: teach --autostash option Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-24 11:05   ` [PATCH v2 17/17] pull: pass --autostash to merge Denton Liu
                     ` (2 subsequent siblings)
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 t/t5520-pull.sh | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v2 17/17] pull: pass --autostash to merge
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (15 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 16/17] t5520: make test_pull_autostash() accept expect_parent_num Denton Liu
@ 2019-12-24 11:05   ` Denton Liu
  2019-12-30 21:49   ` [PATCH v2 00/17] merge: learn --autostash Junio C Hamano
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
  18 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2019-12-24 11:05 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Documentation/git-pull.txt      |  9 -------
 Documentation/merge-options.txt |  4 +--
 builtin/pull.c                  |  9 ++++---
 t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------
 4 files changed, 42 insertions(+), 23 deletions(-)

diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index dfb901f8b8..ba3772de9f 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.
 --no-rebase::
 	Override earlier --rebase.
 
---autostash::
---no-autostash::
-	Before starting rebase, stash local modifications away (see
-	linkgit:git-stash[1]) if needed, and apply the stash entry when
-	done. `--no-autostash` is useful to override the `rebase.autoStash`
-	configuration variable (see linkgit:git-config[1]).
-+
-This option is only valid when "--rebase" is used.
-
 Options related to fetching
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 34493eb58b..ae56cca826 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -155,6 +155,8 @@ ifndef::git-pull[]
 	Note that not all merge strategies may support progress
 	reporting.
 
+endif::git-pull[]
+
 --autostash::
 --no-autostash::
 	Automatically create a temporary stash entry before the operation
@@ -163,8 +165,6 @@ ifndef::git-pull[]
 	with care: the final stash application after a successful
 	rebase might result in non-trivial conflicts.
 
-endif::git-pull[]
-
 --allow-unrelated-histories::
 	By default, `git merge` command refuses to merge histories
 	that do not share a common ancestor.  This option can be
diff --git a/builtin/pull.c b/builtin/pull.c
index d25ff13a60..ee186781ae 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -183,7 +183,7 @@ static struct option pull_options[] = {
 		N_("verify that the named commit has a valid GPG signature"),
 		PARSE_OPT_NOARG),
 	OPT_BOOL(0, "autostash", &opt_autostash,
-		N_("automatically stash/stash pop before and after rebase")),
+		N_("automatically stash/stash pop before and after")),
 	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
 		N_("merge strategy to use"),
 		0),
@@ -671,6 +671,10 @@ static int run_merge(void)
 	argv_array_pushv(&args, opt_strategy_opts.argv);
 	if (opt_gpg_sign)
 		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");
 	if (opt_allow_unrelated_histories > 0)
 		argv_array_push(&args, "--allow-unrelated-histories");
 
@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 	if (get_oid("HEAD", &orig_head))
 		oidclr(&orig_head);
 
-	if (!opt_rebase && opt_autostash != -1)
-		die(_("--[no-]autostash option is only valid with --rebase."));
-
 	autostash = config_autostash;
 	if (opt_rebase) {
 		if (opt_autostash != -1)
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 218f469d0a..2919ad4f81 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -28,7 +28,7 @@ test_pull_autostash_fail () {
 	echo dirty >new_file &&
 	git add new_file &&
 	test_must_fail git pull "$@" . copy 2>err &&
-	test_i18ngrep "uncommitted changes." err
+	test_i18ngrep "\(uncommitted changes.\)\|\(overwritten by merge:\)" err
 }
 
 test_expect_success setup '
@@ -391,13 +391,40 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
 	test_pull_autostash_fail --rebase --no-autostash
 '
 
-for i in --autostash --no-autostash
-do
-	test_expect_success "pull $i (without --rebase) is illegal" '
-		test_must_fail git pull $i . copy 2>err &&
-		test_i18ngrep "only valid with --rebase" err
-	'
-done
+test_expect_success 'pull succeeds with dirty working directory and merge.autostash set' '
+	test_config merge.autostash true &&
+	test_pull_autostash 2
+'
+
+test_expect_success 'pull --autostash & merge.autostash=true' '
+	test_config merge.autostash true &&
+	test_pull_autostash 2 --autostash
+'
+
+test_expect_success 'pull --autostash & merge.autostash=false' '
+	test_config merge.autostash false &&
+	test_pull_autostash 2 --autostash
+'
+
+test_expect_success 'pull --autostash & merge.autostash unset' '
+	test_unconfig merge.autostash &&
+	test_pull_autostash 2 --autostash
+'
+
+test_expect_success 'pull --no-autostash & merge.autostash=true' '
+	test_config merge.autostash true &&
+	test_pull_autostash_fail --no-autostash
+'
+
+test_expect_success 'pull --no-autostash & merge.autostash=false' '
+	test_config merge.autostash false &&
+	test_pull_autostash_fail --no-autostash
+'
+
+test_expect_success 'pull --no-autostash & merge.autostash unset' '
+	test_unconfig merge.autostash &&
+	test_pull_autostash_fail --no-autostash
+'
 
 test_expect_success 'pull.rebase' '
 	git reset --hard before-rebase &&
-- 
2.24.1.810.g65a2f617f4


^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v2 04/17] sequencer: configurably warn on non-existent files
  2019-12-24 11:05   ` [PATCH v2 04/17] sequencer: configurably warn on non-existent files Denton Liu
@ 2019-12-26 20:39     ` Junio C Hamano
  0 siblings, 0 replies; 152+ messages in thread
From: Junio C Hamano @ 2019-12-26 20:39 UTC (permalink / raw)
  To: Denton Liu
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Phillip Wood

Denton Liu <liu.denton@gmail.com> writes:

> -static int read_oneliner(struct strbuf *buf,
> -	const char *path, int skip_if_empty)
> +static int read_oneliner(struct strbuf *buf, const char *path,
> +			 int skip_if_empty, int warn_nonexistence)

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

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

Two possible ways to improve are:

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

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

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

   would transparently work well for those other topics.

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

Thanks.

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v2 01/17] Makefile: alphabetically sort += lists
  2019-12-24 11:04   ` [PATCH v2 01/17] Makefile: alphabetically sort += lists Denton Liu
@ 2019-12-30 21:38     ` Junio C Hamano
  0 siblings, 0 replies; 152+ messages in thread
From: Junio C Hamano @ 2019-12-30 21:38 UTC (permalink / raw)
  To: Denton Liu
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Phillip Wood

Denton Liu <liu.denton@gmail.com> writes:

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

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

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

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v2 00/17] merge: learn --autostash
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (16 preceding siblings ...)
  2019-12-24 11:05   ` [PATCH v2 17/17] pull: pass --autostash to merge Denton Liu
@ 2019-12-30 21:49   ` Junio C Hamano
  2019-12-31 10:34     ` Phillip Wood
  2020-01-01  7:48     ` Denton Liu
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
  18 siblings, 2 replies; 152+ messages in thread
From: Junio C Hamano @ 2019-12-30 21:49 UTC (permalink / raw)
  To: Denton Liu
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Phillip Wood

Denton Liu <liu.denton@gmail.com> writes:

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

Sigh.  

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

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

Let's see how it goes.  Queued.

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v2 00/17] merge: learn --autostash
  2019-12-30 21:49   ` [PATCH v2 00/17] merge: learn --autostash Junio C Hamano
@ 2019-12-31 10:34     ` Phillip Wood
  2020-01-08  6:08       ` Denton Liu
  2020-01-01  7:48     ` Denton Liu
  1 sibling, 1 reply; 152+ messages in thread
From: Phillip Wood @ 2019-12-31 10:34 UTC (permalink / raw)
  To: Junio C Hamano, Denton Liu
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin

On 30/12/2019 21:49, Junio C Hamano wrote:
> Denton Liu <liu.denton@gmail.com> writes:
> 
>> Alban reported[1] that he expected merge to have an --autostash option,
>> just like rebase. Since there's not really any reason why merge can't
>> have it, let's implement it in this patchset.
> 
> Sigh.
> 
> I would actually have preferred to remove the autostash from rebase
> if the goal is to have a consistent behaviour between the two,
> though ;-)

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

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

Best Wishes

Phillip


> But it is OK as long as it does not degrade the codepath with
> changes that wouldn't have been necessary if this weren't added.
> 
> Let's see how it goes.  Queued.
> 

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v2 00/17] merge: learn --autostash
  2019-12-30 21:49   ` [PATCH v2 00/17] merge: learn --autostash Junio C Hamano
  2019-12-31 10:34     ` Phillip Wood
@ 2020-01-01  7:48     ` Denton Liu
  1 sibling, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-01-01  7:48 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Phillip Wood

Hi Junio,

On Mon, Dec 30, 2019 at 01:49:05PM -0800, Junio C Hamano wrote:
> Denton Liu <liu.denton@gmail.com> writes:
> 
> > Alban reported[1] that he expected merge to have an --autostash option,
> > just like rebase. Since there's not really any reason why merge can't
> > have it, let's implement it in this patchset.
> 
> Sigh.  
> 
> I would actually have preferred to remove the autostash from rebase
> if the goal is to have a consistent behaviour between the two,
> though ;-)
> 
> But it is OK as long as it does not degrade the codepath with
> changes that wouldn't have been necessary if this weren't added.
> 
> Let's see how it goes.  Queued.

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

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

Thanks,

Denton

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v2 00/17] merge: learn --autostash
  2019-12-31 10:34     ` Phillip Wood
@ 2020-01-08  6:08       ` Denton Liu
  2020-01-10 14:44         ` Phillip Wood
  0 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-01-08  6:08 UTC (permalink / raw)
  To: phillip.wood
  Cc: Junio C Hamano, Git Mailing List, Alban Gruin, Johannes Schindelin

Hi Phillip,

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

On Tue, Dec 31, 2019 at 10:34:30AM +0000, Phillip Wood wrote:
> For `merge --autostash` I think we need to consider the interaction of
> `--no-commit` with `--autostash` as `stash pop` will refuse to run if the
> merge modified any of the stashed paths.

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


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

Thanks,

Denton

> 
> Best Wishes
> 
> Phillip

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v2 00/17] merge: learn --autostash
  2020-01-08  6:08       ` Denton Liu
@ 2020-01-10 14:44         ` Phillip Wood
  2020-01-15 16:20           ` Denton Liu
  0 siblings, 1 reply; 152+ messages in thread
From: Phillip Wood @ 2020-01-10 14:44 UTC (permalink / raw)
  To: Denton Liu, phillip.wood
  Cc: Junio C Hamano, Git Mailing List, Alban Gruin, Johannes Schindelin

Hi Denton

On 08/01/2020 06:08, Denton Liu wrote:
> Hi Phillip,
> 
> Sorry for the late reply. I'm back in school so I've been pretty busy
> lately.
> 
> On Tue, Dec 31, 2019 at 10:34:30AM +0000, Phillip Wood wrote:
>> For `merge --autostash` I think we need to consider the interaction of
>> `--no-commit` with `--autostash` as `stash pop` will refuse to run if the
>> merge modified any of the stashed paths.
> 
> The only time when we run the `stash pop` is when we either commit the
> merge or abort it. With this in mind, what do you think of the following
> test cases? If they look good, I can squash them into the appropriate
> patch and send in a reroll.

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

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

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

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

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

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

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

Best Wishes

Phillip

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v2 00/17] merge: learn --autostash
  2020-01-10 14:44         ` Phillip Wood
@ 2020-01-15 16:20           ` Denton Liu
  0 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-01-15 16:20 UTC (permalink / raw)
  To: phillip.wood
  Cc: Junio C Hamano, Git Mailing List, Alban Gruin, Johannes Schindelin

On Fri, Jan 10, 2020 at 02:44:30PM +0000, Phillip Wood wrote:
> Hi Denton
> 
> On 08/01/2020 06:08, Denton Liu wrote:
> > Hi Phillip,
> > 
> > Sorry for the late reply. I'm back in school so I've been pretty busy
> > lately.
> > 
> > On Tue, Dec 31, 2019 at 10:34:30AM +0000, Phillip Wood wrote:
> > > For `merge --autostash` I think we need to consider the interaction of
> > > `--no-commit` with `--autostash` as `stash pop` will refuse to run if the
> > > merge modified any of the stashed paths.
> > 
> > The only time when we run the `stash pop` is when we either commit the
> > merge or abort it. With this in mind, what do you think of the following
> > test cases? If they look good, I can squash them into the appropriate
> > patch and send in a reroll.
> 
> Ah I misunderstood what happened with --autostash and --no-commit, the tests
> basically look fine (I've got one comment below).
> 
> One other thing - if the user runs `git reset` or `git checkout <branch>`
> then cleanup_branch_state() removes MERGE_HEAD, MERGE_MSG etc. If we're not
> already doing so then I think we should remove MERGE_AUTOSTASH as well or
> you can get into a situation where the user does something like

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

>   git merge --autostash <something> # results in conflicts
>   git reset --hard <somewhere else>
>   git merge <something> # succeeds and confusingly pops previous stash
> 
> Running `git reset` doesn't make sense unless they want to discard the
> stashed changes as well. This is a difference with rebase where you cannot
> lose the stashed changes by running `git reset`, the only way they can get
> lost is by running `rebase --quit`.  We could always add a warning about it
> throwing away the stashed changes in the future.

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

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

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

> 
> > 	diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
> > 	index e0c8a0bad4..af5db58e16 100755
> > 	--- a/t/t7600-merge.sh
> > 	+++ b/t/t7600-merge.sh
> > 	@@ -727,6 +727,33 @@ test_expect_success 'conflicted merge with --autostash, --abort restores stash'
> > 		test_cmp file.1 file
> > 	 '
> > 	
> > 	+test_expect_success 'completed merge with --no-commit and --autostash' '
> > 	+	git reset --hard c1 &&
> > 	+	git merge-file file file.orig file.9 &&
> 
> Is this a complicated way of getting some unstaged changes so we can stash
> them or have I missed something?

Yes, that's exactly what this does.

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 00/19] merge: learn --autostash
  2019-12-24 11:04 ` [PATCH v2 00/17] merge: learn --autostash Denton Liu
                     ` (17 preceding siblings ...)
  2019-12-30 21:49   ` [PATCH v2 00/17] merge: learn --autostash Junio C Hamano
@ 2020-03-21  9:21   ` Denton Liu
  2020-03-21  9:21     ` [PATCH v3 01/19] Makefile: ASCII-sort += lists Denton Liu
                       ` (19 more replies)
  18 siblings, 20 replies; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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


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

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

Changes since v2:

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

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

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

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

Changes since v1:

* Completely changed how the refactoring was done

* More tests and documentation

[1]: https://github.com/gitgitgadget/git/issues/394
[2]: https://lore.kernel.org/git/xmqqwoadfq8n.fsf@gitster-ct.c.googlers.com/
[3]: https://lore.kernel.org/git/xmqqwoaikehz.fsf@gitster-ct.c.googlers.com/
[4]: https://lore.kernel.org/git/20200123042906.GA29009@generichostname/

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

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

Range-diff against v2:
 1:  31055cdac4 !  1:  77506a39d0 Makefile: alphabetically sort += lists
    @@ Metadata
     Author: Denton Liu <liu.denton@gmail.com>
     
      ## Commit message ##
    -    Makefile: alphabetically sort += lists
    +    Makefile: ASCII-sort += lists
     
         There are many += lists in the Makefile and, over time, they have gotten
    -    slightly out of order, alphabetically. Alphabetically sort all += lists
    -    to bring them back in order.
    +    slightly out of ASCII order. Sort all += lists to bring them back in
    +    order.
     
    -    Note that if we omit file prefixes, the lists aren't sorted in strictly
    -    alphabetical order (e.g. archive.o comes after archive-zip.o instead of
    -    before archive-tar.o). This is intentional because the purpose of
    -    maintaining the sorted list is to ensure line insertions are
    -    deterministic. As a result, we want to ensure that anybody can easily
    -    reproduce the sort order and that is very simple when we don't have to
    -    treat the prefix specially.
    +    ASCII sorting was chosen over strict alphabetical order even though, if
    +    we omit file prefixes, the lists aren't sorted in strictly alphabetical
    +    order (e.g. archive.o comes after archive-zip.o instead of before
    +    archive-tar.o). This is intentional because the purpose of maintaining
    +    the sorted list is to ensure line insertions are deterministic. By using
    +    ASCII ordering, it is more easily mechanically reproducible in the
    +    future, such as by using :sort in Vim.
     
         This patch is best viewed with `--color-moved`.
     
    @@ Makefile: LIB_OBJS += mailinfo.o
      LIB_OBJS += patch-delta.o
      LIB_OBJS += patch-ids.o
      LIB_OBJS += path.o
    -@@ Makefile: LIB_OBJS += range-diff.o
    +@@ Makefile: LIB_OBJS += quote.o
    + LIB_OBJS += range-diff.o
      LIB_OBJS += reachable.o
      LIB_OBJS += read-cache.o
    +-LIB_OBJS += rebase.o
      LIB_OBJS += rebase-interactive.o
    ++LIB_OBJS += rebase.o
     +LIB_OBJS += ref-filter.o
      LIB_OBJS += reflog-walk.o
      LIB_OBJS += refs.o
 2:  2e0020b3e3 =  2:  70699b8b73 t7600: use test_write_lines()
 3:  de79e27c17 =  3:  7c37777f07 sequencer: use file strbuf for read_oneliner()
 4:  fd547aab49 <  -:  ---------- sequencer: configurably warn on non-existent files
 -:  ---------- >  4:  4f97086828 sequencer: make read_oneliner() accept flags
 -:  ---------- >  5:  e42f4bdd4d sequencer: configurably warn on non-existent files
 5:  819fb2203b !  6:  e4d0c2d902 sequencer: make read_oneliner() extern
    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
      	return 0;
      }
      
    +-#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
    +-#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
    +-
     -/*
     - * Reads a file that was presumably written by a shell script, i.e. with an
     - * end-of-line marker that needs to be stripped.
    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
     - *
     - * Returns 1 if the file was read, 0 if it could not be read or does not exist.
     - */
    --static int read_oneliner(struct strbuf *buf, const char *path,
    --			 int skip_if_empty, int warn_nonexistence)
    -+int read_oneliner(struct strbuf *buf, const char *path,
    -+		  int skip_if_empty, int warn_nonexistence)
    +-static int read_oneliner(struct strbuf *buf,
    ++int read_oneliner(struct strbuf *buf,
    + 	const char *path, unsigned flags)
      {
      	int ret = 0;
    - 	struct strbuf file_buf = STRBUF_INIT;
     
      ## sequencer.h ##
     @@ sequencer.h: void print_commit_summary(struct repository *repo,
      			  const struct object_id *oid,
      			  unsigned int flags);
      
    ++#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
    ++#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
    ++
     +/*
     + * Reads a file that was presumably written by a shell script, i.e. with an
     + * end-of-line marker that needs to be stripped.
    @@ sequencer.h: void print_commit_summary(struct repository *repo,
     + *
     + * Returns 1 if the file was read, 0 if it could not be read.
     + */
    -+int read_oneliner(struct strbuf *buf, const char *path,
    -+		  int skip_if_empty, int warn_nonexistence);
    ++int read_oneliner(struct strbuf *buf,
    ++	const char *path, unsigned flags);
      int read_author_script(const char *path, char **name, char **email, char **date,
      		       int allow_missing);
      void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
 6:  06c5bd48d7 !  7:  d07a50c5b2 rebase: use read_oneliner()
    @@ Commit message
         of read_oneliner(), reduce code duplication by replacing read_one() with
         read_oneliner().
     
    -    This was done with the following Coccinelle script:
    +    This was done with the following Coccinelle script
     
                 @@
                 expression a, b;
                 @@
                 - read_one(a, b)
    -            + !read_oneliner(b, a, 0, 1)
    +            + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)
    +
    +    and long lines were manually broken up.
     
      ## builtin/rebase.c ##
     @@ builtin/rebase.c: static const char *state_dir_path(const char *filename, struct rebase_options *o
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      
     -	if (read_one(state_dir_path("head-name", opts), &head_name) ||
     -	    read_one(state_dir_path("onto", opts), &buf))
    -+	if (!read_oneliner(&head_name, state_dir_path("head-name", opts), 0, 1) ||
    -+	    !read_oneliner(&buf, state_dir_path("onto", opts), 0, 1))
    ++	if (!read_oneliner(&head_name, state_dir_path("head-name", opts),
    ++			   READ_ONELINER_WARN_NON_EXISTENCE) ||
    ++	    !read_oneliner(&buf, state_dir_path("onto", opts),
    ++			   READ_ONELINER_WARN_NON_EXISTENCE))
      		return -1;
      	opts->head_name = starts_with(head_name.buf, "refs/") ?
      		xstrdup(head_name.buf) : NULL;
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      	strbuf_reset(&buf);
      	if (file_exists(state_dir_path("orig-head", opts))) {
     -		if (read_one(state_dir_path("orig-head", opts), &buf))
    -+		if (!read_oneliner(&buf, state_dir_path("orig-head", opts), 0, 1))
    ++		if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
    ++				   READ_ONELINER_WARN_NON_EXISTENCE))
      			return -1;
     -	} else if (read_one(state_dir_path("head", opts), &buf))
    -+	} else if (!read_oneliner(&buf, state_dir_path("head", opts), 0, 1))
    ++	} else if (!read_oneliner(&buf, state_dir_path("head", opts),
    ++				  READ_ONELINER_WARN_NON_EXISTENCE))
      		return -1;
      	if (get_oid(buf.buf, &opts->orig_head))
      		return error(_("invalid orig-head: '%s'"), buf.buf);
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
     -			    &buf))
    -+		if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts), 0, 1))
    ++		if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
    ++				   READ_ONELINER_WARN_NON_EXISTENCE))
      			return -1;
      		if (!strcmp(buf.buf, "--rerere-autoupdate"))
      			opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("gpg_sign_opt", opts),
     -			    &buf))
    -+		if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts), 0, 1))
    ++		if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
    ++				   READ_ONELINER_WARN_NON_EXISTENCE))
      			return -1;
      		free(opts->gpg_sign_opt);
      		opts->gpg_sign_opt = xstrdup(buf.buf);
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      	if (file_exists(state_dir_path("strategy", opts))) {
      		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("strategy", opts), &buf))
    -+		if (!read_oneliner(&buf, state_dir_path("strategy", opts), 0, 1))
    ++		if (!read_oneliner(&buf, state_dir_path("strategy", opts),
    ++				   READ_ONELINER_WARN_NON_EXISTENCE))
      			return -1;
      		free(opts->strategy);
      		opts->strategy = xstrdup(buf.buf);
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      	if (file_exists(state_dir_path("strategy_opts", opts))) {
      		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("strategy_opts", opts), &buf))
    -+		if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts), 0, 1))
    ++		if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
    ++				   READ_ONELINER_WARN_NON_EXISTENCE))
      			return -1;
      		free(opts->strategy_opts);
      		opts->strategy_opts = xstrdup(buf.buf);
    @@ builtin/rebase.c: static int apply_autostash(struct rebase_options *opts)
      		return 0;
      
     -	if (read_one(path, &autostash))
    -+	if (!read_oneliner(&autostash, path, 0, 1))
    ++	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
      		return error(_("Could not read '%s'"), path);
      	/* Ensure that the hash is not mistaken for a number */
      	strbuf_addstr(&autostash, "^0");
 7:  12c37d11ba !  8:  5bce4aeb96 sequencer: make apply_rebase() accept a path
    @@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
      	struct child_process child = CHILD_PROCESS_INIT;
      	int ret = 0;
      
    --	if (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1, 0)) {
    -+	if (!read_oneliner(&stash_sha1, path, 1, 0)) {
    +-	if (!read_oneliner(&stash_sha1, rebase_path_autostash(),
    ++	if (!read_oneliner(&stash_sha1, path,
    + 			   READ_ONELINER_SKIP_IF_EMPTY)) {
      		strbuf_release(&stash_sha1);
      		return 0;
    - 	}
     @@ sequencer.c: static int checkout_onto(struct repository *r, struct replay_opts *opts,
      		return error(_("%s: not a valid OID"), orig_head);
      
 8:  893a2f9825 !  9:  03bdaeebc9 rebase: use apply_autostash() from sequencer.c
    @@ builtin/rebase.c: static int rebase_write_basic_state(struct rebase_options *opt
     -	if (!file_exists(path))
     -		return 0;
     -
    --	if (!read_oneliner(&autostash, path, 0, 1))
    +-	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
     -		return error(_("Could not read '%s'"), path);
     -	/* Ensure that the hash is not mistaken for a number */
     -	strbuf_addstr(&autostash, "^0");
    @@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
     
      ## sequencer.h ##
     @@ sequencer.h: void commit_post_rewrite(struct repository *r,
    - int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
    - 				 const char *commit);
    + 			 const struct commit *current_head,
    + 			 const struct object_id *new_head);
      
     +int apply_autostash(const char *path);
     +
 9:  8e3c0c0aec ! 10:  c35c5bdb8c rebase: generify reset_head()
    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
      				strbuf_addf(&buf, "%s: checkout %s",
      					    getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
      					    options.switch_to);
    --				if (reset_head(&oid, "checkout",
    -+				if (reset_head(the_repository, &oid, "checkout",
    +-				if (reset_head(&options.orig_head, "checkout",
    ++				if (reset_head(the_repository,
    ++					       &options.orig_head, "checkout",
      					       options.head_name,
      					       RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
     -					       NULL, buf.buf) < 0) {
10:  981969d230 = 11:  a14a2e0849 reset: extract reset_head() from rebase
11:  1b5bbe66dc = 12:  1dbae3dfbf rebase: extract create_autostash()
12:  bba7586cba = 13:  fffc038cd6 rebase: generify create_autostash()
13:  2ff189692f ! 14:  bf55b86e2b sequencer: extract perform_autostash() from rebase
    @@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
     
      ## sequencer.h ##
     @@ sequencer.h: void commit_post_rewrite(struct repository *r,
    - int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
    - 				 const char *commit);
    + 			 const struct commit *current_head,
    + 			 const struct object_id *new_head);
      
     +void create_autostash(struct repository *r, const char *path,
     +		      const char *default_reflog_action);
14:  2b16711680 = 15:  d5af261eb8 sequencer: unlink autostash in apply_autostash()
 -:  ---------- > 16:  e21e2c6416 sequencer: implement save_autostash()
15:  6e987052c0 ! 17:  9e3d4393ca merge: teach --autostash option
    @@ Commit message
                 git fetch ...
                 git merge --autostash FETCH_HEAD
     
    +    When `git reset --hard` is run to abort a merge, the working tree will
    +    be left in a clean state, as expected, with the autostash pushed onto
    +    the stash stack.
    +
         Suggested-by: Alban Gruin <alban.gruin@gmail.com>
     
      ## Documentation/config/merge.txt ##
    @@ builtin/merge.c: static void finish(struct commit *head_commit,
      		squash_message(head_commit, remoteheads);
      	} else {
     @@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void *cb)
    - 	} else if (!strcmp(k, "commit.gpgsign")) {
    - 		sign_commit = git_config_bool(k, v) ? "" : NULL;
      		return 0;
    + 	} else if (!strcmp(k, "gpg.mintrustlevel")) {
    + 		check_trust_level = 0;
     +	} else if (!strcmp(k, "merge.autostash")) {
     +		autostash = git_config_bool(k, v);
     +		return 0;
    @@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
      	git_committer_info(IDENT_STRICT);
      
     
    + ## builtin/reset.c ##
    +@@
    + #include "cache-tree.h"
    + #include "submodule.h"
    + #include "submodule-config.h"
    ++#include "sequencer.h"
    + 
    + #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
    + 
    +@@ builtin/reset.c: int cmd_reset(int argc, const char **argv, const char *prefix)
    + 		if (reset_type == HARD && !update_ref_status && !quiet)
    + 			print_new_head_line(lookup_commit_reference(the_repository, &oid));
    + 	}
    +-	if (!pathspec.nr)
    ++	if (!pathspec.nr) {
    ++		if (reset_type == HARD)
    ++			save_autostash(git_path_merge_autostash(the_repository));
    ++
    + 		remove_branch_state(the_repository, 0);
    ++	}
    + 
    + 	return update_ref_status;
    + }
    +
      ## path.c ##
     @@ path.c: REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
      REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
    @@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '
     +	test_cmp file.1 file
     +'
     +
    ++test_expect_success 'completed merge with --no-commit and --autostash' '
    ++	git reset --hard c1 &&
    ++	git merge-file file file.orig file.9 &&
    ++	git diff >expect &&
    ++	git merge --no-commit --autostash c2 &&
    ++	git stash show -p MERGE_AUTOSTASH >actual &&
    ++	test_cmp expect actual &&
    ++	git commit 2>err &&
    ++	test_i18ngrep "Applied autostash." err &&
    ++	git show HEAD:file >merge-result &&
    ++	test_cmp result.1-5 merge-result &&
    ++	test_cmp result.1-5-9 file
    ++'
    ++
    ++test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '
    ++	git reset --hard c1 &&
    ++	git merge-file file file.orig file.9 &&
    ++	git diff >expect &&
    ++	git merge --no-commit --autostash c2 &&
    ++	git stash show -p MERGE_AUTOSTASH >actual &&
    ++	test_cmp expect actual &&
    ++	git merge --abort 2>err &&
    ++	test_i18ngrep "Applied autostash." err &&
    ++	git diff >actual &&
    ++	test_cmp expect actual
    ++'
    ++
    ++test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '
    ++	git reset --hard c1 &&
    ++	git merge-file file file.orig file.9 &&
    ++	git diff >expect &&
    ++	git merge --no-commit --autostash c2 &&
    ++	git stash show -p MERGE_AUTOSTASH >actual &&
    ++	test_cmp expect actual &&
    ++	git reset --hard 2>err &&
    ++	test_i18ngrep "Autostash exists; creating a new stash entry." err &&
    ++	git diff --exit-code
    ++'
    ++
     +test_expect_success 'merge with conflicted --autostash changes' '
     +	git reset --hard c1 &&
     +	git merge-file file file.orig file.9y &&
16:  c70643ca9f = 18:  72aa56516e t5520: make test_pull_autostash() accept expect_parent_num
17:  6ebae1d758 = 19:  17caf6d66f pull: pass --autostash to merge
-- 
2.25.0.114.g5b0ca878e0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 01/19] Makefile: ASCII-sort += lists
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-03-21  9:21     ` [PATCH v3 02/19] t7600: use test_write_lines() Denton Liu
                       ` (18 subsequent siblings)
  19 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Makefile | 77 ++++++++++++++++++++++++++++----------------------------
 1 file changed, 39 insertions(+), 38 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 02/19] t7600: use test_write_lines()
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
  2020-03-21  9:21     ` [PATCH v3 01/19] Makefile: ASCII-sort += lists Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-03-21  9:21     ` [PATCH v3 03/19] sequencer: use file strbuf for read_oneliner() Denton Liu
                       ` (17 subsequent siblings)
  19 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 t/t7600-merge.sh | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 03/19] sequencer: use file strbuf for read_oneliner()
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
  2020-03-21  9:21     ` [PATCH v3 01/19] Makefile: ASCII-sort += lists Denton Liu
  2020-03-21  9:21     ` [PATCH v3 02/19] t7600: use test_write_lines() Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-04-02 13:34       ` Phillip Wood
  2020-03-21  9:21     ` [PATCH v3 04/19] sequencer: make read_oneliner() accept flags Denton Liu
                       ` (16 subsequent siblings)
  19 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index e528225e78..c49fe76fe6 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -431,26 +431,28 @@ static int write_message(const void *buf, size_t len, const char *filename,
 static int read_oneliner(struct strbuf *buf,
 	const char *path, int skip_if_empty)
 {
-	int orig_len = buf->len;
+	int ret = 0;
+	struct strbuf file_buf = STRBUF_INIT;
 
 	if (!file_exists(path))
 		return 0;
 
-	if (strbuf_read_file(buf, path, 0) < 0) {
+	if (strbuf_read_file(&file_buf, path, 0) < 0) {
 		warning_errno(_("could not read '%s'"), path);
-		return 0;
+		goto done;
 	}
 
-	if (buf->len > orig_len && buf->buf[buf->len - 1] == '\n') {
-		if (--buf->len > orig_len && buf->buf[buf->len - 1] == '\r')
-			--buf->len;
-		buf->buf[buf->len] = '\0';
-	}
+	strbuf_trim_trailing_newline(&file_buf);
 
-	if (skip_if_empty && buf->len == orig_len)
-		return 0;
+	if (skip_if_empty && !file_buf.len)
+		goto done;
 
-	return 1;
+	strbuf_addbuf(buf, &file_buf);
+	ret = 1;
+
+done:
+	strbuf_release(&file_buf);
+	return ret;
 }
 
 static struct tree *empty_tree(struct repository *r)
-- 
2.25.0.114.g5b0ca878e0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 04/19] sequencer: make read_oneliner() accept flags
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (2 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 03/19] sequencer: use file strbuf for read_oneliner() Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-03-21  9:21     ` [PATCH v3 05/19] sequencer: configurably warn on non-existent files Denton Liu
                       ` (15 subsequent siblings)
  19 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

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

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

and manually break up long lines in the result.

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 05/19] sequencer: configurably warn on non-existent files
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (3 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 04/19] sequencer: make read_oneliner() accept flags Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-04-02 13:39       ` Phillip Wood
  2020-03-21  9:21     ` [PATCH v3 06/19] sequencer: make read_oneliner() extern Denton Liu
                       ` (14 subsequent siblings)
  19 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 06/19] sequencer: make read_oneliner() extern
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (4 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 05/19] sequencer: configurably warn on non-existent files Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-03-21  9:21     ` [PATCH v3 07/19] rebase: use read_oneliner() Denton Liu
                       ` (13 subsequent siblings)
  19 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 14 +-------------
 sequencer.h | 14 ++++++++++++++
 2 files changed, 15 insertions(+), 13 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 07/19] rebase: use read_oneliner()
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (5 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 06/19] sequencer: make read_oneliner() extern Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-04-02 13:41       ` Phillip Wood
  2020-03-21  9:21     ` [PATCH v3 08/19] sequencer: make apply_rebase() accept a path Denton Liu
                       ` (12 subsequent siblings)
  19 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

This was done with the following Coccinelle script

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

and long lines were manually broken up.

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 37 +++++++++++++++++--------------------
 1 file changed, 17 insertions(+), 20 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 08/19] sequencer: make apply_rebase() accept a path
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (6 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 07/19] rebase: use read_oneliner() Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-03-21  9:21     ` [PATCH v3 09/19] rebase: use apply_autostash() from sequencer.c Denton Liu
                       ` (11 subsequent siblings)
  19 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 09/19] rebase: use apply_autostash() from sequencer.c
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (7 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 08/19] sequencer: make apply_rebase() accept a path Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-04-02 14:59       ` Phillip Wood
  2020-03-21  9:21     ` [PATCH v3 10/19] rebase: generify reset_head() Denton Liu
                       ` (10 subsequent siblings)
  19 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 49 ++----------------------------------------------
 sequencer.c      |  2 +-
 sequencer.h      |  2 ++
 3 files changed, 5 insertions(+), 48 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 10/19] rebase: generify reset_head()
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (8 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 09/19] rebase: use apply_autostash() from sequencer.c Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-03-21  9:21     ` [PATCH v3 11/19] reset: extract reset_head() from rebase Denton Liu
                       ` (9 subsequent siblings)
  19 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 64 ++++++++++++++++++++++++++++--------------------
 1 file changed, 37 insertions(+), 27 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 11/19] reset: extract reset_head() from rebase
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (9 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 10/19] rebase: generify reset_head() Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-04-02 15:04       ` Phillip Wood
  2020-03-21  9:21     ` [PATCH v3 12/19] rebase: extract create_autostash() Denton Liu
                       ` (8 subsequent siblings)
  19 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Makefile         |   1 +
 builtin/rebase.c | 139 +---------------------------------------------
 reset.c          | 140 +++++++++++++++++++++++++++++++++++++++++++++++
 reset.h          |  20 +++++++
 4 files changed, 162 insertions(+), 138 deletions(-)
 create mode 100644 reset.c
 create mode 100644 reset.h

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 12/19] rebase: extract create_autostash()
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (10 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 11/19] reset: extract reset_head() from rebase Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-03-21  9:21     ` [PATCH v3 13/19] rebase: generify create_autostash() Denton Liu
                       ` (7 subsequent siblings)
  19 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 94 +++++++++++++++++++++++++-----------------------
 1 file changed, 50 insertions(+), 44 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 13/19] rebase: generify create_autostash()
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (11 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 12/19] rebase: extract create_autostash() Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-03-21  9:21     ` [PATCH v3 14/19] sequencer: extract perform_autostash() from rebase Denton Liu
                       ` (6 subsequent siblings)
  19 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 14/19] sequencer: extract perform_autostash() from rebase
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (12 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 13/19] rebase: generify create_autostash() Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-03-21  9:21     ` [PATCH v3 15/19] sequencer: unlink autostash in apply_autostash() Denton Liu
                       ` (5 subsequent siblings)
  19 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 49 -----------------------------------------------
 sequencer.c      | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |  2 ++
 3 files changed, 52 insertions(+), 49 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index 3370c8f2c4..b6821be62b 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1274,55 +1274,6 @@ static int check_exec_cmd(const char *cmd)
 	return 0;
 }
 
-static void create_autostash(struct repository *r, const char *path,
-			     const char *default_reflog_action)
-{
-	struct strbuf buf = STRBUF_INIT;
-	struct lock_file lock_file = LOCK_INIT;
-	int fd;
-
-	fd = repo_hold_locked_index(r, &lock_file, 0);
-	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
-	if (0 <= fd)
-		repo_update_index_if_able(r, &lock_file);
-	rollback_lock_file(&lock_file);
-
-	if (has_unstaged_changes(r, 1) ||
-	    has_uncommitted_changes(r, 1)) {
-		struct child_process stash = CHILD_PROCESS_INIT;
-		struct object_id oid;
-
-		argv_array_pushl(&stash.args,
-				 "stash", "create", "autostash", NULL);
-		stash.git_cmd = 1;
-		stash.no_stdin = 1;
-		strbuf_reset(&buf);
-		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
-			die(_("Cannot autostash"));
-		strbuf_trim_trailing_newline(&buf);
-		if (get_oid(buf.buf, &oid))
-			die(_("Unexpected stash response: '%s'"),
-			    buf.buf);
-		strbuf_reset(&buf);
-		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
-
-		if (safe_create_leading_directories_const(path))
-			die(_("Could not create directory for '%s'"),
-			    path);
-		write_file(path, "%s", oid_to_hex(&oid));
-		printf(_("Created autostash: %s\n"), buf.buf);
-		if (reset_head(r, NULL, "reset --hard",
-			       NULL, RESET_HEAD_HARD, NULL, NULL,
-			       default_reflog_action) < 0)
-			die(_("could not reset --hard"));
-
-		if (discard_index(r->index) < 0 ||
-			repo_read_index(r) < 0)
-			die(_("could not read index"));
-	}
-	strbuf_release(&buf);
-}
-
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
 	struct rebase_options options = REBASE_OPTIONS_INIT;
diff --git a/sequencer.c b/sequencer.c
index b52668f8de..b2951843a1 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -32,6 +32,7 @@
 #include "alias.h"
 #include "commit-reach.h"
 #include "rebase-interactive.h"
+#include "reset.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -3655,6 +3656,55 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
 	return -1;
 }
 
+void create_autostash(struct repository *r, const char *path,
+		      const char *default_reflog_action)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct lock_file lock_file = LOCK_INIT;
+	int fd;
+
+	fd = repo_hold_locked_index(r, &lock_file, 0);
+	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
+	if (0 <= fd)
+		repo_update_index_if_able(r, &lock_file);
+	rollback_lock_file(&lock_file);
+
+	if (has_unstaged_changes(r, 1) ||
+	    has_uncommitted_changes(r, 1)) {
+		struct child_process stash = CHILD_PROCESS_INIT;
+		struct object_id oid;
+
+		argv_array_pushl(&stash.args,
+				 "stash", "create", "autostash", NULL);
+		stash.git_cmd = 1;
+		stash.no_stdin = 1;
+		strbuf_reset(&buf);
+		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
+			die(_("Cannot autostash"));
+		strbuf_trim_trailing_newline(&buf);
+		if (get_oid(buf.buf, &oid))
+			die(_("Unexpected stash response: '%s'"),
+			    buf.buf);
+		strbuf_reset(&buf);
+		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
+
+		if (safe_create_leading_directories_const(path))
+			die(_("Could not create directory for '%s'"),
+			    path);
+		write_file(path, "%s", oid_to_hex(&oid));
+		printf(_("Created autostash: %s\n"), buf.buf);
+		if (reset_head(r, NULL, "reset --hard",
+			       NULL, RESET_HEAD_HARD, NULL, NULL,
+			       default_reflog_action) < 0)
+			die(_("could not reset --hard"));
+
+		if (discard_index(r->index) < 0 ||
+			repo_read_index(r) < 0)
+			die(_("could not read index"));
+	}
+	strbuf_release(&buf);
+}
+
 int apply_autostash(const char *path)
 {
 	struct strbuf stash_sha1 = STRBUF_INIT;
diff --git a/sequencer.h b/sequencer.h
index bab910f012..44ecfee7f3 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,
 			 const struct commit *current_head,
 			 const struct object_id *new_head);
 
+void create_autostash(struct repository *r, const char *path,
+		      const char *default_reflog_action);
 int apply_autostash(const char *path);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
-- 
2.25.0.114.g5b0ca878e0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 15/19] sequencer: unlink autostash in apply_autostash()
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (13 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 14/19] sequencer: extract perform_autostash() from rebase Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-03-21  9:21     ` [PATCH v3 16/19] sequencer: implement save_autostash() Denton Liu
                       ` (4 subsequent siblings)
  19 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/sequencer.c b/sequencer.c
index b2951843a1..81b6c700d1 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -3746,6 +3746,7 @@ int apply_autostash(const char *path)
 				  " \"git stash drop\" at any time.\n"));
 	}
 
+	unlink(path);
 	strbuf_release(&stash_sha1);
 	return ret;
 }
-- 
2.25.0.114.g5b0ca878e0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 16/19] sequencer: implement save_autostash()
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (14 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 15/19] sequencer: unlink autostash in apply_autostash() Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-04-02 15:10       ` Phillip Wood
  2020-03-21  9:21     ` [PATCH v3 17/19] merge: teach --autostash option Denton Liu
                       ` (3 subsequent siblings)
  19 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 37 +++++++++++++++++++++++++++----------
 sequencer.h |  1 +
 2 files changed, 28 insertions(+), 10 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 17/19] merge: teach --autostash option
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (15 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 16/19] sequencer: implement save_autostash() Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-04-02 15:24       ` Phillip Wood
  2020-03-21  9:21     ` [PATCH v3 18/19] t5520: make test_pull_autostash() accept expect_parent_num Denton Liu
                       ` (2 subsequent siblings)
  19 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

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

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

but now, that is reduced to

	git fetch ...
	git merge --autostash FETCH_HEAD

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

Suggested-by: Alban Gruin <alban.gruin@gmail.com>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Documentation/config/merge.txt  |  10 +++
 Documentation/merge-options.txt |   8 +++
 branch.c                        |   1 +
 builtin/commit.c                |   2 +
 builtin/merge.c                 |  17 ++++++
 builtin/reset.c                 |   7 ++-
 path.c                          |   1 +
 path.h                          |   4 +-
 t/t3033-merge-toplevel.sh       |  22 +++++++
 t/t7600-merge.sh                | 104 ++++++++++++++++++++++++++++++++
 10 files changed, 174 insertions(+), 2 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 18/19] t5520: make test_pull_autostash() accept expect_parent_num
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (16 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 17/19] merge: teach --autostash option Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-03-21  9:21     ` [PATCH v3 19/19] pull: pass --autostash to merge Denton Liu
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
  19 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 t/t5520-pull.sh | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 2f86fca042..f610dc14de 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -10,11 +10,13 @@ modify () {
 }
 
 test_pull_autostash () {
+	expect_parent_num="$1" &&
+	shift &&
 	git reset --hard before-rebase &&
 	echo dirty >new_file &&
 	git add new_file &&
 	git pull "$@" . copy &&
-	test_cmp_rev HEAD^ copy &&
+	test_cmp_rev HEAD^"$expect_parent_num" copy &&
 	echo dirty >expect &&
 	test_cmp expect new_file &&
 	echo "modified again" >expect &&
@@ -369,22 +371,22 @@ test_expect_success '--rebase fails with multiple branches' '
 
 test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' '
 	test_config rebase.autostash true &&
-	test_pull_autostash --rebase
+	test_pull_autostash 1 --rebase
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash=true' '
 	test_config rebase.autostash true &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash=false' '
 	test_config rebase.autostash false &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash unset' '
 	test_unconfig rebase.autostash &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '
@@ -422,7 +424,7 @@ test_expect_success 'pull.rebase' '
 
 test_expect_success 'pull --autostash & pull.rebase=true' '
 	test_config pull.rebase true &&
-	test_pull_autostash --autostash
+	test_pull_autostash 1 --autostash
 '
 
 test_expect_success 'pull --no-autostash & pull.rebase=true' '
-- 
2.25.0.114.g5b0ca878e0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v3 19/19] pull: pass --autostash to merge
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (17 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 18/19] t5520: make test_pull_autostash() accept expect_parent_num Denton Liu
@ 2020-03-21  9:21     ` Denton Liu
  2020-04-02 16:07       ` Phillip Wood
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
  19 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-03-21  9:21 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Documentation/git-pull.txt      |  9 -------
 Documentation/merge-options.txt |  4 +--
 builtin/pull.c                  |  9 ++++---
 t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------
 4 files changed, 42 insertions(+), 23 deletions(-)

diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index dfb901f8b8..ba3772de9f 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.
 --no-rebase::
 	Override earlier --rebase.
 
---autostash::
---no-autostash::
-	Before starting rebase, stash local modifications away (see
-	linkgit:git-stash[1]) if needed, and apply the stash entry when
-	done. `--no-autostash` is useful to override the `rebase.autoStash`
-	configuration variable (see linkgit:git-config[1]).
-+
-This option is only valid when "--rebase" is used.
-
 Options related to fetching
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 34493eb58b..ae56cca826 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -155,6 +155,8 @@ ifndef::git-pull[]
 	Note that not all merge strategies may support progress
 	reporting.
 
+endif::git-pull[]
+
 --autostash::
 --no-autostash::
 	Automatically create a temporary stash entry before the operation
@@ -163,8 +165,6 @@ ifndef::git-pull[]
 	with care: the final stash application after a successful
 	rebase might result in non-trivial conflicts.
 
-endif::git-pull[]
-
 --allow-unrelated-histories::
 	By default, `git merge` command refuses to merge histories
 	that do not share a common ancestor.  This option can be
diff --git a/builtin/pull.c b/builtin/pull.c
index 3e624d1e00..9beb4841d1 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -163,7 +163,7 @@ static struct option pull_options[] = {
 		N_("verify that the named commit has a valid GPG signature"),
 		PARSE_OPT_NOARG),
 	OPT_BOOL(0, "autostash", &opt_autostash,
-		N_("automatically stash/stash pop before and after rebase")),
+		N_("automatically stash/stash pop before and after")),
 	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
 		N_("merge strategy to use"),
 		0),
@@ -661,6 +661,10 @@ static int run_merge(void)
 	argv_array_pushv(&args, opt_strategy_opts.argv);
 	if (opt_gpg_sign)
 		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");
 	if (opt_allow_unrelated_histories > 0)
 		argv_array_push(&args, "--allow-unrelated-histories");
 
@@ -908,9 +912,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 	if (get_oid("HEAD", &orig_head))
 		oidclr(&orig_head);
 
-	if (!opt_rebase && opt_autostash != -1)
-		die(_("--[no-]autostash option is only valid with --rebase."));
-
 	autostash = config_autostash;
 	if (opt_rebase) {
 		if (opt_autostash != -1)
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index f610dc14de..37535d63a9 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -28,7 +28,7 @@ test_pull_autostash_fail () {
 	echo dirty >new_file &&
 	git add new_file &&
 	test_must_fail git pull "$@" . copy 2>err &&
-	test_i18ngrep "uncommitted changes." err
+	test_i18ngrep "\(uncommitted changes.\)\|\(overwritten by merge:\)" err
 }
 
 test_expect_success setup '
@@ -404,13 +404,40 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
 	test_pull_autostash_fail --rebase --no-autostash
 '
 
-for i in --autostash --no-autostash
-do
-	test_expect_success "pull $i (without --rebase) is illegal" '
-		test_must_fail git pull $i . copy 2>err &&
-		test_i18ngrep "only valid with --rebase" err
-	'
-done
+test_expect_success 'pull succeeds with dirty working directory and merge.autostash set' '
+	test_config merge.autostash true &&
+	test_pull_autostash 2
+'
+
+test_expect_success 'pull --autostash & merge.autostash=true' '
+	test_config merge.autostash true &&
+	test_pull_autostash 2 --autostash
+'
+
+test_expect_success 'pull --autostash & merge.autostash=false' '
+	test_config merge.autostash false &&
+	test_pull_autostash 2 --autostash
+'
+
+test_expect_success 'pull --autostash & merge.autostash unset' '
+	test_unconfig merge.autostash &&
+	test_pull_autostash 2 --autostash
+'
+
+test_expect_success 'pull --no-autostash & merge.autostash=true' '
+	test_config merge.autostash true &&
+	test_pull_autostash_fail --no-autostash
+'
+
+test_expect_success 'pull --no-autostash & merge.autostash=false' '
+	test_config merge.autostash false &&
+	test_pull_autostash_fail --no-autostash
+'
+
+test_expect_success 'pull --no-autostash & merge.autostash unset' '
+	test_unconfig merge.autostash &&
+	test_pull_autostash_fail --no-autostash
+'
 
 test_expect_success 'pull.rebase' '
 	git reset --hard before-rebase &&
-- 
2.25.0.114.g5b0ca878e0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 03/19] sequencer: use file strbuf for read_oneliner()
  2020-03-21  9:21     ` [PATCH v3 03/19] sequencer: use file strbuf for read_oneliner() Denton Liu
@ 2020-04-02 13:34       ` Phillip Wood
  0 siblings, 0 replies; 152+ messages in thread
From: Phillip Wood @ 2020-04-02 13:34 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 21/03/2020 09:21, Denton Liu wrote:
> In the original read_oneliner logic, we duplicated the logic for
> strbuf_trim_trailing_newline() with one exception: instead of preventing
> buffer accesses below index 0, it would prevent buffer accesses below
> index `orig_len`. Although this is correct, it isn't worth having the
> duplicated logic around.
> 
> Add a second strbuf to which files are read and run
> strbuf_trim_trailing_newline() directly on this strbuf then concatenate
> this strbuf with the argument strbuf at the end of the function. The
> function's external behaviour is unchanged.

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

Best Wishes

Phillip

> 
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>   sequencer.c | 24 +++++++++++++-----------
>   1 file changed, 13 insertions(+), 11 deletions(-)
> 
> diff --git a/sequencer.c b/sequencer.c
> index e528225e78..c49fe76fe6 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -431,26 +431,28 @@ static int write_message(const void *buf, size_t len, const char *filename,
>   static int read_oneliner(struct strbuf *buf,
>   	const char *path, int skip_if_empty)
>   {
> -	int orig_len = buf->len;
> +	int ret = 0;
> +	struct strbuf file_buf = STRBUF_INIT;
>   
>   	if (!file_exists(path))
>   		return 0;
>   
> -	if (strbuf_read_file(buf, path, 0) < 0) {
> +	if (strbuf_read_file(&file_buf, path, 0) < 0) {
>   		warning_errno(_("could not read '%s'"), path);
> -		return 0;
> +		goto done;
>   	}
>   
> -	if (buf->len > orig_len && buf->buf[buf->len - 1] == '\n') {
> -		if (--buf->len > orig_len && buf->buf[buf->len - 1] == '\r')
> -			--buf->len;
> -		buf->buf[buf->len] = '\0';
> -	}
> +	strbuf_trim_trailing_newline(&file_buf);
>   
> -	if (skip_if_empty && buf->len == orig_len)
> -		return 0;
> +	if (skip_if_empty && !file_buf.len)
> +		goto done;
>   
> -	return 1;
> +	strbuf_addbuf(buf, &file_buf);
> +	ret = 1;
> +
> +done:
> +	strbuf_release(&file_buf);
> +	return ret;
>   }
>   
>   static struct tree *empty_tree(struct repository *r)
> 

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 05/19] sequencer: configurably warn on non-existent files
  2020-03-21  9:21     ` [PATCH v3 05/19] sequencer: configurably warn on non-existent files Denton Liu
@ 2020-04-02 13:39       ` Phillip Wood
  0 siblings, 0 replies; 152+ messages in thread
From: Phillip Wood @ 2020-04-02 13:39 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 21/03/2020 09:21, Denton Liu wrote:
> In the future, we plan on externing read_oneliner(). Future users of
> read_oneliner() will want the ability to output warnings in the event
> that the `path` doesn't exist. Introduce the
> `READ_ONELINER_WARN_NON_EXISTENCE`

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

> flag which, if active, would issue a
> warning when a file doesn't exist by skipping the `!file_exists()` check
> and letting `strbuf_read_file()` handle that case.
> 
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>   sequencer.c | 3 ++-
>   1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/sequencer.c b/sequencer.c
> index abb2a21e9d..92e8d38290 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename,
>   }
>   
>   #define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
> +#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
>   
>   /*
>    * Reads a file that was presumably written by a shell script, i.e. with an
> @@ -436,7 +437,7 @@ static int read_oneliner(struct strbuf *buf,
>   	int ret = 0;
>   	struct strbuf file_buf = STRBUF_INIT;
>   
> -	if (!file_exists(path))
> +	if (!(flags & READ_ONELINER_WARN_NON_EXISTENCE) && !file_exists(path))
>   		return 0;

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

Best Wishes

Phillip

>   	if (strbuf_read_file(&file_buf, path, 0) < 0) {
> 

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 07/19] rebase: use read_oneliner()
  2020-03-21  9:21     ` [PATCH v3 07/19] rebase: use read_oneliner() Denton Liu
@ 2020-04-02 13:41       ` Phillip Wood
  0 siblings, 0 replies; 152+ messages in thread
From: Phillip Wood @ 2020-04-02 13:41 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 21/03/2020 09:21, Denton Liu wrote:
> Since in sequencer.c, read_one() basically duplicates the functionality
> of read_oneliner(), reduce code duplication by replacing read_one() with
> read_oneliner().
> 
> This was done with the following Coccinelle script
> 
> 	@@
> 	expression a, b;
> 	@@
> 	- read_one(a, b)
> 	+ !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)
> 
> and long lines were manually broken up.

It's great to see read_one() going away

Thanks

Phillip

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 09/19] rebase: use apply_autostash() from sequencer.c
  2020-03-21  9:21     ` [PATCH v3 09/19] rebase: use apply_autostash() from sequencer.c Denton Liu
@ 2020-04-02 14:59       ` Phillip Wood
  0 siblings, 0 replies; 152+ messages in thread
From: Phillip Wood @ 2020-04-02 14:59 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 21/03/2020 09:21, Denton Liu wrote:
> The apply_autostash() function in builtin/rebase.c is similar enough to
> the apply_autostash() function in sequencer.c that they are almost
> interchangeable.

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

Best Wishes

Phillip

  Make the sequencer.c version extern and use it in
> rebase.
> 
> The rebase version was introduced in 6defce2b02 (builtin rebase: support
> `--autostash` option, 2018-09-04) as part of the shell to C conversion.
> It opted to duplicate the function because, at the time, there was
> another in-progress project converting interactive rebase from shell to
> C as well and they did not want to clash with them by refactoring
> sequencer.c version of apply_autostash(). Since both efforts are long
> done, we can freely combine them together now.
> 
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>   builtin/rebase.c | 49 ++----------------------------------------------
>   sequencer.c      |  2 +-
>   sequencer.h      |  2 ++
>   3 files changed, 5 insertions(+), 48 deletions(-)
> 
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index 1146463099..ceb115247a 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -712,51 +712,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)
>   	return 0;
>   }
>   
> -static int apply_autostash(struct rebase_options *opts)
> -{
> -	const char *path = state_dir_path("autostash", opts);
> -	struct strbuf autostash = STRBUF_INIT;
> -	struct child_process stash_apply = CHILD_PROCESS_INIT;
> -
> -	if (!file_exists(path))
> -		return 0;
> -
> -	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
> -		return error(_("Could not read '%s'"), path);
> -	/* Ensure that the hash is not mistaken for a number */
> -	strbuf_addstr(&autostash, "^0");
> -	argv_array_pushl(&stash_apply.args,
> -			 "stash", "apply", autostash.buf, NULL);
> -	stash_apply.git_cmd = 1;
> -	stash_apply.no_stderr = stash_apply.no_stdout =
> -		stash_apply.no_stdin = 1;
> -	if (!run_command(&stash_apply))
> -		printf(_("Applied autostash.\n"));
> -	else {
> -		struct argv_array args = ARGV_ARRAY_INIT;
> -		int res = 0;
> -
> -		argv_array_pushl(&args,
> -				 "stash", "store", "-m", "autostash", "-q",
> -				 autostash.buf, NULL);
> -		if (run_command_v_opt(args.argv, RUN_GIT_CMD))
> -			res = error(_("Cannot store %s"), autostash.buf);
> -		argv_array_clear(&args);
> -		strbuf_release(&autostash);
> -		if (res)
> -			return res;
> -
> -		fprintf(stderr,
> -			_("Applying autostash resulted in conflicts.\n"
> -			  "Your changes are safe in the stash.\n"
> -			  "You can run \"git stash pop\" or \"git stash drop\" "
> -			  "at any time.\n"));
> -	}
> -
> -	strbuf_release(&autostash);
> -	return 0;
> -}
> -
>   static int finish_rebase(struct rebase_options *opts)
>   {
>   	struct strbuf dir = STRBUF_INIT;
> @@ -764,7 +719,7 @@ static int finish_rebase(struct rebase_options *opts)
>   	int ret = 0;
>   
>   	delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
> -	apply_autostash(opts);
> +	apply_autostash(state_dir_path("autostash", opts));
>   	close_object_store(the_repository->objects);
>   	/*
>   	 * We ignore errors in 'gc --auto', since the
> @@ -1209,7 +1164,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
>   	} else if (status == 2) {
>   		struct strbuf dir = STRBUF_INIT;
>   
> -		apply_autostash(opts);
> +		apply_autostash(state_dir_path("autostash", opts));
>   		strbuf_addstr(&dir, opts->state_dir);
>   		remove_dir_recursively(&dir, 0);
>   		strbuf_release(&dir);
> diff --git a/sequencer.c b/sequencer.c
> index ad40a8b3fc..b52668f8de 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -3655,7 +3655,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
>   	return -1;
>   }
>   
> -static int apply_autostash(const char *path)
> +int apply_autostash(const char *path)
>   {
>   	struct strbuf stash_sha1 = STRBUF_INIT;
>   	struct child_process child = CHILD_PROCESS_INIT;
> diff --git a/sequencer.h b/sequencer.h
> index 5c9662a60a..bab910f012 100644
> --- a/sequencer.h
> +++ b/sequencer.h
> @@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,
>   			 const struct commit *current_head,
>   			 const struct object_id *new_head);
>   
> +int apply_autostash(const char *path);
> +
>   #define SUMMARY_INITIAL_COMMIT   (1 << 0)
>   #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
>   void print_commit_summary(struct repository *repo,
> 

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 11/19] reset: extract reset_head() from rebase
  2020-03-21  9:21     ` [PATCH v3 11/19] reset: extract reset_head() from rebase Denton Liu
@ 2020-04-02 15:04       ` Phillip Wood
  0 siblings, 0 replies; 152+ messages in thread
From: Phillip Wood @ 2020-04-02 15:04 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 21/03/2020 09:21, Denton Liu wrote:
> Continue the process of lib-ifying the autostash code. In a future
> commit, this will be used to implement `--autostash` in other builtins.
> 
> This patch is best viewed with `--color-moved`.

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

Best Wishes

Phillip

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 16/19] sequencer: implement save_autostash()
  2020-03-21  9:21     ` [PATCH v3 16/19] sequencer: implement save_autostash() Denton Liu
@ 2020-04-02 15:10       ` Phillip Wood
  0 siblings, 0 replies; 152+ messages in thread
From: Phillip Wood @ 2020-04-02 15:10 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 21/03/2020 09:21, Denton Liu wrote:
> Extract common functionality of apply_autostash() into
> apply_save_autostash() and use it to implement save_autostash(). This
> function will be used in a future commit.
> 
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>   sequencer.c | 37 +++++++++++++++++++++++++++----------
>   sequencer.h |  1 +
>   2 files changed, 28 insertions(+), 10 deletions(-)
> 
> diff --git a/sequencer.c b/sequencer.c
> index 81b6c700d1..d67d47b332 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -3705,7 +3705,7 @@ void create_autostash(struct repository *r, const char *path,
>   	strbuf_release(&buf);
>   }
>   
> -int apply_autostash(const char *path)
> +static int apply_save_autostash(const char *path, int attempt_apply)
>   {
>   	struct strbuf stash_sha1 = STRBUF_INIT;
>   	struct child_process child = CHILD_PROCESS_INIT;
> @@ -3718,13 +3718,17 @@ int apply_autostash(const char *path)
>   	}
>   	strbuf_trim(&stash_sha1);
>   
> -	child.git_cmd = 1;
> -	child.no_stdout = 1;
> -	child.no_stderr = 1;
> -	argv_array_push(&child.args, "stash");
> -	argv_array_push(&child.args, "apply");
> -	argv_array_push(&child.args, stash_sha1.buf);
> -	if (!run_command(&child))
> +	if (attempt_apply) {
> +		child.git_cmd = 1;
> +		child.no_stdout = 1;
> +		child.no_stderr = 1;
> +		argv_array_push(&child.args, "stash");
> +		argv_array_push(&child.args, "apply");
> +		argv_array_push(&child.args, stash_sha1.buf);
> +		ret = run_command(&child);
> +	}
> +
> +	if (attempt_apply && !ret)

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

>   		fprintf(stderr, _("Applied autostash.\n"));
>   	else {
>   		struct child_process store = CHILD_PROCESS_INIT;
> @@ -3740,10 +3744,13 @@ int apply_autostash(const char *path)
>   			ret = error(_("cannot store %s"), stash_sha1.buf);
>   		else
>   			fprintf(stderr,
> -				_("Applying autostash resulted in conflicts.\n"
> +				_("%s\n"
>   				  "Your changes are safe in the stash.\n"
>   				  "You can run \"git stash pop\" or"
> -				  " \"git stash drop\" at any time.\n"));
> +				  " \"git stash drop\" at any time.\n"),
> +				attempt_apply ?
> +				_("Applying autostash resulted in conflicts.") :
> +				_("Autostash exists; creating a new stash entry."));

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

Best Wishes

Phillip

>   	}
>   
>   	unlink(path);
> @@ -3751,6 +3758,16 @@ int apply_autostash(const char *path)
>   	return ret;
>   }
>   
> +int save_autostash(const char *path)
> +{
> +	return apply_save_autostash(path, 0);
> +}
> +
> +int apply_autostash(const char *path)
> +{
> +	return apply_save_autostash(path, 1);
> +}
> +
>   static const char *reflog_message(struct replay_opts *opts,
>   	const char *sub_action, const char *fmt, ...)
>   {
> diff --git a/sequencer.h b/sequencer.h
> index 44ecfee7f3..1cb0e4e4b7 100644
> --- a/sequencer.h
> +++ b/sequencer.h
> @@ -193,6 +193,7 @@ void commit_post_rewrite(struct repository *r,
>   
>   void create_autostash(struct repository *r, const char *path,
>   		      const char *default_reflog_action);
> +int save_autostash(const char *path);
>   int apply_autostash(const char *path);
>   
>   #define SUMMARY_INITIAL_COMMIT   (1 << 0)
> 

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 17/19] merge: teach --autostash option
  2020-03-21  9:21     ` [PATCH v3 17/19] merge: teach --autostash option Denton Liu
@ 2020-04-02 15:24       ` Phillip Wood
  2020-04-03 10:31         ` Denton Liu
  0 siblings, 1 reply; 152+ messages in thread
From: Phillip Wood @ 2020-04-02 15:24 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 21/03/2020 09:21, Denton Liu wrote:
> In rebase, one can pass the `--autostash` option to cause the worktree
> to be automatically stashed before continuing with the rebase. This
> option is missing in merge, however.
> 
> Implement the `--autostash` option and corresponding `merge.autoStash`
> option in merge which stashes before merging and then pops after.
> 
> This option is useful when a developer has some local changes on a topic
> branch but they realize that their work depends on another branch.
> Previously, they had to run something like
> 
> 	git fetch ...
> 	git stash push
> 	git merge FETCH_HEAD
> 	git stash pop
> 
> but now, that is reduced to
> 
> 	git fetch ...
> 	git merge --autostash FETCH_HEAD
> 
> When `git reset --hard` is run to abort a merge, the working tree will
> be left in a clean state, as expected, with the autostash pushed onto
> the stash stack.
> 
> Suggested-by: Alban Gruin <alban.gruin@gmail.com>
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>   Documentation/config/merge.txt  |  10 +++
>   Documentation/merge-options.txt |   8 +++
>   branch.c                        |   1 +
>   builtin/commit.c                |   2 +
>   builtin/merge.c                 |  17 ++++++
>   builtin/reset.c                 |   7 ++-
>   path.c                          |   1 +
>   path.h                          |   4 +-
>   t/t3033-merge-toplevel.sh       |  22 +++++++
>   t/t7600-merge.sh                | 104 ++++++++++++++++++++++++++++++++
>   10 files changed, 174 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt
> index 6a313937f8..88b29127bf 100644
> --- a/Documentation/config/merge.txt
> +++ b/Documentation/config/merge.txt
> @@ -70,6 +70,16 @@ merge.stat::
>   	Whether to print the diffstat between ORIG_HEAD and the merge result
>   	at the end of the merge.  True by default.
>   
> +merge.autoStash::
> +	When set to true, automatically create a temporary stash entry
> +	before the operation begins, and apply it after the operation
> +	ends.  This means that you can run rebase

copy and paste error - s/rebase/merge/

> on a dirty worktree.
> +	However, use with care: the final stash application after a
> +	successful rebase might result in non-trivial conflicts.
> +	This option can be overridden by the `--no-autostash` and
> +	`--autostash` options of linkgit:git-merge[1].
> +	Defaults to false.
> +
>   merge.tool::
>   	Controls which merge tool is used by linkgit:git-mergetool[1].
>   	The list below shows the valid built-in values.
> diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
> index 40dc4f5e8c..34493eb58b 100644
> --- a/Documentation/merge-options.txt
> +++ b/Documentation/merge-options.txt
> @@ -155,6 +155,14 @@ ifndef::git-pull[]
>   	Note that not all merge strategies may support progress
>   	reporting.
>   
> +--autostash::
> +--no-autostash::
> +	Automatically create a temporary stash entry before the operation
> +	begins, and apply it after the operation ends.  This means
> +	that you can run rebase on a dirty worktree.  However, use > +	with care: the final stash application after a successful
> +	rebase might result in non-trivial conflicts.

s/rebase/merge/

> +
>   endif::git-pull[]
>   
>   --allow-unrelated-histories::
> diff --git a/branch.c b/branch.c
> index 579494738a..bf2536c70d 100644
> --- a/branch.c
> +++ b/branch.c
> @@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
>   	unlink(git_path_merge_rr(r));
>   	unlink(git_path_merge_msg(r));
>   	unlink(git_path_merge_mode(r));
> +	apply_autostash(git_path_merge_autostash(r));
>   }
>   
>   void remove_branch_state(struct repository *r, int verbose)
> diff --git a/builtin/commit.c b/builtin/commit.c
> index 7ba33a3bec..c11894423a 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>   	unlink(git_path_merge_mode(the_repository));
>   	unlink(git_path_squash_msg(the_repository));
>   
> +	apply_autostash(git_path_merge_autostash(the_repository));
> +

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

>   	if (commit_index_files())
>   		die(_("repository has been updated, but unable to write\n"
>   		      "new_index file. Check that disk is not full and quota is\n"
> diff --git a/builtin/merge.c b/builtin/merge.c
> index d127d2225f..e038bef5ad 100644
> --- a/builtin/merge.c
> +++ b/builtin/merge.c
> @@ -82,6 +82,7 @@ static int show_progress = -1;
>   static int default_to_upstream = 1;
>   static int signoff;
>   static const char *sign_commit;
> +static int autostash;
>   static int no_verify;
>   
>   static struct strategy all_strategy[] = {
> @@ -286,6 +287,8 @@ static struct option builtin_merge_options[] = {
>   	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
>   	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
>   	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
> +	OPT_BOOL(0, "autostash", &autostash,
> +	      N_("automatically stash/stash pop before and after")),

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

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

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

Best Wishes

Phillip

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 19/19] pull: pass --autostash to merge
  2020-03-21  9:21     ` [PATCH v3 19/19] pull: pass --autostash to merge Denton Liu
@ 2020-04-02 16:07       ` Phillip Wood
  0 siblings, 0 replies; 152+ messages in thread
From: Phillip Wood @ 2020-04-02 16:07 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 21/03/2020 09:21, Denton Liu wrote:
> Before, `--autostash` only worked with `git pull --rebase`. However, in
> the last patch, merge learned `--autostash` as well so there's no reason
> why we should have this restriction anymore. Teach pull to pass
> `--autostash` to merge, just like it did for rebase.
> 
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>   Documentation/git-pull.txt      |  9 -------
>   Documentation/merge-options.txt |  4 +--
>   builtin/pull.c                  |  9 ++++---
>   t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------
>   4 files changed, 42 insertions(+), 23 deletions(-)
> 
> diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
> index dfb901f8b8..ba3772de9f 100644
> --- a/Documentation/git-pull.txt
> +++ b/Documentation/git-pull.txt
> @@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.
>   --no-rebase::
>   	Override earlier --rebase.
>   
> ---autostash::
> ---no-autostash::
> -	Before starting rebase, stash local modifications away (see
> -	linkgit:git-stash[1]) if needed, and apply the stash entry when
> -	done. `--no-autostash` is useful to override the `rebase.autoStash`
> -	configuration variable (see linkgit:git-config[1]).
> -+
> -This option is only valid when "--rebase" is used.
> -
>   Options related to fetching
>   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
>   
> diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
> index 34493eb58b..ae56cca826 100644
> --- a/Documentation/merge-options.txt
> +++ b/Documentation/merge-options.txt
> @@ -155,6 +155,8 @@ ifndef::git-pull[]
>   	Note that not all merge strategies may support progress
>   	reporting.
>   
> +endif::git-pull[]
> +
>   --autostash::
>   --no-autostash::
>   	Automatically create a temporary stash entry before the operation
> @@ -163,8 +165,6 @@ ifndef::git-pull[]
>   	with care: the final stash application after a successful
>   	rebase might result in non-trivial conflicts.
>   
> -endif::git-pull[]
> -

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

Best Wishes

Phillip

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 17/19] merge: teach --autostash option
  2020-04-02 15:24       ` Phillip Wood
@ 2020-04-03 10:31         ` Denton Liu
  2020-04-03 10:56           ` Denton Liu
  2020-04-03 13:34           ` Phillip Wood
  0 siblings, 2 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-03 10:31 UTC (permalink / raw)
  To: phillip.wood
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Phillip,

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

On Thu, Apr 02, 2020 at 04:24:54PM +0100, Phillip Wood wrote:
> > diff --git a/branch.c b/branch.c
> > index 579494738a..bf2536c70d 100644
> > --- a/branch.c
> > +++ b/branch.c
> > @@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
> >   	unlink(git_path_merge_rr(r));
> >   	unlink(git_path_merge_msg(r));
> >   	unlink(git_path_merge_mode(r));
> > +	apply_autostash(git_path_merge_autostash(r));
> >   }
> >   void remove_branch_state(struct repository *r, int verbose)
> > diff --git a/builtin/commit.c b/builtin/commit.c
> > index 7ba33a3bec..c11894423a 100644
> > --- a/builtin/commit.c
> > +++ b/builtin/commit.c
> > @@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
> >   	unlink(git_path_merge_mode(the_repository));
> >   	unlink(git_path_squash_msg(the_repository));
> > +	apply_autostash(git_path_merge_autostash(the_repository));
> > +
> 
> It's hard to tell from the limited context but do we want to run
> commit_index_files() before applying the autostash?

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

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

We can _almost_ use remove_branch_state() even! Unfortunately,
remove_merge_branch_state() calls `unlink(git_path_merge_rr(r))` which
we cannot do since repo_rerere() relies on it later. Perhaps we can
can move repo_rerere() earlier?
 
> > diff --git a/builtin/reset.c b/builtin/reset.c
> > index 18228c312e..038c8532eb 100644
> > --- a/builtin/reset.c
> > +++ b/builtin/reset.c
> > @@ -25,6 +25,7 @@
> >   #include "cache-tree.h"
> >   #include "submodule.h"
> >   #include "submodule-config.h"
> > +#include "sequencer.h"
> >   #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
> > @@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
> >   		if (reset_type == HARD && !update_ref_status && !quiet)
> >   			print_new_head_line(lookup_commit_reference(the_repository, &oid));
> >   	}
> > -	if (!pathspec.nr)
> > +	if (!pathspec.nr) {
> > +		if (reset_type == HARD)
> > +			save_autostash(git_path_merge_autostash(the_repository));
> > +
> >   		remove_branch_state(the_repository, 0);
> 
> This removes the autostash file for all reset types but we only keep the
> stash in the case of 'reset --hard' which is confusing.

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

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

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

Thanks,

Denton

> Best Wishes
> 
> Phillip
> 
> > +	}
> >   	return update_ref_status;
> >   }

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 17/19] merge: teach --autostash option
  2020-04-03 10:31         ` Denton Liu
@ 2020-04-03 10:56           ` Denton Liu
  2020-04-03 13:09             ` Phillip Wood
  2020-04-03 13:34           ` Phillip Wood
  1 sibling, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-04-03 10:56 UTC (permalink / raw)
  To: phillip.wood
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Junio C Hamano

On Fri, Apr 03, 2020 at 06:31:26AM -0400, Denton Liu wrote:
> > > diff --git a/builtin/reset.c b/builtin/reset.c
> > > index 18228c312e..038c8532eb 100644
> > > --- a/builtin/reset.c
> > > +++ b/builtin/reset.c
> > > @@ -25,6 +25,7 @@
> > >   #include "cache-tree.h"
> > >   #include "submodule.h"
> > >   #include "submodule-config.h"
> > > +#include "sequencer.h"
> > >   #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
> > > @@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
> > >   		if (reset_type == HARD && !update_ref_status && !quiet)
> > >   			print_new_head_line(lookup_commit_reference(the_repository, &oid));
> > >   	}
> > > -	if (!pathspec.nr)
> > > +	if (!pathspec.nr) {
> > > +		if (reset_type == HARD)
> > > +			save_autostash(git_path_merge_autostash(the_repository));
> > > +
> > >   		remove_branch_state(the_repository, 0);
> > 
> > This removes the autostash file for all reset types but we only keep the
> > stash in the case of 'reset --hard' which is confusing.
> 
> I was worried that this change would be controversial... The rationale
> behind this change was that with `reset --hard`, we want to leave a
> clean working tree behind so we save it into the stash reflog. In all
> other cases, remove_branch_state() will apply the saved stash entry
> which should be fine since users don't expect a clean worktree with the
> other reset types.
> 
> I considered saving the autostash in all cases of reset but
> `git merge --abort` invokes `git reset --merge` behind the scenes so
> we'd have to consider that. Perhaps we can make all resets save the
> stash entry and, in the case of `merge --abort`, we can add some extra
> logic to subvert this so that the stash entry is applied?

Perhaps something like this?

-- >8 --
commit 14d0b569cb7675f00d32d3d7fad7564fcaeca458
Author: Denton Liu <liu.denton@gmail.com>
Date:   Fri Apr 3 06:50:34 2020 -0400

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

diff --git a/builtin/merge.c b/builtin/merge.c
index 9573d77096..31b82d614c 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1242,6 +1242,8 @@ static int merging_a_throwaway_tag(struct commit *commit)
 	return is_throwaway_tag;
 }
 
+static GIT_PATH_FUNC(git_path_merge_autostash_saved, "MERGE_AUTOSTASH_SAVED")
+
 int cmd_merge(int argc, const char **argv, const char *prefix)
 {
 	struct object_id result_tree, stash, head_oid;
@@ -1295,9 +1297,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		if (!file_exists(git_path_merge_head(the_repository)))
 			die(_("There is no merge to abort (MERGE_HEAD missing)."));
 
+		if (file_exists(git_path_merge_autostash(the_repository))) {
+			if (rename(git_path_merge_autostash(the_repository),
+						git_path_merge_autostash_saved()))
+				die_errno(_("failed to rename autostash"));
+		}
+
 		/* Invoke 'git reset --merge' */
 		ret = cmd_reset(nargc, nargv, prefix);
-		apply_autostash(git_path_merge_autostash(the_repository));
+
+		apply_autostash(git_path_merge_autostash_saved());
 		goto done;
 	}
 
diff --git a/builtin/reset.c b/builtin/reset.c
index 038c8532eb..060470c455 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -439,9 +439,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 			print_new_head_line(lookup_commit_reference(the_repository, &oid));
 	}
 	if (!pathspec.nr) {
-		if (reset_type == HARD)
-			save_autostash(git_path_merge_autostash(the_repository));
-
+		save_autostash(git_path_merge_autostash(the_repository));
 		remove_branch_state(the_repository, 0);
 	}
 
> 
> I'm not sure about what the most intuitive behaviour is. I thought that
> this implementation would be the best but I'm not entirely sure. I'd
> appreciate some more discussion on this.
> 
> Thanks,
> 
> Denton
> 
> > Best Wishes
> > 
> > Phillip
> > 
> > > +	}
> > >   	return update_ref_status;
> > >   }

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 17/19] merge: teach --autostash option
  2020-04-03 10:56           ` Denton Liu
@ 2020-04-03 13:09             ` Phillip Wood
  2020-04-03 21:14               ` Denton Liu
  0 siblings, 1 reply; 152+ messages in thread
From: Phillip Wood @ 2020-04-03 13:09 UTC (permalink / raw)
  To: Denton Liu, phillip.wood
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 03/04/2020 11:56, Denton Liu wrote:
> On Fri, Apr 03, 2020 at 06:31:26AM -0400, Denton Liu wrote:
>>>> diff --git a/builtin/reset.c b/builtin/reset.c
>>>> index 18228c312e..038c8532eb 100644
>>>> --- a/builtin/reset.c
>>>> +++ b/builtin/reset.c
>>>> @@ -25,6 +25,7 @@
>>>>    #include "cache-tree.h"
>>>>    #include "submodule.h"
>>>>    #include "submodule-config.h"
>>>> +#include "sequencer.h"
>>>>    #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
>>>> @@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>>>>    		if (reset_type == HARD && !update_ref_status && !quiet)
>>>>    			print_new_head_line(lookup_commit_reference(the_repository, &oid));
>>>>    	}
>>>> -	if (!pathspec.nr)
>>>> +	if (!pathspec.nr) {
>>>> +		if (reset_type == HARD)
>>>> +			save_autostash(git_path_merge_autostash(the_repository));
>>>> +
>>>>    		remove_branch_state(the_repository, 0);
>>>
>>> This removes the autostash file for all reset types but we only keep the
>>> stash in the case of 'reset --hard' which is confusing.
>>
>> I was worried that this change would be controversial... The rationale
>> behind this change was that with `reset --hard`, we want to leave a
>> clean working tree behind so we save it into the stash reflog. In all
>> other cases, remove_branch_state() will apply the saved stash entry
>> which should be fine since users don't expect a clean worktree with the
>> other reset types.
>>
>> I considered saving the autostash in all cases of reset but
>> `git merge --abort` invokes `git reset --merge` behind the scenes so
>> we'd have to consider that. Perhaps we can make all resets save the
>> stash entry and, in the case of `merge --abort`, we can add some extra
>> logic to subvert this so that the stash entry is applied?
> 
> Perhaps something like this?
> 
> -- >8 --
> commit 14d0b569cb7675f00d32d3d7fad7564fcaeca458
> Author: Denton Liu <liu.denton@gmail.com>
> Date:   Fri Apr 3 06:50:34 2020 -0400
> 
>      squash! merge: teach --autostash option
>      
>      Stash is saved when any reset is run, when git merge --abort is run,
>      stash is applied.

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

> diff --git a/builtin/merge.c b/builtin/merge.c
> index 9573d77096..31b82d614c 100644
> --- a/builtin/merge.c
> +++ b/builtin/merge.c
> @@ -1242,6 +1242,8 @@ static int merging_a_throwaway_tag(struct commit *commit)
>   	return is_throwaway_tag;
>   }
>   
> +static GIT_PATH_FUNC(git_path_merge_autostash_saved, "MERGE_AUTOSTASH_SAVED")
> +
>   int cmd_merge(int argc, const char **argv, const char *prefix)
>   {
>   	struct object_id result_tree, stash, head_oid;
> @@ -1295,9 +1297,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
>   		if (!file_exists(git_path_merge_head(the_repository)))
>   			die(_("There is no merge to abort (MERGE_HEAD missing)."));
>   
> +		if (file_exists(git_path_merge_autostash(the_repository))) {
> +			if (rename(git_path_merge_autostash(the_repository),
> +						git_path_merge_autostash_saved()))
> +				die_errno(_("failed to rename autostash"));

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

> +		}
> +
>   		/* Invoke 'git reset --merge' */
>   		ret = cmd_reset(nargc, nargv, prefix);
> -		apply_autostash(git_path_merge_autostash(the_repository));
> +
> +		apply_autostash(git_path_merge_autostash_saved());

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

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

Best Wishes

Phillip

>   		goto done;
>   	}
>   
> diff --git a/builtin/reset.c b/builtin/reset.c
> index 038c8532eb..060470c455 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -439,9 +439,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>   			print_new_head_line(lookup_commit_reference(the_repository, &oid));
>   	}
>   	if (!pathspec.nr) {
> -		if (reset_type == HARD)
> -			save_autostash(git_path_merge_autostash(the_repository));
> -
> +		save_autostash(git_path_merge_autostash(the_repository));
>   		remove_branch_state(the_repository, 0);
>   	}
>   
>>
>> I'm not sure about what the most intuitive behaviour is. I thought that
>> this implementation would be the best but I'm not entirely sure. I'd
>> appreciate some more discussion on this.
>>
>> Thanks,
>>
>> Denton
>>
>>> Best Wishes
>>>
>>> Phillip
>>>
>>>> +	}
>>>>    	return update_ref_status;
>>>>    }

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 17/19] merge: teach --autostash option
  2020-04-03 10:31         ` Denton Liu
  2020-04-03 10:56           ` Denton Liu
@ 2020-04-03 13:34           ` Phillip Wood
  2020-04-03 22:25             ` Denton Liu
  1 sibling, 1 reply; 152+ messages in thread
From: Phillip Wood @ 2020-04-03 13:34 UTC (permalink / raw)
  To: Denton Liu, phillip.wood
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 03/04/2020 11:31, Denton Liu wrote:
> Hi Phillip,
> 
> Thanks for your detailed review on the series. I've updated everything
> according to your comments except for the parts below:

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

> On Thu, Apr 02, 2020 at 04:24:54PM +0100, Phillip Wood wrote:
>>> diff --git a/branch.c b/branch.c
>>> index 579494738a..bf2536c70d 100644
>>> --- a/branch.c
>>> +++ b/branch.c
>>> @@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
>>>    	unlink(git_path_merge_rr(r));
>>>    	unlink(git_path_merge_msg(r));
>>>    	unlink(git_path_merge_mode(r));
>>> +	apply_autostash(git_path_merge_autostash(r));
>>>    }
>>>    void remove_branch_state(struct repository *r, int verbose)
>>> diff --git a/builtin/commit.c b/builtin/commit.c
>>> index 7ba33a3bec..c11894423a 100644
>>> --- a/builtin/commit.c
>>> +++ b/builtin/commit.c
>>> @@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>>>    	unlink(git_path_merge_mode(the_repository));
>>>    	unlink(git_path_squash_msg(the_repository));
>>> +	apply_autostash(git_path_merge_autostash(the_repository));
>>> +
>>
>> It's hard to tell from the limited context but do we want to run
>> commit_index_files() before applying the autostash?
> 
> I don't think it really matters which order we run it in. When we run
> apply_autostash(), we only ever touch the working tree, not the index so
> it doesn't matter if it's run before or after. I'd prefer to keep it
> here because if we ever refactor this to use
> remove_merge_branch_state(), the person working on this will be able to
> perform the refactor more easily without having to worry about implicit
> ordering dependencies.

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

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


>> I wonder if this should
>> be using remove_merge_branch_state() instead of duplicating it as well.
> 
> We can _almost_ use remove_branch_state() even! Unfortunately,
> remove_merge_branch_state() calls `unlink(git_path_merge_rr(r))` which
> we cannot do since repo_rerere() relies on it later. Perhaps we can
> can move repo_rerere() earlier?

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

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

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

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

Best Wishes

Phillip


>>> diff --git a/builtin/reset.c b/builtin/reset.c
>>> index 18228c312e..038c8532eb 100644
>>> --- a/builtin/reset.c
>>> +++ b/builtin/reset.c
>>> @@ -25,6 +25,7 @@
>>>    #include "cache-tree.h"
>>>    #include "submodule.h"
>>>    #include "submodule-config.h"
>>> +#include "sequencer.h"
>>>    #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
>>> @@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>>>    		if (reset_type == HARD && !update_ref_status && !quiet)
>>>    			print_new_head_line(lookup_commit_reference(the_repository, &oid));
>>>    	}
>>> -	if (!pathspec.nr)
>>> +	if (!pathspec.nr) {
>>> +		if (reset_type == HARD)
>>> +			save_autostash(git_path_merge_autostash(the_repository));
>>> +
>>>    		remove_branch_state(the_repository, 0);
>>
>> This removes the autostash file for all reset types but we only keep the
>> stash in the case of 'reset --hard' which is confusing.
> 
> I was worried that this change would be controversial... The rationale
> behind this change was that with `reset --hard`, we want to leave a
> clean working tree behind so we save it into the stash reflog. In all
> other cases, remove_branch_state() will apply the saved stash entry
> which should be fine since users don't expect a clean worktree with the
> other reset types.
> 
> I considered saving the autostash in all cases of reset but
> `git merge --abort` invokes `git reset --merge` behind the scenes so
> we'd have to consider that. Perhaps we can make all resets save the
> stash entry and, in the case of `merge --abort`, we can add some extra
> logic to subvert this so that the stash entry is applied?
> 
> I'm not sure about what the most intuitive behaviour is. I thought that
> this implementation would be the best but I'm not entirely sure. I'd
> appreciate some more discussion on this.
> 
> Thanks,
> 
> Denton
> 
>> Best Wishes
>>
>> Phillip
>>
>>> +	}
>>>    	return update_ref_status;
>>>    }

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 17/19] merge: teach --autostash option
  2020-04-03 13:09             ` Phillip Wood
@ 2020-04-03 21:14               ` Denton Liu
  0 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-03 21:14 UTC (permalink / raw)
  To: phillip.wood
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Phillip,

On Fri, Apr 03, 2020 at 02:09:26PM +0100, Phillip Wood wrote:
> Hi Denton
> 
> On 03/04/2020 11:56, Denton Liu wrote:
> > On Fri, Apr 03, 2020 at 06:31:26AM -0400, Denton Liu wrote:
> > > > > diff --git a/builtin/reset.c b/builtin/reset.c
> > > > > index 18228c312e..038c8532eb 100644
> > > > > --- a/builtin/reset.c
> > > > > +++ b/builtin/reset.c
> > > > > @@ -25,6 +25,7 @@
> > > > >    #include "cache-tree.h"
> > > > >    #include "submodule.h"
> > > > >    #include "submodule-config.h"
> > > > > +#include "sequencer.h"
> > > > >    #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
> > > > > @@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
> > > > >    		if (reset_type == HARD && !update_ref_status && !quiet)
> > > > >    			print_new_head_line(lookup_commit_reference(the_repository, &oid));
> > > > >    	}
> > > > > -	if (!pathspec.nr)
> > > > > +	if (!pathspec.nr) {
> > > > > +		if (reset_type == HARD)
> > > > > +			save_autostash(git_path_merge_autostash(the_repository));
> > > > > +
> > > > >    		remove_branch_state(the_repository, 0);
> > > > 
> > > > This removes the autostash file for all reset types but we only keep the
> > > > stash in the case of 'reset --hard' which is confusing.
> > > 
> > > I was worried that this change would be controversial... The rationale
> > > behind this change was that with `reset --hard`, we want to leave a
> > > clean working tree behind so we save it into the stash reflog. In all
> > > other cases, remove_branch_state() will apply the saved stash entry
> > > which should be fine since users don't expect a clean worktree with the
> > > other reset types.
> > > 
> > > I considered saving the autostash in all cases of reset but
> > > `git merge --abort` invokes `git reset --merge` behind the scenes so
> > > we'd have to consider that. Perhaps we can make all resets save the
> > > stash entry and, in the case of `merge --abort`, we can add some extra
> > > logic to subvert this so that the stash entry is applied?
> > 
> > Perhaps something like this?
> > 
> > -- >8 --
> > commit 14d0b569cb7675f00d32d3d7fad7564fcaeca458
> > Author: Denton Liu <liu.denton@gmail.com>
> > Date:   Fri Apr 3 06:50:34 2020 -0400
> > 
> >      squash! merge: teach --autostash option
> >      Stash is saved when any reset is run, when git merge --abort is run,
> >      stash is applied.
> 
> I think this is the easiest behavior to understand, it avoids changing the
> behavior of reset, in particular it stops 'reset --mixed/--soft' from
> suddenly starting to touch the working tree.

Great, I'll use this approach then.

> > diff --git a/builtin/merge.c b/builtin/merge.c
> > index 9573d77096..31b82d614c 100644
> > --- a/builtin/merge.c
> > +++ b/builtin/merge.c
> > @@ -1242,6 +1242,8 @@ static int merging_a_throwaway_tag(struct commit *commit)
> >   	return is_throwaway_tag;
> >   }
> > +static GIT_PATH_FUNC(git_path_merge_autostash_saved, "MERGE_AUTOSTASH_SAVED")
> > +
> >   int cmd_merge(int argc, const char **argv, const char *prefix)
> >   {
> >   	struct object_id result_tree, stash, head_oid;
> > @@ -1295,9 +1297,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
> >   		if (!file_exists(git_path_merge_head(the_repository)))
> >   			die(_("There is no merge to abort (MERGE_HEAD missing)."));
> > +		if (file_exists(git_path_merge_autostash(the_repository))) {
> > +			if (rename(git_path_merge_autostash(the_repository),
> > +						git_path_merge_autostash_saved()))
> > +				die_errno(_("failed to rename autostash"));
> 
> This is a bit of a performance, can't we just remember the stash oid in a
> variable and tweak the apply code?

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

> > +		}
> > +
> >   		/* Invoke 'git reset --merge' */
> >   		ret = cmd_reset(nargc, nargv, prefix);
> > -		apply_autostash(git_path_merge_autostash(the_repository));
> > +
> > +		apply_autostash(git_path_merge_autostash_saved());
> 
> Calling cmd_reset() was already a bit dodgy by our normal rules, now we're
> calling other functions after it though I guess given the current autostash
> implementation it's mostly in a separate process.
> 
> I think this is a good direction to go in
> BTW what message gets printed when the stash is saved?

This is the message that is printed:

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

Thanks,

Denton

> Best Wishes
> 
> Phillip
> 
> >   		goto done;
> >   	}
> > diff --git a/builtin/reset.c b/builtin/reset.c
> > index 038c8532eb..060470c455 100644
> > --- a/builtin/reset.c
> > +++ b/builtin/reset.c
> > @@ -439,9 +439,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
> >   			print_new_head_line(lookup_commit_reference(the_repository, &oid));
> >   	}
> >   	if (!pathspec.nr) {
> > -		if (reset_type == HARD)
> > -			save_autostash(git_path_merge_autostash(the_repository));
> > -
> > +		save_autostash(git_path_merge_autostash(the_repository));
> >   		remove_branch_state(the_repository, 0);
> >   	}
> > > 
> > > I'm not sure about what the most intuitive behaviour is. I thought that
> > > this implementation would be the best but I'm not entirely sure. I'd
> > > appreciate some more discussion on this.
> > > 
> > > Thanks,
> > > 
> > > Denton
> > > 
> > > > Best Wishes
> > > > 
> > > > Phillip
> > > > 
> > > > > +	}
> > > > >    	return update_ref_status;
> > > > >    }

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v3 17/19] merge: teach --autostash option
  2020-04-03 13:34           ` Phillip Wood
@ 2020-04-03 22:25             ` Denton Liu
  0 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-03 22:25 UTC (permalink / raw)
  To: phillip.wood
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Phillip,

On Fri, Apr 03, 2020 at 02:34:30PM +0100, Phillip Wood wrote:
> > On Thu, Apr 02, 2020 at 04:24:54PM +0100, Phillip Wood wrote:
> > > > diff --git a/branch.c b/branch.c
> > > > index 579494738a..bf2536c70d 100644
> > > > --- a/branch.c
> > > > +++ b/branch.c
> > > > @@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
> > > >    	unlink(git_path_merge_rr(r));
> > > >    	unlink(git_path_merge_msg(r));
> > > >    	unlink(git_path_merge_mode(r));
> > > > +	apply_autostash(git_path_merge_autostash(r));
> > > >    }
> > > >    void remove_branch_state(struct repository *r, int verbose)
> > > > diff --git a/builtin/commit.c b/builtin/commit.c
> > > > index 7ba33a3bec..c11894423a 100644
> > > > --- a/builtin/commit.c
> > > > +++ b/builtin/commit.c
> > > > @@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
> > > >    	unlink(git_path_merge_mode(the_repository));
> > > >    	unlink(git_path_squash_msg(the_repository));
> > > > +	apply_autostash(git_path_merge_autostash(the_repository));
> > > > +
> > > 
> > > It's hard to tell from the limited context but do we want to run
> > > commit_index_files() before applying the autostash?
> > 
> > I don't think it really matters which order we run it in. When we run
> > apply_autostash(), we only ever touch the working tree, not the index so
> > it doesn't matter if it's run before or after. I'd prefer to keep it
> > here because if we ever refactor this to use
> > remove_merge_branch_state(), the person working on this will be able to
> > perform the refactor more easily without having to worry about implicit
> > ordering dependencies.
> 
> Thinking a bit more about this we definitely want to commit the index files
> before applying the stash as that is done in a separate process so we want
> our index written to disk first.
> 
> 'git stash apply' does touch the index while it's merging and then tries to
> reset it to the pre-merge state if the merge has no conflicts. If the user
> runs
>     git add new-file
>     git merge --autostash branch
> and new-file is not in branch then 'git stash apply' will add new-file to
> the index
> 
> 
> > > I wonder if this should
> > > be using remove_merge_branch_state() instead of duplicating it as well.
> > 
> > We can _almost_ use remove_branch_state() even! Unfortunately,
> > remove_merge_branch_state() calls `unlink(git_path_merge_rr(r))` which
> > we cannot do since repo_rerere() relies on it later. Perhaps we can
> > can move repo_rerere() earlier?
> 
> I think we should move apply_autostash() down so that repo_rerere() is
> called with the index that we've committed before we apply the stash which
> might add conflicts or new files. We should probably run the post-commit and
> post-rewrite hooks before we apply the autostash as well to avoid changing
> the existing behaviour.
> 
> If we aim for something like
>   commit_index_files()
>   repo_rerere()
>   stash_oid = read_autostash_oid()
>   cleanup_branch_state()
>   run_post_commit_hook()
>   run_post_rewrite_hook()
>   print_commit_summary()
>   apply_autostash(stash_oid)
> 
> Then we keep the existing behaviour for rerere and the hooks, and the commit
> summary is printed before all the status junk that apply_autostash() spews
> out

Makes sense, I'll make this change.

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

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

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

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

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

I'll send up a new version later today.

Thanks for helping shape this topic up,

Denton

> Best Wishes
> 
> Phillip

^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 00/23] merge: learn --autostash
  2020-03-21  9:21   ` [PATCH v3 00/19] " Denton Liu
                       ` (18 preceding siblings ...)
  2020-03-21  9:21     ` [PATCH v3 19/19] pull: pass --autostash to merge Denton Liu
@ 2020-04-04  1:11     ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 01/23] Makefile: ASCII-sort += lists Denton Liu
                         ` (23 more replies)
  19 siblings, 24 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

Hi Phillip,

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


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

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

Changes since v3:

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

* Rename flag to READ_ONELINER_WARN_MISSING

* Add 'sequencer: rename stash_sha1 to stash_oid'

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

Changes since v2:

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

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

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

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

Changes since v1:

* Completely changed how the refactoring was done

* More tests and documentation

[1]: https://github.com/gitgitgadget/git/issues/394
[2]: https://lore.kernel.org/git/xmqqwoadfq8n.fsf@gitster-ct.c.googlers.com/
[3]: https://lore.kernel.org/git/xmqqwoaikehz.fsf@gitster-ct.c.googlers.com/
[4]: https://lore.kernel.org/git/20200123042906.GA29009@generichostname/

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

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

Range-diff against v3:
 1:  67511a47b2 =  1:  c26771e934 Makefile: ASCII-sort += lists
 2:  6d76cea5b4 =  2:  059e0e8e43 t7600: use test_write_lines()
 3:  7c254b34b0 <  -:  ---------- sequencer: use file strbuf for read_oneliner()
 -:  ---------- >  3:  76585e5b13 sequencer: stop leaking buf
 -:  ---------- >  4:  c7a3cfa200 sequencer: reuse strbuf_trim() in read_oneliner()
 -:  ---------- >  5:  58da8e9555 sequencer: make file exists check more efficient
 4:  dc88da2ebf !  6:  6ead240957 sequencer: make read_oneliner() accept flags
    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
     +#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
     +
      /*
    -  * Reads a file that was presumably written by a shell script, i.e. with an
    -  * end-of-line marker that needs to be stripped.
    +  * Resets a strbuf then reads a file that was presumably written by a shell
    +  * script, i.e. with an end-of-line marker that needs to be stripped.
     @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,
       * Returns 1 if the file was read, 0 if it could not be read or does not exist.
       */
    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
     -	const char *path, int skip_if_empty)
     +	const char *path, unsigned flags)
      {
    - 	int ret = 0;
    - 	struct strbuf file_buf = STRBUF_INIT;
    + 	strbuf_reset(buf);
    + 	if (strbuf_read_file(buf, path, 0) < 0) {
     @@ sequencer.c: static int read_oneliner(struct strbuf *buf,
      
    - 	strbuf_trim_trailing_newline(&file_buf);
    + 	strbuf_trim(buf);
      
    --	if (skip_if_empty && !file_buf.len)
    -+	if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !file_buf.len)
    - 		goto done;
    +-	if (skip_if_empty && !buf->len)
    ++	if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !buf->len)
    + 		return 0;
      
    - 	strbuf_addbuf(buf, &file_buf);
    + 	return 1;
     @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
    - 	if (is_rebase_i(opts)) {
      		struct strbuf buf = STRBUF_INIT;
    + 		int ret = 0;
      
     -		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
     +		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(),
    @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
      				strbuf_reset(&buf);
      			else {
     @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
    - 			strbuf_reset(&buf);
    + 			}
      		}
      
     -		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
    @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
      				opts->allow_rerere_auto = RERERE_AUTOUPDATE;
      			else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
     @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
    - 		strbuf_release(&buf);
    + 		read_strategy_opts(opts, &buf);
      
      		if (read_oneliner(&opts->current_fixups,
     -				  rebase_path_current_fixups(), 1)) {
 5:  7702543d91 <  -:  ---------- sequencer: configurably warn on non-existent files
 -:  ---------- >  7:  2e7922b259 sequencer: configurably warn on non-existent files
 6:  361bc2e885 !  8:  6c3c37994b sequencer: make read_oneliner() extern
    @@ Commit message
         Instead of hiding it as a static function within sequencer.c, extern it
         so that it can be reused by others.
     
    +    This patch is best viewed with --color-moved.
    +
      ## sequencer.c ##
     @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,
      	return 0;
      }
      
     -#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
    --#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
    +-#define READ_ONELINER_WARN_MISSING (1 << 1)
     -
     -/*
    -- * Reads a file that was presumably written by a shell script, i.e. with an
    -- * end-of-line marker that needs to be stripped.
    +- * Resets a strbuf then reads a file that was presumably written by a shell
    +- * script, i.e. with an end-of-line marker that needs to be stripped.
     - *
     - * Note that only the last end-of-line marker is stripped, consistent with the
     - * behavior of "$(cat path)" in a shell script.
    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
     +int read_oneliner(struct strbuf *buf,
      	const char *path, unsigned flags)
      {
    - 	int ret = 0;
    + 	strbuf_reset(buf);
     
      ## sequencer.h ##
     @@ sequencer.h: void print_commit_summary(struct repository *repo,
    @@ sequencer.h: void print_commit_summary(struct repository *repo,
      			  unsigned int flags);
      
     +#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
    -+#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
    ++#define READ_ONELINER_WARN_MISSING (1 << 1)
     +
     +/*
    -+ * Reads a file that was presumably written by a shell script, i.e. with an
    -+ * end-of-line marker that needs to be stripped.
    ++ * Resets a strbuf then reads a file that was presumably written by a shell
    ++ * script, i.e. with an end-of-line marker that needs to be stripped.
     + *
     + * Note that only the last end-of-line marker is stripped, consistent with the
     + * behavior of "$(cat path)" in a shell script.
     + *
    -+ * Returns 1 if the file was read, 0 if it could not be read.
    ++ * Returns 1 if the file was read, 0 if it could not be read or does not exist.
     + */
     +int read_oneliner(struct strbuf *buf,
     +	const char *path, unsigned flags);
 7:  47816abff1 !  9:  689f34a2a5 rebase: use read_oneliner()
    @@ Commit message
     
         Since in sequencer.c, read_one() basically duplicates the functionality
         of read_oneliner(), reduce code duplication by replacing read_one() with
    -    read_oneliner().
    +    read_oneliner(). Also, delete strbuf_reset() calls prior to
    +    read_oneliner() as read_oneliner() already resets the strbuf.
     
    -    This was done with the following Coccinelle script
    +    This was mostly done with the following Coccinelle script
     
                 @@
                 expression a, b;
    @@ Commit message
                 - read_one(a, b)
                 + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)
     
    -    and long lines were manually broken up.
    +    Long lines were broken up and strbuf_reset()s were deleted manually.
     
      ## builtin/rebase.c ##
     @@ builtin/rebase.c: static const char *state_dir_path(const char *filename, struct rebase_options *o
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
     -	if (read_one(state_dir_path("head-name", opts), &head_name) ||
     -	    read_one(state_dir_path("onto", opts), &buf))
     +	if (!read_oneliner(&head_name, state_dir_path("head-name", opts),
    -+			   READ_ONELINER_WARN_NON_EXISTENCE) ||
    ++			   READ_ONELINER_WARN_MISSING) ||
     +	    !read_oneliner(&buf, state_dir_path("onto", opts),
    -+			   READ_ONELINER_WARN_NON_EXISTENCE))
    ++			   READ_ONELINER_WARN_MISSING))
      		return -1;
      	opts->head_name = starts_with(head_name.buf, "refs/") ?
      		xstrdup(head_name.buf) : NULL;
     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    + 	 * head. Fall back to reading from head to cover for the case that the
    + 	 * user upgraded git with an ongoing interactive rebase.
      	 */
    - 	strbuf_reset(&buf);
    +-	strbuf_reset(&buf);
      	if (file_exists(state_dir_path("orig-head", opts))) {
     -		if (read_one(state_dir_path("orig-head", opts), &buf))
     +		if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
    -+				   READ_ONELINER_WARN_NON_EXISTENCE))
    ++				   READ_ONELINER_WARN_MISSING))
      			return -1;
     -	} else if (read_one(state_dir_path("head", opts), &buf))
     +	} else if (!read_oneliner(&buf, state_dir_path("head", opts),
    -+				  READ_ONELINER_WARN_NON_EXISTENCE))
    ++				  READ_ONELINER_WARN_MISSING))
      		return -1;
      	if (get_oid(buf.buf, &opts->orig_head))
      		return error(_("invalid orig-head: '%s'"), buf.buf);
     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    + 	}
      
      	if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
    - 		strbuf_reset(&buf);
    +-		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
     -			    &buf))
     +		if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
    -+				   READ_ONELINER_WARN_NON_EXISTENCE))
    ++				   READ_ONELINER_WARN_MISSING))
      			return -1;
      		if (!strcmp(buf.buf, "--rerere-autoupdate"))
      			opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    + 	}
      
      	if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
    - 		strbuf_reset(&buf);
    +-		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("gpg_sign_opt", opts),
     -			    &buf))
     +		if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
    -+				   READ_ONELINER_WARN_NON_EXISTENCE))
    ++				   READ_ONELINER_WARN_MISSING))
      			return -1;
      		free(opts->gpg_sign_opt);
      		opts->gpg_sign_opt = xstrdup(buf.buf);
    -@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    + 	}
      
      	if (file_exists(state_dir_path("strategy", opts))) {
    - 		strbuf_reset(&buf);
    +-		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("strategy", opts), &buf))
     +		if (!read_oneliner(&buf, state_dir_path("strategy", opts),
    -+				   READ_ONELINER_WARN_NON_EXISTENCE))
    ++				   READ_ONELINER_WARN_MISSING))
      			return -1;
      		free(opts->strategy);
      		opts->strategy = xstrdup(buf.buf);
    -@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    + 	}
      
      	if (file_exists(state_dir_path("strategy_opts", opts))) {
    - 		strbuf_reset(&buf);
    +-		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("strategy_opts", opts), &buf))
     +		if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
    -+				   READ_ONELINER_WARN_NON_EXISTENCE))
    ++				   READ_ONELINER_WARN_MISSING))
      			return -1;
      		free(opts->strategy_opts);
      		opts->strategy_opts = xstrdup(buf.buf);
    @@ builtin/rebase.c: static int apply_autostash(struct rebase_options *opts)
      		return 0;
      
     -	if (read_one(path, &autostash))
    -+	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
    ++	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))
      		return error(_("Could not read '%s'"), path);
      	/* Ensure that the hash is not mistaken for a number */
      	strbuf_addstr(&autostash, "^0");
 8:  7e9a86c337 = 10:  e0b8e409af sequencer: make apply_rebase() accept a path
 -:  ---------- > 11:  a52583beff sequencer: rename stash_sha1 to stash_oid
 9:  76a486d8b0 ! 12:  389b17df33 rebase: use apply_autostash() from sequencer.c
    @@ Commit message
     
         The apply_autostash() function in builtin/rebase.c is similar enough to
         the apply_autostash() function in sequencer.c that they are almost
    -    interchangeable. Make the sequencer.c version extern and use it in
    -    rebase.
    +    interchangeable, except for the type of arg they accept. Make the
    +    sequencer.c version extern and use it in rebase.
     
         The rebase version was introduced in 6defce2b02 (builtin rebase: support
         `--autostash` option, 2018-09-04) as part of the shell to C conversion.
    @@ builtin/rebase.c: static int rebase_write_basic_state(struct rebase_options *opt
     -	if (!file_exists(path))
     -		return 0;
     -
    --	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
    +-	if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))
     -		return error(_("Could not read '%s'"), path);
     -	/* Ensure that the hash is not mistaken for a number */
     -	strbuf_addstr(&autostash, "^0");
    @@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
     -static int apply_autostash(const char *path)
     +int apply_autostash(const char *path)
      {
    - 	struct strbuf stash_sha1 = STRBUF_INIT;
    + 	struct strbuf stash_oid = STRBUF_INIT;
      	struct child_process child = CHILD_PROCESS_INIT;
     
      ## sequencer.h ##
10:  6d37e8049b = 13:  fe57e21dbc rebase: generify reset_head()
11:  18e1900a16 = 14:  045668620d reset: extract reset_head() from rebase
12:  21e228920e = 15:  d5f51cd80e rebase: extract create_autostash()
13:  7e2eab5203 = 16:  9ab10d23d4 rebase: generify create_autostash()
14:  fddf506115 ! 17:  26cca49be6 sequencer: extract perform_autostash() from rebase
    @@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
     +
      int apply_autostash(const char *path)
      {
    - 	struct strbuf stash_sha1 = STRBUF_INIT;
    + 	struct strbuf stash_oid = STRBUF_INIT;
     
      ## sequencer.h ##
     @@ sequencer.h: void commit_post_rewrite(struct repository *r,
15:  9394c7ae30 ! 18:  e703022fda sequencer: unlink autostash in apply_autostash()
    @@ sequencer.c: int apply_autostash(const char *path)
      	}
      
     +	unlink(path);
    - 	strbuf_release(&stash_sha1);
    + 	strbuf_release(&stash_oid);
      	return ret;
      }
16:  0035b84388 ! 19:  75dc3f10a1 sequencer: implement save_autostash()
    @@ Commit message
         apply_save_autostash() and use it to implement save_autostash(). This
         function will be used in a future commit.
     
    +    The difference between save_autostash() and apply_autostash() is that
    +    the latter does not try to apply the stash. It skips that step and
    +    just stores the created entry in the stash reflog.
    +
      ## sequencer.c ##
     @@ sequencer.c: void create_autostash(struct repository *r, const char *path,
      	strbuf_release(&buf);
    @@ sequencer.c: void create_autostash(struct repository *r, const char *path,
     -int apply_autostash(const char *path)
     +static int apply_save_autostash(const char *path, int attempt_apply)
      {
    - 	struct strbuf stash_sha1 = STRBUF_INIT;
    + 	struct strbuf stash_oid = STRBUF_INIT;
      	struct child_process child = CHILD_PROCESS_INIT;
     @@ sequencer.c: int apply_autostash(const char *path)
      	}
    - 	strbuf_trim(&stash_sha1);
    + 	strbuf_trim(&stash_oid);
      
     -	child.git_cmd = 1;
     -	child.no_stdout = 1;
     -	child.no_stderr = 1;
     -	argv_array_push(&child.args, "stash");
     -	argv_array_push(&child.args, "apply");
    --	argv_array_push(&child.args, stash_sha1.buf);
    +-	argv_array_push(&child.args, stash_oid.buf);
     -	if (!run_command(&child))
     +	if (attempt_apply) {
     +		child.git_cmd = 1;
    @@ sequencer.c: int apply_autostash(const char *path)
     +		child.no_stderr = 1;
     +		argv_array_push(&child.args, "stash");
     +		argv_array_push(&child.args, "apply");
    -+		argv_array_push(&child.args, stash_sha1.buf);
    ++		argv_array_push(&child.args, stash_oid.buf);
     +		ret = run_command(&child);
     +	}
     +
    @@ sequencer.c: int apply_autostash(const char *path)
      	else {
      		struct child_process store = CHILD_PROCESS_INIT;
     @@ sequencer.c: int apply_autostash(const char *path)
    - 			ret = error(_("cannot store %s"), stash_sha1.buf);
    + 			ret = error(_("cannot store %s"), stash_oid.buf);
      		else
      			fprintf(stderr,
     -				_("Applying autostash resulted in conflicts.\n"
 -:  ---------- > 20:  598ddea6c1 sequencer: implement apply_autostash_oid()
17:  085f8e0dd2 ! 21:  7adf794192 merge: teach --autostash option
    @@ Commit message
                 git fetch ...
                 git merge --autostash FETCH_HEAD
     
    -    When `git reset --hard` is run to abort a merge, the working tree will
    -    be left in a clean state, as expected, with the autostash pushed onto
    -    the stash stack.
    +    When an autostash is generated, it is automatically reapplied to the
    +    worktree only in three explicit situations:
    +
    +            1. An incomplete merge is commit using `git commit`.
    +            2. A merge completes successfully.
    +            3. A merge is aborted using `git merge --abort`.
    +
    +    In all other situations where the merge state is removed using
    +    remove_merge_branch_state() such as aborting a merge via
    +    `git reset --hard`, the autostash is saved into the stash reflog
    +    instead keeping the worktree clean.
     
         Suggested-by: Alban Gruin <alban.gruin@gmail.com>
     
    @@ Documentation/config/merge.txt: merge.stat::
     +merge.autoStash::
     +	When set to true, automatically create a temporary stash entry
     +	before the operation begins, and apply it after the operation
    -+	ends.  This means that you can run rebase on a dirty worktree.
    ++	ends.  This means that you can run merge on a dirty worktree.
     +	However, use with care: the final stash application after a
    -+	successful rebase might result in non-trivial conflicts.
    ++	successful merge might result in non-trivial conflicts.
     +	This option can be overridden by the `--no-autostash` and
     +	`--autostash` options of linkgit:git-merge[1].
     +	Defaults to false.
    @@ Documentation/config/merge.txt: merge.stat::
      	Controls which merge tool is used by linkgit:git-mergetool[1].
      	The list below shows the valid built-in values.
     
    + ## Documentation/git-merge.txt ##
    +@@ Documentation/git-merge.txt: will be appended to the specified message.
    + 
    + --abort::
    + 	Abort the current conflict resolution process, and
    +-	try to reconstruct the pre-merge state.
    ++	try to reconstruct the pre-merge state. If an autostash entry is
    ++	present, apply it back to the worktree.
    + +
    + If there were uncommitted worktree changes present when the merge
    + started, 'git merge --abort' will in some cases be unable to
    +@@ Documentation/git-merge.txt: reconstruct these changes. It is therefore recommended to always
    + commit or stash your changes before running 'git merge'.
    + +
    + 'git merge --abort' is equivalent to 'git reset --merge' when
    +-`MERGE_HEAD` is present.
    ++`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present where
    ++the stash entry is applied to the worktree.
    + 
    + --quit::
    + 	Forget about the current merge in progress. Leave the index
    +
      ## Documentation/merge-options.txt ##
     @@ Documentation/merge-options.txt: ifndef::git-pull[]
      	Note that not all merge strategies may support progress
    @@ Documentation/merge-options.txt: ifndef::git-pull[]
     +--no-autostash::
     +	Automatically create a temporary stash entry before the operation
     +	begins, and apply it after the operation ends.  This means
    -+	that you can run rebase on a dirty worktree.  However, use
    ++	that you can run the operation on a dirty worktree.  However, use
     +	with care: the final stash application after a successful
    -+	rebase might result in non-trivial conflicts.
    ++	merge might result in non-trivial conflicts.
     +
      endif::git-pull[]
      
    @@ branch.c: void remove_merge_branch_state(struct repository *r)
      	unlink(git_path_merge_rr(r));
      	unlink(git_path_merge_msg(r));
      	unlink(git_path_merge_mode(r));
    -+	apply_autostash(git_path_merge_autostash(r));
    ++	save_autostash(git_path_merge_autostash(r));
      }
      
      void remove_branch_state(struct repository *r, int verbose)
     
      ## builtin/commit.c ##
     @@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix)
    - 	unlink(git_path_merge_mode(the_repository));
    - 	unlink(git_path_squash_msg(the_repository));
    + 				     &oid, flags);
    + 	}
      
     +	apply_autostash(git_path_merge_autostash(the_repository));
     +
    - 	if (commit_index_files())
    - 		die(_("repository has been updated, but unable to write\n"
    - 		      "new_index file. Check that disk is not full and quota is\n"
    + 	UNLEAK(err);
    + 	UNLEAK(sb);
    + 	return 0;
     
      ## builtin/merge.c ##
     @@ builtin/merge.c: static int show_progress = -1;
    @@ builtin/merge.c: static struct option builtin_merge_options[] = {
      	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
      	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
      	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
    -+	OPT_BOOL(0, "autostash", &autostash,
    -+	      N_("automatically stash/stash pop before and after")),
    ++	OPT_AUTOSTASH(&autostash),
      	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
      	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
      	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
     @@ builtin/merge.c: static void finish(struct commit *head_commit,
    - 		strbuf_addf(&reflog_message, "%s: %s",
    - 			getenv("GIT_REFLOG_ACTION"), msg);
    - 	}
    + 	/* Run a post-merge hook */
    + 	run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
    + 
     +	apply_autostash(git_path_merge_autostash(the_repository));
    - 	if (squash) {
    - 		squash_message(head_commit, remoteheads);
    - 	} else {
    + 	strbuf_release(&reflog_message);
    + }
    + 
     @@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void *cb)
      		return 0;
      	} else if (!strcmp(k, "gpg.mintrustlevel")) {
    @@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void
      
      	status = fmt_merge_msg_config(k, v, cb);
     @@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
    + 	if (abort_current_merge) {
    + 		int nargc = 2;
    + 		const char *nargv[] = {"reset", "--merge", NULL};
    ++		struct strbuf stash_oid = STRBUF_INIT;
      
    + 		if (orig_argc != 2)
    + 			usage_msg_opt(_("--abort expects no arguments"),
    +@@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
    + 		if (!file_exists(git_path_merge_head(the_repository)))
    + 			die(_("There is no merge to abort (MERGE_HEAD missing)."));
    + 
    ++		if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),
    ++			   READ_ONELINER_SKIP_IF_EMPTY)) {
    ++			strbuf_trim(&stash_oid);
    ++			unlink(git_path_merge_autostash(the_repository));
    ++		}
    ++
      		/* Invoke 'git reset --merge' */
      		ret = cmd_reset(nargc, nargv, prefix);
    -+		apply_autostash(git_path_merge_autostash(the_repository));
    ++
    ++		if (stash_oid.len)
    ++			apply_autostash_oid(stash_oid.buf);
    ++
    ++		strbuf_release(&stash_oid);
      		goto done;
      	}
      
    @@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
      	git_committer_info(IDENT_STRICT);
      
     
    + ## builtin/rebase.c ##
    +@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
    + 		{ OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
    + 			N_("GPG-sign commits"),
    + 			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
    +-		OPT_BOOL(0, "autostash", &options.autostash,
    +-			 N_("automatically stash/stash pop before and after")),
    ++		OPT_AUTOSTASH(&options.autostash),
    + 		OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
    + 				N_("add exec lines after each commit of the "
    + 				   "editable list")),
    +
      ## builtin/reset.c ##
     @@
      #include "cache-tree.h"
    @@ builtin/reset.c: int cmd_reset(int argc, const char **argv, const char *prefix)
      	}
     -	if (!pathspec.nr)
     +	if (!pathspec.nr) {
    -+		if (reset_type == HARD)
    -+			save_autostash(git_path_merge_autostash(the_repository));
    -+
      		remove_branch_state(the_repository, 0);
     +	}
      
      	return update_ref_status;
      }
     
    + ## parse-options.h ##
    +@@ parse-options.h: int parse_opt_passthru_argv(const struct option *, const char *, int);
    + #define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
    + #define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file"))
    + #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character"))
    ++#define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after"))
    + 
    + #endif
    +
      ## path.c ##
     @@ path.c: REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
      REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
    @@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '
     +	test_cmp file.1 file
     +'
     +
    -+test_expect_success 'completed merge with --no-commit and --autostash' '
    ++test_expect_success 'completed merge (git commit) with --no-commit and --autostash' '
     +	git reset --hard c1 &&
     +	git merge-file file file.orig file.9 &&
     +	git diff >expect &&
    @@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '
     +	test_cmp result.1-5-9 file
     +'
     +
    ++test_expect_success 'completed merge (git merge --continue) with --no-commit and --autostash' '
    ++	git reset --hard c1 &&
    ++	git merge-file file file.orig file.9 &&
    ++	git diff >expect &&
    ++	git merge --no-commit --autostash c2 &&
    ++	git stash show -p MERGE_AUTOSTASH >actual &&
    ++	test_cmp expect actual &&
    ++	git merge --continue 2>err &&
    ++	test_i18ngrep "Applied autostash." err &&
    ++	git show HEAD:file >merge-result &&
    ++	test_cmp result.1-5 merge-result &&
    ++	test_cmp result.1-5-9 file
    ++'
    ++
     +test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '
     +	git reset --hard c1 &&
     +	git merge-file file file.orig file.9 &&
18:  4dabe214f2 = 22:  f1e54622fb t5520: make test_pull_autostash() accept expect_parent_num
19:  9b6f16f1bf ! 23:  177c7e537b pull: pass --autostash to merge
    @@ Documentation/merge-options.txt: ifndef::git-pull[]
      	Automatically create a temporary stash entry before the operation
     @@ Documentation/merge-options.txt: ifndef::git-pull[]
      	with care: the final stash application after a successful
    - 	rebase might result in non-trivial conflicts.
    + 	merge might result in non-trivial conflicts.
      
     -endif::git-pull[]
     -
-- 
2.26.0.159.g23e2136ad0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 01/23] Makefile: ASCII-sort += lists
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 02/23] t7600: use test_write_lines() Denton Liu
                         ` (22 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Makefile | 77 ++++++++++++++++++++++++++++----------------------------
 1 file changed, 39 insertions(+), 38 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 02/23] t7600: use test_write_lines()
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
  2020-04-04  1:11       ` [PATCH v4 01/23] Makefile: ASCII-sort += lists Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 03/23] sequencer: stop leaking buf Denton Liu
                         ` (21 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 t/t7600-merge.sh | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 03/23] sequencer: stop leaking buf
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
  2020-04-04  1:11       ` [PATCH v4 01/23] Makefile: ASCII-sort += lists Denton Liu
  2020-04-04  1:11       ` [PATCH v4 02/23] t7600: use test_write_lines() Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-05 21:33         ` Junio C Hamano
  2020-04-04  1:11       ` [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner() Denton Liu
                         ` (20 subsequent siblings)
  23 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index e528225e78..faab0b13e8 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2485,6 +2485,7 @@ static int read_populate_opts(struct replay_opts *opts)
 {
 	if (is_rebase_i(opts)) {
 		struct strbuf buf = STRBUF_INIT;
+		int ret = 0;
 
 		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
 			if (!starts_with(buf.buf, "-S"))
@@ -2525,7 +2526,7 @@ static int read_populate_opts(struct replay_opts *opts)
 			opts->keep_redundant_commits = 1;
 
 		read_strategy_opts(opts, &buf);
-		strbuf_release(&buf);
+		strbuf_reset(&buf);
 
 		if (read_oneliner(&opts->current_fixups,
 				  rebase_path_current_fixups(), 1)) {
@@ -2538,12 +2539,16 @@ static int read_populate_opts(struct replay_opts *opts)
 		}
 
 		if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {
-			if (get_oid_hex(buf.buf, &opts->squash_onto) < 0)
-				return error(_("unusable squash-onto"));
+			if (get_oid_hex(buf.buf, &opts->squash_onto) < 0) {
+				ret = error(_("unusable squash-onto"));
+				goto done_rebase_i;
+			}
 			opts->have_squash_onto = 1;
 		}
 
-		return 0;
+done_rebase_i:
+		strbuf_release(&buf);
+		return ret;
 	}
 
 	if (!file_exists(git_path_opts_file()))
-- 
2.26.0.159.g23e2136ad0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner()
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (2 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 03/23] sequencer: stop leaking buf Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-05 21:46         ` Junio C Hamano
  2020-04-06 14:03         ` Phillip Wood
  2020-04-04  1:11       ` [PATCH v4 05/23] sequencer: make file exists check more efficient Denton Liu
                         ` (19 subsequent siblings)
  23 siblings, 2 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 18 +++++-------------
 1 file changed, 5 insertions(+), 13 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 05/23] sequencer: make file exists check more efficient
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (3 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner() Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 06/23] sequencer: make read_oneliner() accept flags Denton Liu
                         ` (18 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 09ca68f540..6e63224c41 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -431,13 +431,10 @@ static int write_message(const void *buf, size_t len, const char *filename,
 static int read_oneliner(struct strbuf *buf,
 	const char *path, int skip_if_empty)
 {
-
-	if (!file_exists(path))
-		return 0;
-
 	strbuf_reset(buf);
 	if (strbuf_read_file(buf, path, 0) < 0) {
-		warning_errno(_("could not read '%s'"), path);
+		if (errno != ENOENT && errno != ENOTDIR)
+			warning_errno(_("could not read '%s'"), path);
 		return 0;
 	}
 
-- 
2.26.0.159.g23e2136ad0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 06/23] sequencer: make read_oneliner() accept flags
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (4 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 05/23] sequencer: make file exists check more efficient Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 07/23] sequencer: configurably warn on non-existent files Denton Liu
                         ` (17 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

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

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

and manually break up long lines in the result.

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 6e63224c41..6c26d61670 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -419,6 +419,8 @@ static int write_message(const void *buf, size_t len, const char *filename,
 	return 0;
 }
 
+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
+
 /*
  * Resets a strbuf then reads a file that was presumably written by a shell
  * script, i.e. with an end-of-line marker that needs to be stripped.
@@ -429,7 +431,7 @@ static int write_message(const void *buf, size_t len, const char *filename,
  * Returns 1 if the file was read, 0 if it could not be read or does not exist.
  */
 static int read_oneliner(struct strbuf *buf,
-	const char *path, int skip_if_empty)
+	const char *path, unsigned flags)
 {
 	strbuf_reset(buf);
 	if (strbuf_read_file(buf, path, 0) < 0) {
@@ -440,7 +442,7 @@ static int read_oneliner(struct strbuf *buf,
 
 	strbuf_trim(buf);
 
-	if (skip_if_empty && !buf->len)
+	if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !buf->len)
 		return 0;
 
 	return 1;
@@ -2479,7 +2481,8 @@ static int read_populate_opts(struct replay_opts *opts)
 		struct strbuf buf = STRBUF_INIT;
 		int ret = 0;
 
-		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
+		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(),
+				  READ_ONELINER_SKIP_IF_EMPTY)) {
 			if (!starts_with(buf.buf, "-S"))
 				strbuf_reset(&buf);
 			else {
@@ -2488,7 +2491,8 @@ static int read_populate_opts(struct replay_opts *opts)
 			}
 		}
 
-		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
+		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(),
+				  READ_ONELINER_SKIP_IF_EMPTY)) {
 			if (!strcmp(buf.buf, "--rerere-autoupdate"))
 				opts->allow_rerere_auto = RERERE_AUTOUPDATE;
 			else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
@@ -2519,7 +2523,8 @@ static int read_populate_opts(struct replay_opts *opts)
 		read_strategy_opts(opts, &buf);
 
 		if (read_oneliner(&opts->current_fixups,
-				  rebase_path_current_fixups(), 1)) {
+				  rebase_path_current_fixups(),
+				  READ_ONELINER_SKIP_IF_EMPTY)) {
 			const char *p = opts->current_fixups.buf;
 			opts->current_fixup_count = 1;
 			while ((p = strchr(p, '\n'))) {
@@ -3660,7 +3665,8 @@ static int apply_autostash(struct replay_opts *opts)
 	struct child_process child = CHILD_PROCESS_INIT;
 	int ret = 0;
 
-	if (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {
+	if (!read_oneliner(&stash_sha1, rebase_path_autostash(),
+			   READ_ONELINER_SKIP_IF_EMPTY)) {
 		strbuf_release(&stash_sha1);
 		return 0;
 	}
@@ -4283,7 +4289,8 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
 		struct strbuf buf = STRBUF_INIT;
 		struct object_id oid;
 
-		if (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&
+		if (read_oneliner(&buf, rebase_path_stopped_sha(),
+				  READ_ONELINER_SKIP_IF_EMPTY) &&
 		    !get_oid_committish(buf.buf, &oid))
 			record_in_rewritten(&oid, peek_command(&todo_list, 0));
 		strbuf_release(&buf);
-- 
2.26.0.159.g23e2136ad0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 07/23] sequencer: configurably warn on non-existent files
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (5 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 06/23] sequencer: make read_oneliner() accept flags Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-06 14:45         ` Phillip Wood
  2020-04-04  1:11       ` [PATCH v4 08/23] sequencer: make read_oneliner() extern Denton Liu
                         ` (16 subsequent siblings)
  23 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 08/23] sequencer: make read_oneliner() extern
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (6 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 07/23] sequencer: configurably warn on non-existent files Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 09/23] rebase: use read_oneliner() Denton Liu
                         ` (15 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 14 +-------------
 sequencer.h | 14 ++++++++++++++
 2 files changed, 15 insertions(+), 13 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 09/23] rebase: use read_oneliner()
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (7 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 08/23] sequencer: make read_oneliner() extern Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 10/23] sequencer: make apply_rebase() accept a path Denton Liu
                         ` (14 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

This was mostly done with the following Coccinelle script

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 42 +++++++++++++++++-------------------------
 1 file changed, 17 insertions(+), 25 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 10/23] sequencer: make apply_rebase() accept a path
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (8 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 09/23] rebase: use read_oneliner() Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-06 15:07         ` Phillip Wood
  2020-04-04  1:11       ` [PATCH v4 11/23] sequencer: rename stash_sha1 to stash_oid Denton Liu
                         ` (13 subsequent siblings)
  23 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 11/23] sequencer: rename stash_sha1 to stash_oid
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (9 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 10/23] sequencer: make apply_rebase() accept a path Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 12/23] rebase: use apply_autostash() from sequencer.c Denton Liu
                         ` (12 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 12/23] rebase: use apply_autostash() from sequencer.c
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (10 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 11/23] sequencer: rename stash_sha1 to stash_oid Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 13/23] rebase: generify reset_head() Denton Liu
                         ` (11 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 49 ++----------------------------------------------
 sequencer.c      |  2 +-
 sequencer.h      |  2 ++
 3 files changed, 5 insertions(+), 48 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 13/23] rebase: generify reset_head()
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (11 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 12/23] rebase: use apply_autostash() from sequencer.c Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 14/23] reset: extract reset_head() from rebase Denton Liu
                         ` (10 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 64 ++++++++++++++++++++++++++++--------------------
 1 file changed, 37 insertions(+), 27 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 14/23] reset: extract reset_head() from rebase
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (12 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 13/23] rebase: generify reset_head() Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 15/23] rebase: extract create_autostash() Denton Liu
                         ` (9 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Makefile         |   1 +
 builtin/rebase.c | 139 +---------------------------------------------
 reset.c          | 140 +++++++++++++++++++++++++++++++++++++++++++++++
 reset.h          |  20 +++++++
 4 files changed, 162 insertions(+), 138 deletions(-)
 create mode 100644 reset.c
 create mode 100644 reset.h

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 15/23] rebase: extract create_autostash()
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (13 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 14/23] reset: extract reset_head() from rebase Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 16/23] rebase: generify create_autostash() Denton Liu
                         ` (8 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 94 +++++++++++++++++++++++++-----------------------
 1 file changed, 50 insertions(+), 44 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 16/23] rebase: generify create_autostash()
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (14 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 15/23] rebase: extract create_autostash() Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 17/23] sequencer: extract perform_autostash() from rebase Denton Liu
                         ` (7 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 17/23] sequencer: extract perform_autostash() from rebase
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (15 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 16/23] rebase: generify create_autostash() Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 18/23] sequencer: unlink autostash in apply_autostash() Denton Liu
                         ` (6 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 builtin/rebase.c | 49 -----------------------------------------------
 sequencer.c      | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
 sequencer.h      |  2 ++
 3 files changed, 52 insertions(+), 49 deletions(-)

diff --git a/builtin/rebase.c b/builtin/rebase.c
index e9b05a6cd8..b800c8bfca 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1269,55 +1269,6 @@ static int check_exec_cmd(const char *cmd)
 	return 0;
 }
 
-static void create_autostash(struct repository *r, const char *path,
-			     const char *default_reflog_action)
-{
-	struct strbuf buf = STRBUF_INIT;
-	struct lock_file lock_file = LOCK_INIT;
-	int fd;
-
-	fd = repo_hold_locked_index(r, &lock_file, 0);
-	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
-	if (0 <= fd)
-		repo_update_index_if_able(r, &lock_file);
-	rollback_lock_file(&lock_file);
-
-	if (has_unstaged_changes(r, 1) ||
-	    has_uncommitted_changes(r, 1)) {
-		struct child_process stash = CHILD_PROCESS_INIT;
-		struct object_id oid;
-
-		argv_array_pushl(&stash.args,
-				 "stash", "create", "autostash", NULL);
-		stash.git_cmd = 1;
-		stash.no_stdin = 1;
-		strbuf_reset(&buf);
-		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
-			die(_("Cannot autostash"));
-		strbuf_trim_trailing_newline(&buf);
-		if (get_oid(buf.buf, &oid))
-			die(_("Unexpected stash response: '%s'"),
-			    buf.buf);
-		strbuf_reset(&buf);
-		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
-
-		if (safe_create_leading_directories_const(path))
-			die(_("Could not create directory for '%s'"),
-			    path);
-		write_file(path, "%s", oid_to_hex(&oid));
-		printf(_("Created autostash: %s\n"), buf.buf);
-		if (reset_head(r, NULL, "reset --hard",
-			       NULL, RESET_HEAD_HARD, NULL, NULL,
-			       default_reflog_action) < 0)
-			die(_("could not reset --hard"));
-
-		if (discard_index(r->index) < 0 ||
-			repo_read_index(r) < 0)
-			die(_("could not read index"));
-	}
-	strbuf_release(&buf);
-}
-
 int cmd_rebase(int argc, const char **argv, const char *prefix)
 {
 	struct rebase_options options = REBASE_OPTIONS_INIT;
diff --git a/sequencer.c b/sequencer.c
index dde1f6b30f..8efe0976fd 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -32,6 +32,7 @@
 #include "alias.h"
 #include "commit-reach.h"
 #include "rebase-interactive.h"
+#include "reset.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -3649,6 +3650,55 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
 	return -1;
 }
 
+void create_autostash(struct repository *r, const char *path,
+		      const char *default_reflog_action)
+{
+	struct strbuf buf = STRBUF_INIT;
+	struct lock_file lock_file = LOCK_INIT;
+	int fd;
+
+	fd = repo_hold_locked_index(r, &lock_file, 0);
+	refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
+	if (0 <= fd)
+		repo_update_index_if_able(r, &lock_file);
+	rollback_lock_file(&lock_file);
+
+	if (has_unstaged_changes(r, 1) ||
+	    has_uncommitted_changes(r, 1)) {
+		struct child_process stash = CHILD_PROCESS_INIT;
+		struct object_id oid;
+
+		argv_array_pushl(&stash.args,
+				 "stash", "create", "autostash", NULL);
+		stash.git_cmd = 1;
+		stash.no_stdin = 1;
+		strbuf_reset(&buf);
+		if (capture_command(&stash, &buf, GIT_MAX_HEXSZ))
+			die(_("Cannot autostash"));
+		strbuf_trim_trailing_newline(&buf);
+		if (get_oid(buf.buf, &oid))
+			die(_("Unexpected stash response: '%s'"),
+			    buf.buf);
+		strbuf_reset(&buf);
+		strbuf_add_unique_abbrev(&buf, &oid, DEFAULT_ABBREV);
+
+		if (safe_create_leading_directories_const(path))
+			die(_("Could not create directory for '%s'"),
+			    path);
+		write_file(path, "%s", oid_to_hex(&oid));
+		printf(_("Created autostash: %s\n"), buf.buf);
+		if (reset_head(r, NULL, "reset --hard",
+			       NULL, RESET_HEAD_HARD, NULL, NULL,
+			       default_reflog_action) < 0)
+			die(_("could not reset --hard"));
+
+		if (discard_index(r->index) < 0 ||
+			repo_read_index(r) < 0)
+			die(_("could not read index"));
+	}
+	strbuf_release(&buf);
+}
+
 int apply_autostash(const char *path)
 {
 	struct strbuf stash_oid = STRBUF_INIT;
diff --git a/sequencer.h b/sequencer.h
index d6ba62a089..cf1284f9ed 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,
 			 const struct commit *current_head,
 			 const struct object_id *new_head);
 
+void create_autostash(struct repository *r, const char *path,
+		      const char *default_reflog_action);
 int apply_autostash(const char *path);
 
 #define SUMMARY_INITIAL_COMMIT   (1 << 0)
-- 
2.26.0.159.g23e2136ad0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 18/23] sequencer: unlink autostash in apply_autostash()
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (16 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 17/23] sequencer: extract perform_autostash() from rebase Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 19/23] sequencer: implement save_autostash() Denton Liu
                         ` (5 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/sequencer.c b/sequencer.c
index 8efe0976fd..5dd1c2438e 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -3740,6 +3740,7 @@ int apply_autostash(const char *path)
 				  " \"git stash drop\" at any time.\n"));
 	}
 
+	unlink(path);
 	strbuf_release(&stash_oid);
 	return ret;
 }
-- 
2.26.0.159.g23e2136ad0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 19/23] sequencer: implement save_autostash()
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (17 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 18/23] sequencer: unlink autostash in apply_autostash() Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-06 15:15         ` Phillip Wood
  2020-04-04  1:11       ` [PATCH v4 20/23] sequencer: implement apply_autostash_oid() Denton Liu
                         ` (4 subsequent siblings)
  23 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 37 +++++++++++++++++++++++++++----------
 sequencer.h |  1 +
 2 files changed, 28 insertions(+), 10 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 20/23] sequencer: implement apply_autostash_oid()
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (18 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 19/23] sequencer: implement save_autostash() Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 21/23] merge: teach --autostash option Denton Liu
                         ` (3 subsequent siblings)
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 38 ++++++++++++++++++++++++++------------
 sequencer.h |  1 +
 2 files changed, 27 insertions(+), 12 deletions(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 21/23] merge: teach --autostash option
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (19 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 20/23] sequencer: implement apply_autostash_oid() Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-06 15:20         ` Phillip Wood
  2020-04-04  1:11       ` [PATCH v4 22/23] t5520: make test_pull_autostash() accept expect_parent_num Denton Liu
                         ` (2 subsequent siblings)
  23 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

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

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

but now, that is reduced to

	git fetch ...
	git merge --autostash FETCH_HEAD

When an autostash is generated, it is automatically reapplied to the
worktree only in three explicit situations:

	1. An incomplete merge is commit using `git commit`.
	2. A merge completes successfully.
	3. A merge is aborted using `git merge --abort`.

In all other situations where the merge state is removed using
remove_merge_branch_state() such as aborting a merge via
`git reset --hard`, the autostash is saved into the stash reflog
instead keeping the worktree clean.

Suggested-by: Alban Gruin <alban.gruin@gmail.com>
Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Documentation/config/merge.txt  |  10 +++
 Documentation/git-merge.txt     |   6 +-
 Documentation/merge-options.txt |   8 +++
 branch.c                        |   1 +
 builtin/commit.c                |   2 +
 builtin/merge.c                 |  27 ++++++++
 builtin/rebase.c                |   3 +-
 builtin/reset.c                 |   4 +-
 parse-options.h                 |   1 +
 path.c                          |   1 +
 path.h                          |   4 +-
 t/t3033-merge-toplevel.sh       |  22 ++++++
 t/t7600-merge.sh                | 118 ++++++++++++++++++++++++++++++++
 13 files changed, 201 insertions(+), 6 deletions(-)

diff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt
index 6a313937f8..cb2ed58907 100644
--- a/Documentation/config/merge.txt
+++ b/Documentation/config/merge.txt
@@ -70,6 +70,16 @@ merge.stat::
 	Whether to print the diffstat between ORIG_HEAD and the merge result
 	at the end of the merge.  True by default.
 
+merge.autoStash::
+	When set to true, automatically create a temporary stash entry
+	before the operation begins, and apply it after the operation
+	ends.  This means that you can run merge on a dirty worktree.
+	However, use with care: the final stash application after a
+	successful merge might result in non-trivial conflicts.
+	This option can be overridden by the `--no-autostash` and
+	`--autostash` options of linkgit:git-merge[1].
+	Defaults to false.
+
 merge.tool::
 	Controls which merge tool is used by linkgit:git-mergetool[1].
 	The list below shows the valid built-in values.
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 092529c619..9cb14f0059 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -94,7 +94,8 @@ will be appended to the specified message.
 
 --abort::
 	Abort the current conflict resolution process, and
-	try to reconstruct the pre-merge state.
+	try to reconstruct the pre-merge state. If an autostash entry is
+	present, apply it back to the worktree.
 +
 If there were uncommitted worktree changes present when the merge
 started, 'git merge --abort' will in some cases be unable to
@@ -102,7 +103,8 @@ reconstruct these changes. It is therefore recommended to always
 commit or stash your changes before running 'git merge'.
 +
 'git merge --abort' is equivalent to 'git reset --merge' when
-`MERGE_HEAD` is present.
+`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present where
+the stash entry is applied to the worktree.
 
 --quit::
 	Forget about the current merge in progress. Leave the index
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 40dc4f5e8c..3985e6d4a9 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -155,6 +155,14 @@ ifndef::git-pull[]
 	Note that not all merge strategies may support progress
 	reporting.
 
+--autostash::
+--no-autostash::
+	Automatically create a temporary stash entry before the operation
+	begins, and apply it after the operation ends.  This means
+	that you can run the operation on a dirty worktree.  However, use
+	with care: the final stash application after a successful
+	merge might result in non-trivial conflicts.
+
 endif::git-pull[]
 
 --allow-unrelated-histories::
diff --git a/branch.c b/branch.c
index 579494738a..2d9e7675a6 100644
--- a/branch.c
+++ b/branch.c
@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
 	unlink(git_path_merge_rr(r));
 	unlink(git_path_merge_msg(r));
 	unlink(git_path_merge_mode(r));
+	save_autostash(git_path_merge_autostash(r));
 }
 
 void remove_branch_state(struct repository *r, int verbose)
diff --git a/builtin/commit.c b/builtin/commit.c
index 7ba33a3bec..c1b25d2954 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1713,6 +1713,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 				     &oid, flags);
 	}
 
+	apply_autostash(git_path_merge_autostash(the_repository));
+
 	UNLEAK(err);
 	UNLEAK(sb);
 	return 0;
diff --git a/builtin/merge.c b/builtin/merge.c
index d127d2225f..f2f8069f85 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -82,6 +82,7 @@ static int show_progress = -1;
 static int default_to_upstream = 1;
 static int signoff;
 static const char *sign_commit;
+static int autostash;
 static int no_verify;
 
 static struct strategy all_strategy[] = {
@@ -286,6 +287,7 @@ static struct option builtin_merge_options[] = {
 	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
 	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
 	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+	OPT_AUTOSTASH(&autostash),
 	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
 	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
 	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ -475,6 +477,7 @@ static void finish(struct commit *head_commit,
 	/* Run a post-merge hook */
 	run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
 
+	apply_autostash(git_path_merge_autostash(the_repository));
 	strbuf_release(&reflog_message);
 }
 
@@ -634,6 +637,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 		return 0;
 	} else if (!strcmp(k, "gpg.mintrustlevel")) {
 		check_trust_level = 0;
+	} else if (!strcmp(k, "merge.autostash")) {
+		autostash = git_config_bool(k, v);
+		return 0;
 	}
 
 	status = fmt_merge_msg_config(k, v, cb);
@@ -1281,6 +1287,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	if (abort_current_merge) {
 		int nargc = 2;
 		const char *nargv[] = {"reset", "--merge", NULL};
+		struct strbuf stash_oid = STRBUF_INIT;
 
 		if (orig_argc != 2)
 			usage_msg_opt(_("--abort expects no arguments"),
@@ -1289,8 +1296,19 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		if (!file_exists(git_path_merge_head(the_repository)))
 			die(_("There is no merge to abort (MERGE_HEAD missing)."));
 
+		if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),
+			   READ_ONELINER_SKIP_IF_EMPTY)) {
+			strbuf_trim(&stash_oid);
+			unlink(git_path_merge_autostash(the_repository));
+		}
+
 		/* Invoke 'git reset --merge' */
 		ret = cmd_reset(nargc, nargv, prefix);
+
+		if (stash_oid.len)
+			apply_autostash_oid(stash_oid.buf);
+
+		strbuf_release(&stash_oid);
 		goto done;
 	}
 
@@ -1513,6 +1531,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 			goto done;
 		}
 
+		if (autostash)
+			create_autostash(the_repository,
+					 git_path_merge_autostash(the_repository),
+					 "merge");
 		if (checkout_fast_forward(the_repository,
 					  &head_commit->object.oid,
 					  &commit->object.oid,
@@ -1579,6 +1601,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	if (fast_forward == FF_ONLY)
 		die(_("Not possible to fast-forward, aborting."));
 
+	if (autostash)
+		create_autostash(the_repository,
+				 git_path_merge_autostash(the_repository),
+				 "merge");
+
 	/* We are going to make a new commit. */
 	git_committer_info(IDENT_STRICT);
 
diff --git a/builtin/rebase.c b/builtin/rebase.c
index b800c8bfca..e681ff58de 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -1371,8 +1371,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 		{ OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
 			N_("GPG-sign commits"),
 			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-		OPT_BOOL(0, "autostash", &options.autostash,
-			 N_("automatically stash/stash pop before and after")),
+		OPT_AUTOSTASH(&options.autostash),
 		OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
 				N_("add exec lines after each commit of the "
 				   "editable list")),
diff --git a/builtin/reset.c b/builtin/reset.c
index 18228c312e..0d155101f4 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -25,6 +25,7 @@
 #include "cache-tree.h"
 #include "submodule.h"
 #include "submodule-config.h"
+#include "sequencer.h"
 
 #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
 
@@ -437,8 +438,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		if (reset_type == HARD && !update_ref_status && !quiet)
 			print_new_head_line(lookup_commit_reference(the_repository, &oid));
 	}
-	if (!pathspec.nr)
+	if (!pathspec.nr) {
 		remove_branch_state(the_repository, 0);
+	}
 
 	return update_ref_status;
 }
diff --git a/parse-options.h b/parse-options.h
index fece5ba628..46af942093 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -336,5 +336,6 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);
 #define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
 #define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file"))
 #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character"))
+#define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after"))
 
 #endif
diff --git a/path.c b/path.c
index 88cf593007..d764738146 100644
--- a/path.c
+++ b/path.c
@@ -1535,5 +1535,6 @@ REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
 REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
 REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE")
 REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
+REPO_GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
 REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")
 REPO_GIT_PATH_FUNC(shallow, "shallow")
diff --git a/path.h b/path.h
index 14d6dcad16..1f1bf8f87a 100644
--- a/path.h
+++ b/path.h
@@ -177,11 +177,12 @@ struct path_cache {
 	const char *merge_rr;
 	const char *merge_mode;
 	const char *merge_head;
+	const char *merge_autostash;
 	const char *fetch_head;
 	const char *shallow;
 };
 
-#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 
 const char *git_path_cherry_pick_head(struct repository *r);
 const char *git_path_revert_head(struct repository *r);
@@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);
 const char *git_path_merge_rr(struct repository *r);
 const char *git_path_merge_mode(struct repository *r);
 const char *git_path_merge_head(struct repository *r);
+const char *git_path_merge_autostash(struct repository *r);
 const char *git_path_fetch_head(struct repository *r);
 const char *git_path_shallow(struct repository *r);
 
diff --git a/t/t3033-merge-toplevel.sh b/t/t3033-merge-toplevel.sh
index d314599428..e29c284b9b 100755
--- a/t/t3033-merge-toplevel.sh
+++ b/t/t3033-merge-toplevel.sh
@@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' '
 	test_must_fail git merge five
 '
 
+test_expect_success 'refuse two-project merge by default, quit before --autostash happens' '
+	t3033_reset &&
+	git reset --hard four &&
+	echo change >>one.t &&
+	git diff >expect &&
+	test_must_fail git merge --autostash five 2>err &&
+	test_i18ngrep ! "stash" err &&
+	git diff >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'two-project merge with --allow-unrelated-histories' '
 	t3033_reset &&
 	git reset --hard four &&
@@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' '
 	git diff --exit-code five
 '
 
+test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' '
+	t3033_reset &&
+	git reset --hard four &&
+	echo change >>one.t &&
+	git diff one.t >expect &&
+	git merge --allow-unrelated-histories --autostash five 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git diff one.t >actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 4fa0ef8e3b..d809310df1 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -30,13 +30,17 @@ Testing basic merge operations/option parsing.
 . "$TEST_DIRECTORY"/lib-gpg.sh
 
 test_write_lines 1 2 3 4 5 6 7 8 9 >file
+cp file file.orig
 test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1
+test_write_lines 1 2 '3 X' 4 5 6 7 8 9 >file.3
 test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5
 test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9
 test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y
 test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1
 test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
+test_write_lines '1 X' 2 3 4 5 6 7 8 '9 X' >result.1-9
 test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
+test_write_lines '1 X' 2 '3 X' 4 '5 X' 6 7 8 '9 X' >result.1-3-5-9
 test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z
 
 create_merge_msgs () {
@@ -675,6 +679,120 @@ test_expect_success 'refresh the index before merging' '
 	git merge c3
 '
 
+test_expect_success 'merge with --autostash' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9 &&
+	git merge --autostash c2 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git show HEAD:file >merge-result &&
+	test_cmp result.1-5 merge-result &&
+	test_cmp result.1-5-9 file
+'
+
+test_expect_success 'merge with merge.autoStash' '
+	test_config merge.autoStash true &&
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9 &&
+	git merge c2 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git show HEAD:file >merge-result &&
+	test_cmp result.1-5 merge-result &&
+	test_cmp result.1-5-9 file
+'
+
+test_expect_success 'fast-forward merge with --autostash' '
+	git reset --hard c0 &&
+	git merge-file file file.orig file.5 &&
+	git merge --autostash c1 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	test_cmp result.1-5 file
+'
+
+test_expect_success 'octopus merge with --autostash' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.3 &&
+	git merge --autostash c2 c3 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git show HEAD:file >merge-result &&
+	test_cmp result.1-5-9 merge-result &&
+	test_cmp result.1-3-5-9 file
+'
+
+test_expect_success 'conflicted merge with --autostash, --abort restores stash' '
+	git reset --hard c3 &&
+	cp file.1 file &&
+	test_must_fail git merge --autostash c7 &&
+	git merge --abort 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	test_cmp file.1 file
+'
+
+test_expect_success 'completed merge (git commit) with --no-commit and --autostash' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9 &&
+	git diff >expect &&
+	git merge --no-commit --autostash c2 &&
+	git stash show -p MERGE_AUTOSTASH >actual &&
+	test_cmp expect actual &&
+	git commit 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git show HEAD:file >merge-result &&
+	test_cmp result.1-5 merge-result &&
+	test_cmp result.1-5-9 file
+'
+
+test_expect_success 'completed merge (git merge --continue) with --no-commit and --autostash' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9 &&
+	git diff >expect &&
+	git merge --no-commit --autostash c2 &&
+	git stash show -p MERGE_AUTOSTASH >actual &&
+	test_cmp expect actual &&
+	git merge --continue 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git show HEAD:file >merge-result &&
+	test_cmp result.1-5 merge-result &&
+	test_cmp result.1-5-9 file
+'
+
+test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9 &&
+	git diff >expect &&
+	git merge --no-commit --autostash c2 &&
+	git stash show -p MERGE_AUTOSTASH >actual &&
+	test_cmp expect actual &&
+	git merge --abort 2>err &&
+	test_i18ngrep "Applied autostash." err &&
+	git diff >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9 &&
+	git diff >expect &&
+	git merge --no-commit --autostash c2 &&
+	git stash show -p MERGE_AUTOSTASH >actual &&
+	test_cmp expect actual &&
+	git reset --hard 2>err &&
+	test_i18ngrep "Autostash exists; creating a new stash entry." err &&
+	git diff --exit-code
+'
+
+test_expect_success 'merge with conflicted --autostash changes' '
+	git reset --hard c1 &&
+	git merge-file file file.orig file.9y &&
+	git diff >expect &&
+	test_when_finished "test_might_fail git stash drop" &&
+	git merge --autostash c3 2>err &&
+	test_i18ngrep "Applying autostash resulted in conflicts." err &&
+	git show HEAD:file >merge-result &&
+	test_cmp result.1-9 merge-result &&
+	git stash show -p >actual &&
+	test_cmp expect actual
+'
+
 cat >expected.branch <<\EOF
 Merge branch 'c5-branch' (early part)
 EOF
-- 
2.26.0.159.g23e2136ad0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 22/23] t5520: make test_pull_autostash() accept expect_parent_num
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (20 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 21/23] merge: teach --autostash option Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-04  1:11       ` [PATCH v4 23/23] pull: pass --autostash to merge Denton Liu
  2020-04-07 14:27       ` [PATCH v5 00/22] merge: learn --autostash Denton Liu
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 t/t5520-pull.sh | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 2f86fca042..f610dc14de 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -10,11 +10,13 @@ modify () {
 }
 
 test_pull_autostash () {
+	expect_parent_num="$1" &&
+	shift &&
 	git reset --hard before-rebase &&
 	echo dirty >new_file &&
 	git add new_file &&
 	git pull "$@" . copy &&
-	test_cmp_rev HEAD^ copy &&
+	test_cmp_rev HEAD^"$expect_parent_num" copy &&
 	echo dirty >expect &&
 	test_cmp expect new_file &&
 	echo "modified again" >expect &&
@@ -369,22 +371,22 @@ test_expect_success '--rebase fails with multiple branches' '
 
 test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' '
 	test_config rebase.autostash true &&
-	test_pull_autostash --rebase
+	test_pull_autostash 1 --rebase
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash=true' '
 	test_config rebase.autostash true &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash=false' '
 	test_config rebase.autostash false &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --autostash & rebase.autostash unset' '
 	test_unconfig rebase.autostash &&
-	test_pull_autostash --rebase --autostash
+	test_pull_autostash 1 --rebase --autostash
 '
 
 test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '
@@ -422,7 +424,7 @@ test_expect_success 'pull.rebase' '
 
 test_expect_success 'pull --autostash & pull.rebase=true' '
 	test_config pull.rebase true &&
-	test_pull_autostash --autostash
+	test_pull_autostash 1 --autostash
 '
 
 test_expect_success 'pull --no-autostash & pull.rebase=true' '
-- 
2.26.0.159.g23e2136ad0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v4 23/23] pull: pass --autostash to merge
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (21 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 22/23] t5520: make test_pull_autostash() accept expect_parent_num Denton Liu
@ 2020-04-04  1:11       ` Denton Liu
  2020-04-07 14:27       ` [PATCH v5 00/22] merge: learn --autostash Denton Liu
  23 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-04  1:11 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 Documentation/git-pull.txt      |  9 -------
 Documentation/merge-options.txt |  4 +--
 builtin/pull.c                  |  9 ++++---
 t/t5520-pull.sh                 | 43 +++++++++++++++++++++++++++------
 4 files changed, 42 insertions(+), 23 deletions(-)

diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index dfb901f8b8..ba3772de9f 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully.
 --no-rebase::
 	Override earlier --rebase.
 
---autostash::
---no-autostash::
-	Before starting rebase, stash local modifications away (see
-	linkgit:git-stash[1]) if needed, and apply the stash entry when
-	done. `--no-autostash` is useful to override the `rebase.autoStash`
-	configuration variable (see linkgit:git-config[1]).
-+
-This option is only valid when "--rebase" is used.
-
 Options related to fetching
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 3985e6d4a9..48bfcda084 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -155,6 +155,8 @@ ifndef::git-pull[]
 	Note that not all merge strategies may support progress
 	reporting.
 
+endif::git-pull[]
+
 --autostash::
 --no-autostash::
 	Automatically create a temporary stash entry before the operation
@@ -163,8 +165,6 @@ ifndef::git-pull[]
 	with care: the final stash application after a successful
 	merge might result in non-trivial conflicts.
 
-endif::git-pull[]
-
 --allow-unrelated-histories::
 	By default, `git merge` command refuses to merge histories
 	that do not share a common ancestor.  This option can be
diff --git a/builtin/pull.c b/builtin/pull.c
index 3e624d1e00..9beb4841d1 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -163,7 +163,7 @@ static struct option pull_options[] = {
 		N_("verify that the named commit has a valid GPG signature"),
 		PARSE_OPT_NOARG),
 	OPT_BOOL(0, "autostash", &opt_autostash,
-		N_("automatically stash/stash pop before and after rebase")),
+		N_("automatically stash/stash pop before and after")),
 	OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
 		N_("merge strategy to use"),
 		0),
@@ -661,6 +661,10 @@ static int run_merge(void)
 	argv_array_pushv(&args, opt_strategy_opts.argv);
 	if (opt_gpg_sign)
 		argv_array_push(&args, opt_gpg_sign);
+	if (opt_autostash == 0)
+		argv_array_push(&args, "--no-autostash");
+	else if (opt_autostash == 1)
+		argv_array_push(&args, "--autostash");
 	if (opt_allow_unrelated_histories > 0)
 		argv_array_push(&args, "--allow-unrelated-histories");
 
@@ -908,9 +912,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 	if (get_oid("HEAD", &orig_head))
 		oidclr(&orig_head);
 
-	if (!opt_rebase && opt_autostash != -1)
-		die(_("--[no-]autostash option is only valid with --rebase."));
-
 	autostash = config_autostash;
 	if (opt_rebase) {
 		if (opt_autostash != -1)
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index f610dc14de..37535d63a9 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -28,7 +28,7 @@ test_pull_autostash_fail () {
 	echo dirty >new_file &&
 	git add new_file &&
 	test_must_fail git pull "$@" . copy 2>err &&
-	test_i18ngrep "uncommitted changes." err
+	test_i18ngrep "\(uncommitted changes.\)\|\(overwritten by merge:\)" err
 }
 
 test_expect_success setup '
@@ -404,13 +404,40 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' '
 	test_pull_autostash_fail --rebase --no-autostash
 '
 
-for i in --autostash --no-autostash
-do
-	test_expect_success "pull $i (without --rebase) is illegal" '
-		test_must_fail git pull $i . copy 2>err &&
-		test_i18ngrep "only valid with --rebase" err
-	'
-done
+test_expect_success 'pull succeeds with dirty working directory and merge.autostash set' '
+	test_config merge.autostash true &&
+	test_pull_autostash 2
+'
+
+test_expect_success 'pull --autostash & merge.autostash=true' '
+	test_config merge.autostash true &&
+	test_pull_autostash 2 --autostash
+'
+
+test_expect_success 'pull --autostash & merge.autostash=false' '
+	test_config merge.autostash false &&
+	test_pull_autostash 2 --autostash
+'
+
+test_expect_success 'pull --autostash & merge.autostash unset' '
+	test_unconfig merge.autostash &&
+	test_pull_autostash 2 --autostash
+'
+
+test_expect_success 'pull --no-autostash & merge.autostash=true' '
+	test_config merge.autostash true &&
+	test_pull_autostash_fail --no-autostash
+'
+
+test_expect_success 'pull --no-autostash & merge.autostash=false' '
+	test_config merge.autostash false &&
+	test_pull_autostash_fail --no-autostash
+'
+
+test_expect_success 'pull --no-autostash & merge.autostash unset' '
+	test_unconfig merge.autostash &&
+	test_pull_autostash_fail --no-autostash
+'
 
 test_expect_success 'pull.rebase' '
 	git reset --hard before-rebase &&
-- 
2.26.0.159.g23e2136ad0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 03/23] sequencer: stop leaking buf
  2020-04-04  1:11       ` [PATCH v4 03/23] sequencer: stop leaking buf Denton Liu
@ 2020-04-05 21:33         ` Junio C Hamano
  2020-04-05 21:37           ` Junio C Hamano
  2020-04-05 23:42           ` Denton Liu
  0 siblings, 2 replies; 152+ messages in thread
From: Junio C Hamano @ 2020-04-05 21:33 UTC (permalink / raw)
  To: Denton Liu
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Phillip Wood

Denton Liu <liu.denton@gmail.com> writes:

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

I do not think the above makes much sense.  _release() will free the
piece of memory occupied by .buf and reinitializes the strbuf, and
in doing so there is no leak.  read_oneliner() called after it
allocates and reads into there.  Freeing the resource needs to be
done after the caller is done with what read_oneliner() has read.

There is a leak, because read_oneliner() gets called and before the
code has a chance to do strbuf_reease() there is an error return.
That does not have anything to do with the call to strbuf_release()
in the middle of the function.

But that leak has nothing to do with the release called before
read_oneliner().

> Always clean up the strbuf by going to `done_rebase_i` whether or not
> we return an error.
>
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>  sequencer.c | 13 +++++++++----
>  1 file changed, 9 insertions(+), 4 deletions(-)
>
> diff --git a/sequencer.c b/sequencer.c
> index e528225e78..faab0b13e8 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -2485,6 +2485,7 @@ static int read_populate_opts(struct replay_opts *opts)
>  {
>  	if (is_rebase_i(opts)) {
>  		struct strbuf buf = STRBUF_INIT;
> +		int ret = 0;
>  
>  		if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
>  			if (!starts_with(buf.buf, "-S"))
> @@ -2525,7 +2526,7 @@ static int read_populate_opts(struct replay_opts *opts)
>  			opts->keep_redundant_commits = 1;
>  
>  		read_strategy_opts(opts, &buf);
> -		strbuf_release(&buf);
> +		strbuf_reset(&buf);

As read_oneliner() has a strbuf_reset() at the beginning *anyway*,
why not just get rid of the call to _release() here instead?  After
all, there is no _release() or _reset() before the call to read_oneliner()
in the next hunk, or two calls to read_oneliner() inside the
read_strategy_opts() function called in the above.

>  		if (read_oneliner(&opts->current_fixups,
>  				  rebase_path_current_fixups(), 1)) {
> @@ -2538,12 +2539,16 @@ static int read_populate_opts(struct replay_opts *opts)
>  		}
>  
>  		if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {
> -			if (get_oid_hex(buf.buf, &opts->squash_onto) < 0)
> -				return error(_("unusable squash-onto"));
> +			if (get_oid_hex(buf.buf, &opts->squash_onto) < 0) {
> +				ret = error(_("unusable squash-onto"));
> +				goto done_rebase_i;
> +			}
>  			opts->have_squash_onto = 1;
>  		}
>  
> -		return 0;
> +done_rebase_i:
> +		strbuf_release(&buf);
> +		return ret;

This part indeed IS the right fix to the existing leak.

>  	}
>  
>  	if (!file_exists(git_path_opts_file()))

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 03/23] sequencer: stop leaking buf
  2020-04-05 21:33         ` Junio C Hamano
@ 2020-04-05 21:37           ` Junio C Hamano
  2020-04-05 23:42           ` Denton Liu
  1 sibling, 0 replies; 152+ messages in thread
From: Junio C Hamano @ 2020-04-05 21:37 UTC (permalink / raw)
  To: Denton Liu
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Phillip Wood

Junio C Hamano <gitster@pobox.com> writes:

>>  		read_strategy_opts(opts, &buf);
>> -		strbuf_release(&buf);
>> +		strbuf_reset(&buf);
>
> As read_oneliner() has a strbuf_reset() at the beginning *anyway*,
> why not just get rid of the call to _release() here instead?  After
> all, there is no _release() or _reset() before the call to read_oneliner()
> in the next hunk, or two calls to read_oneliner() inside the
> read_strategy_opts() function called in the above.

Ah, I was reading the state of applying the series thru to the end.
At this point, there is no reset there, so you'd need to call reset
to ensure that the next read_oneliner() on &buf will not append, but
restart from the beginning.

>>  		if (read_oneliner(&buf, rebase_path_squash_onto(), 0)) {
>> -			if (get_oid_hex(buf.buf, &opts->squash_onto) < 0)
>> -				return error(_("unusable squash-onto"));

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner()
  2020-04-04  1:11       ` [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner() Denton Liu
@ 2020-04-05 21:46         ` Junio C Hamano
  2020-04-06  1:39           ` Denton Liu
  2020-04-06 14:03         ` Phillip Wood
  1 sibling, 1 reply; 152+ messages in thread
From: Junio C Hamano @ 2020-04-05 21:46 UTC (permalink / raw)
  To: Denton Liu
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Phillip Wood

Denton Liu <liu.denton@gmail.com> writes:

> @@ -2471,7 +2467,6 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
>  
>  static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
>  {
> -	strbuf_reset(buf);
>  	if (!read_oneliner(buf, rebase_path_strategy(), 0))
>  		return;
>  	opts->strategy = strbuf_detach(buf, NULL);
> @@ -2494,7 +2489,6 @@ static int read_populate_opts(struct replay_opts *opts)
>  				free(opts->gpg_sign);
>  				opts->gpg_sign = xstrdup(buf.buf + 2);
>  			}
> -			strbuf_reset(&buf);
>  		}
>  
>  		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
> @@ -2526,7 +2520,6 @@ static int read_populate_opts(struct replay_opts *opts)
>  			opts->keep_redundant_commits = 1;
>  
>  		read_strategy_opts(opts, &buf);
> -		strbuf_reset(&buf);
>  

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

Is this conversion correct around here?  read_oneliner() used to
"append" what was read from the file to what is already in the
strbuf, and many strbuf_reset() in this function was because these
callers of read_oneliner() in this function that has strbuf_reset()
immediately before did *not* want the "append" semantics.  But this
one looks different.  Where in the original does the current_fixups
strbuf get emptied for this read_oneliner() to ignore the previous
contents?  Or are we relying on the caller not to have done anything
to current_fixups before it calls this function?

In other words, the original behaviour of read_oneliner() having the
"append" semantics suggests me that there were callers that wanted
to keep the current contents and append---this current_fixups may
not be one of them, but nevertheless, changing the semantics of the
function from "append" to "discard and read from scratch" without
vetting all the existing callers smells iffy to me.


^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 03/23] sequencer: stop leaking buf
  2020-04-05 21:33         ` Junio C Hamano
  2020-04-05 21:37           ` Junio C Hamano
@ 2020-04-05 23:42           ` Denton Liu
  1 sibling, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-05 23:42 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Phillip Wood

Hi Junio,

On Sun, Apr 05, 2020 at 02:33:08PM -0700, Junio C Hamano wrote:
> Denton Liu <liu.denton@gmail.com> writes:
> 
> > In read_populate_opts(), we call read_oneliner() _after_ calling
> > strbuf_release(). This means that `buf` is leaked at the end of the
> > function.
> 
> I do not think the above makes much sense.  _release() will free the
> piece of memory occupied by .buf and reinitializes the strbuf, and
> in doing so there is no leak.  read_oneliner() called after it
> allocates and reads into there.  Freeing the resource needs to be
> done after the caller is done with what read_oneliner() has read.
> 
> There is a leak, because read_oneliner() gets called and before the
> code has a chance to do strbuf_reease() there is an error return.
> That does not have anything to do with the call to strbuf_release()
> in the middle of the function.

There is also a leak in the case where we don't take the early return
since, before this patch, the read_oneliner() has no strbuf_release()
following it which means buf gets leaked.

> But that leak has nothing to do with the release called before
> read_oneliner().

What I meant to say in my paragraph is essentially, "strbuf_release() is
placed too early, there is a read_oneliner() after that nullifies the
effects of strbuf_release()". I can rewrite this to be more clear.

Thanks,

Denton

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner()
  2020-04-05 21:46         ` Junio C Hamano
@ 2020-04-06  1:39           ` Denton Liu
  0 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-06  1:39 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Phillip Wood

Hi Junio,

On Sun, Apr 05, 2020 at 02:46:47PM -0700, Junio C Hamano wrote:
> Denton Liu <liu.denton@gmail.com> writes:
> 
> > @@ -2471,7 +2467,6 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)
> >  
> >  static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
> >  {
> > -	strbuf_reset(buf);
> >  	if (!read_oneliner(buf, rebase_path_strategy(), 0))
> >  		return;
> >  	opts->strategy = strbuf_detach(buf, NULL);
> > @@ -2494,7 +2489,6 @@ static int read_populate_opts(struct replay_opts *opts)
> >  				free(opts->gpg_sign);
> >  				opts->gpg_sign = xstrdup(buf.buf + 2);
> >  			}
> > -			strbuf_reset(&buf);
> >  		}
> >  
> >  		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
> > @@ -2526,7 +2520,6 @@ static int read_populate_opts(struct replay_opts *opts)
> >  			opts->keep_redundant_commits = 1;
> >  
> >  		read_strategy_opts(opts, &buf);
> > -		strbuf_reset(&buf);
> >  
> 
> >  		if (read_oneliner(&opts->current_fixups,
> >  				  rebase_path_current_fixups(), 1)) {
> 
> Is this conversion correct around here?  read_oneliner() used to
> "append" what was read from the file to what is already in the
> strbuf, and many strbuf_reset() in this function was because these
> callers of read_oneliner() in this function that has strbuf_reset()
> immediately before did *not* want the "append" semantics.  But this
> one looks different.  Where in the original does the current_fixups
> strbuf get emptied for this read_oneliner() to ignore the previous
> contents?  Or are we relying on the caller not to have done anything
> to current_fixups before it calls this function?

As far as I can tell, opts->current_fixups is always empty when
read_oneliner() is called here.

> In other words, the original behaviour of read_oneliner() having the
> "append" semantics suggests me that there were callers that wanted
> to keep the current contents and append---this current_fixups may
> not be one of them, but nevertheless, changing the semantics of the
> function from "append" to "discard and read from scratch" without
> vetting all the existing callers smells iffy to me.

Before making this change, I manually checked all invocations of
read_oneliner() and ensured that they all passed in an empty strbuf.
Same thing with "rebase: use read_oneliner()", I manually checked all of
those invocations as well. It's quite possible that I made a mistake,
though.

To be doubly sure that I caught everything, I ran the test suite on
'master' with this patch:

	diff --git a/builtin/rebase.c b/builtin/rebase.c
	index 27a07d4e78..a0c03dd1d6 100644
	--- a/builtin/rebase.c
	+++ b/builtin/rebase.c
	@@ -589,6 +589,9 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o
	 /* Read one file, then strip line endings */
	 static int read_one(const char *path, struct strbuf *buf)
	 {
	+	if (buf->len)
	+		BUG("rebase change bad: %s", buf->buf);
	+
		if (strbuf_read_file(buf, path, 0) < 0)
			return error_errno(_("could not read '%s'"), path);
		strbuf_trim_trailing_newline(buf);
	diff --git a/sequencer.c b/sequencer.c
	index 6fd2674632..d7bc5c9c95 100644
	--- a/sequencer.c
	+++ b/sequencer.c
	@@ -433,6 +433,9 @@ static int read_oneliner(struct strbuf *buf,
	 {
		int orig_len = buf->len;
	 
	+	if (buf->len)
	+		BUG("sequencer change bad: %s", buf->buf);
	+
		if (!file_exists(path))
			return 0;

Thanks,

Denton

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner()
  2020-04-04  1:11       ` [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner() Denton Liu
  2020-04-05 21:46         ` Junio C Hamano
@ 2020-04-06 14:03         ` Phillip Wood
  2020-04-06 14:42           ` Phillip Wood
  1 sibling, 1 reply; 152+ messages in thread
From: Phillip Wood @ 2020-04-06 14:03 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 04/04/2020 02:11, Denton Liu wrote:
> In the original read_oneliner() logic, we duplicated the logic for
> strbuf_trim_trailing_newline() with one exception: instead of preventing
> buffer accesses below index 0, it would prevent buffer accesses below
> index `orig_len`. Although this is correct, it isn't worth having the
> duplicated logic around.
> 
> Reset `buf` before using it then replace the trimming logic with
> strbuf_trim().

I should have picked up on this before but this changes the semantics of 
the function as it strips all whitespace from the start and end of the 
strbuf. Above you talked about using strbuf_trim_trailing_newline() 
instead which would not change the semantics of this function. You could 
test to see if we've read anything and only call 
strbuf_trim_trailing_newline() in that case without messing with 
strbuf_reset(). (there is a corner case where if the buffer ends with 
'\r' when the function is called and it reads a single '\n' then the 
'\r' would be stripped as well but I think that is unlikely to happen in 
the wild)

Best Wishes

Phillip

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner()
  2020-04-06 14:03         ` Phillip Wood
@ 2020-04-06 14:42           ` Phillip Wood
  2020-04-07 13:56             ` Denton Liu
  0 siblings, 1 reply; 152+ messages in thread
From: Phillip Wood @ 2020-04-06 14:42 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 06/04/2020 15:03, Phillip Wood wrote:
> Hi Denton
> 
> On 04/04/2020 02:11, Denton Liu wrote:
>> In the original read_oneliner() logic, we duplicated the logic for
>> strbuf_trim_trailing_newline() with one exception: instead of preventing
>> buffer accesses below index 0, it would prevent buffer accesses below
>> index `orig_len`. Although this is correct, it isn't worth having the
>> duplicated logic around.
>>
>> Reset `buf` before using it then replace the trimming logic with
>> strbuf_trim().
> 
> I should have picked up on this before but this changes the semantics of 
> the function as it strips all whitespace from the start and end of the 
> strbuf. Above you talked about using strbuf_trim_trailing_newline() 
> instead which would not change the semantics of this function. You could 
> test to see if we've read anything and only call 
> strbuf_trim_trailing_newline() in that case without messing with 
> strbuf_reset(). (there is a corner case where if the buffer ends with 
> '\r' when the function is called and it reads a single '\n' then the 
> '\r' would be stripped as well but I think that is unlikely to happen in 
> the wild)

I'd also be fine with dropping this patch and leaving the trimming code 
as is

Best Wishes

Phillip

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 07/23] sequencer: configurably warn on non-existent files
  2020-04-04  1:11       ` [PATCH v4 07/23] sequencer: configurably warn on non-existent files Denton Liu
@ 2020-04-06 14:45         ` Phillip Wood
  0 siblings, 0 replies; 152+ messages in thread
From: Phillip Wood @ 2020-04-06 14:45 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

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

Now that you've updated the function in a previous patch the 
file_exists() check referred to in the message no longer exists

> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>   sequencer.c | 4 +++-
>   1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/sequencer.c b/sequencer.c
> index 6c26d61670..b771e8f4ca 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename,
>   }
>   
>   #define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
> +#define READ_ONELINER_WARN_MISSING (1 << 1)
>   
>   /*
>    * Resets a strbuf then reads a file that was presumably written by a shell
> @@ -435,7 +436,8 @@ static int read_oneliner(struct strbuf *buf,
>   {
>   	strbuf_reset(buf);
>   	if (strbuf_read_file(buf, path, 0) < 0) {
> -		if (errno != ENOENT && errno != ENOTDIR)
> +		if ((flags & READ_ONELINER_WARN_MISSING) ||
> +				(errno != ENOENT && errno != ENOTDIR))

We don't need the extra indentation for '(errno != ...', it should align 
with the second '(' on the line above I think

Best Wishes

Phillip

>   			warning_errno(_("could not read '%s'"), path);
>   		return 0;
>   	}
> 

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 10/23] sequencer: make apply_rebase() accept a path
  2020-04-04  1:11       ` [PATCH v4 10/23] sequencer: make apply_rebase() accept a path Denton Liu
@ 2020-04-06 15:07         ` Phillip Wood
  0 siblings, 0 replies; 152+ messages in thread
From: Phillip Wood @ 2020-04-06 15:07 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 04/04/2020 02:11, Denton Liu wrote:
> In order to make apply_rebase()

I think you mean apply_autostash() (in the subject as well)

Best Wishes

Phillip

> more generic for future extraction, make
> it accept a `path` argument so that the location from where to read the
> reference to the autostash commit can be customized. Remove the `opts`
> argument since it was unused before anyway.
> 
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>   sequencer.c | 14 +++++++-------
>   1 file changed, 7 insertions(+), 7 deletions(-)
> 
> diff --git a/sequencer.c b/sequencer.c
> index d1d59867ae..ae9efe5825 100644
> --- a/sequencer.c
> +++ b/sequencer.c
> @@ -3649,13 +3649,13 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
>   	return -1;
>   }
>   
> -static int apply_autostash(struct replay_opts *opts)
> +static int apply_autostash(const char *path)
>   {
>   	struct strbuf stash_sha1 = STRBUF_INIT;
>   	struct child_process child = CHILD_PROCESS_INIT;
>   	int ret = 0;
>   
> -	if (!read_oneliner(&stash_sha1, rebase_path_autostash(),
> +	if (!read_oneliner(&stash_sha1, path,
>   			   READ_ONELINER_SKIP_IF_EMPTY)) {
>   		strbuf_release(&stash_sha1);
>   		return 0;
> @@ -3748,7 +3748,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,
>   		return error(_("%s: not a valid OID"), orig_head);
>   
>   	if (run_git_checkout(r, opts, oid_to_hex(onto), action)) {
> -		apply_autostash(opts);
> +		apply_autostash(rebase_path_autostash());
>   		sequencer_remove_state(opts);
>   		return error(_("could not detach HEAD"));
>   	}
> @@ -4061,7 +4061,7 @@ static int pick_commits(struct repository *r,
>   				run_command(&hook);
>   			}
>   		}
> -		apply_autostash(opts);
> +		apply_autostash(rebase_path_autostash());
>   
>   		if (!opts->quiet) {
>   			if (!opts->verbose)
> @@ -5070,7 +5070,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
>   		todo_list_add_exec_commands(todo_list, commands);
>   
>   	if (count_commands(todo_list) == 0) {
> -		apply_autostash(opts);
> +		apply_autostash(rebase_path_autostash());
>   		sequencer_remove_state(opts);
>   
>   		return error(_("nothing to do"));
> @@ -5081,12 +5081,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
>   	if (res == -1)
>   		return -1;
>   	else if (res == -2) {
> -		apply_autostash(opts);
> +		apply_autostash(rebase_path_autostash());
>   		sequencer_remove_state(opts);
>   
>   		return -1;
>   	} else if (res == -3) {
> -		apply_autostash(opts);
> +		apply_autostash(rebase_path_autostash());
>   		sequencer_remove_state(opts);
>   		todo_list_release(&new_todo);
>   
> 

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 19/23] sequencer: implement save_autostash()
  2020-04-04  1:11       ` [PATCH v4 19/23] sequencer: implement save_autostash() Denton Liu
@ 2020-04-06 15:15         ` Phillip Wood
  0 siblings, 0 replies; 152+ messages in thread
From: Phillip Wood @ 2020-04-06 15:15 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

On 04/04/2020 02:11, Denton Liu wrote:
> Extract common functionality of apply_autostash() into
> apply_save_autostash() and use it to implement save_autostash(). This
> function will be used in a future commit.
> 
> The difference between save_autostash() and apply_autostash() is that
> the latter does not try to apply the stash. 

the latter is apply_autostash() - surely that tries to apply the stash?

> It skips that step and
> just stores the created entry in the stash reflog.

I think we want save_autostash() so we can call it to save the stash 
when running a reset - perhaps we should mention that in the commit message.

Best Wishes

Phillip

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 21/23] merge: teach --autostash option
  2020-04-04  1:11       ` [PATCH v4 21/23] merge: teach --autostash option Denton Liu
@ 2020-04-06 15:20         ` Phillip Wood
  2020-04-07 13:09           ` Denton Liu
  0 siblings, 1 reply; 152+ messages in thread
From: Phillip Wood @ 2020-04-06 15:20 UTC (permalink / raw)
  To: Denton Liu, Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Denton

Thanks for re-rolling, I think this addresses all of my previous 
concerns, I've got a few minor comments below and a query about 'git 
merge --quit'

On 04/04/2020 02:11, Denton Liu wrote:
> In rebase, one can pass the `--autostash` option to cause the worktree
> to be automatically stashed before continuing with the rebase. This
> option is missing in merge, however.
> 
> Implement the `--autostash` option and corresponding `merge.autoStash`
> option in merge which stashes before merging and then pops after.
> 
> This option is useful when a developer has some local changes on a topic
> branch but they realize that their work depends on another branch.
> Previously, they had to run something like
> 
> 	git fetch ...
> 	git stash push
> 	git merge FETCH_HEAD
> 	git stash pop
> 
> but now, that is reduced to
> 
> 	git fetch ...
> 	git merge --autostash FETCH_HEAD
> 
> When an autostash is generated, it is automatically reapplied to the
> worktree only in three explicit situations:
> 
> 	1. An incomplete merge is commit using `git commit`.
> 	2. A merge completes successfully.
> 	3. A merge is aborted using `git merge --abort`.
> 
> In all other situations where the merge state is removed using
> remove_merge_branch_state() such as aborting a merge via
> `git reset --hard`, the autostash is saved into the stash reflog
> instead keeping the worktree clean.
> 
> Suggested-by: Alban Gruin <alban.gruin@gmail.com>
> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
>   Documentation/config/merge.txt  |  10 +++
>   Documentation/git-merge.txt     |   6 +-
>   Documentation/merge-options.txt |   8 +++
>   branch.c                        |   1 +
>   builtin/commit.c                |   2 +
>   builtin/merge.c                 |  27 ++++++++
>   builtin/rebase.c                |   3 +-
>   builtin/reset.c                 |   4 +-
>   parse-options.h                 |   1 +
>   path.c                          |   1 +
>   path.h                          |   4 +-
>   t/t3033-merge-toplevel.sh       |  22 ++++++
>   t/t7600-merge.sh                | 118 ++++++++++++++++++++++++++++++++
>   13 files changed, 201 insertions(+), 6 deletions(-)
> 
> diff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt
> index 6a313937f8..cb2ed58907 100644
> --- a/Documentation/config/merge.txt
> +++ b/Documentation/config/merge.txt
> @@ -70,6 +70,16 @@ merge.stat::
>   	Whether to print the diffstat between ORIG_HEAD and the merge result
>   	at the end of the merge.  True by default.
>   
> +merge.autoStash::
> +	When set to true, automatically create a temporary stash entry
> +	before the operation begins, and apply it after the operation
> +	ends.  This means that you can run merge on a dirty worktree.
> +	However, use with care: the final stash application after a
> +	successful merge might result in non-trivial conflicts.
> +	This option can be overridden by the `--no-autostash` and
> +	`--autostash` options of linkgit:git-merge[1].
> +	Defaults to false.
> +
>   merge.tool::
>   	Controls which merge tool is used by linkgit:git-mergetool[1].
>   	The list below shows the valid built-in values.
> diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
> index 092529c619..9cb14f0059 100644
> --- a/Documentation/git-merge.txt
> +++ b/Documentation/git-merge.txt
> @@ -94,7 +94,8 @@ will be appended to the specified message.
>   
>   --abort::
>   	Abort the current conflict resolution process, and
> -	try to reconstruct the pre-merge state.
> +	try to reconstruct the pre-merge state. If an autostash entry is
> +	present, apply it back to the worktree.

Being picky I'd suggest deleting 'back'

>   +
>   If there were uncommitted worktree changes present when the merge
>   started, 'git merge --abort' will in some cases be unable to
> @@ -102,7 +103,8 @@ reconstruct these changes. It is therefore recommended to always
>   commit or stash your changes before running 'git merge'.
>   +
>   'git merge --abort' is equivalent to 'git reset --merge' when
> -`MERGE_HEAD` is present.
> +`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present where
> +the stash entry is applied to the worktree.

Perhaps
+`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present in
+which case 'git merge --abort' applies the stash entry to the worktree
+whereas 'git reset --merge' will save the stashed changes in the stash
+reflog.

>   --quit::
>   	Forget about the current merge in progress. Leave the index

Should we be saving the stash if the user runs 'git merge --quit'?

> diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
> index 40dc4f5e8c..3985e6d4a9 100644
> --- a/Documentation/merge-options.txt
> +++ b/Documentation/merge-options.txt
> @@ -155,6 +155,14 @@ ifndef::git-pull[]
>   	Note that not all merge strategies may support progress
>   	reporting.
>   
> +--autostash::
> +--no-autostash::
> +	Automatically create a temporary stash entry before the operation
> +	begins, and apply it after the operation ends.  This means
> +	that you can run the operation on a dirty worktree.  However, use
> +	with care: the final stash application after a successful
> +	merge might result in non-trivial conflicts.
> +
>   endif::git-pull[]
>   
>   --allow-unrelated-histories::
> diff --git a/branch.c b/branch.c
> index 579494738a..2d9e7675a6 100644
> --- a/branch.c
> +++ b/branch.c
> @@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
>   	unlink(git_path_merge_rr(r));
>   	unlink(git_path_merge_msg(r));
>   	unlink(git_path_merge_mode(r));
> +	save_autostash(git_path_merge_autostash(r));
>   }
>   
>   void remove_branch_state(struct repository *r, int verbose)
> diff --git a/builtin/commit.c b/builtin/commit.c
> index 7ba33a3bec..c1b25d2954 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -1713,6 +1713,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
>   				     &oid, flags);
>   	}
>   
> +	apply_autostash(git_path_merge_autostash(the_repository));
> +

I think this is the right place to apply the stash. There is a corner 
case - if commit_index_files() fails then we leave MERGE_AUTOSTASH lying 
around. I think that is probably the best we can do - if we cannot write 
the new index file to disk then saving or applying the stash would 
probably fail as well.

>   	UNLEAK(err);
>   	UNLEAK(sb);
>   	return 0;
> diff --git a/builtin/merge.c b/builtin/merge.c
> index d127d2225f..f2f8069f85 100644
> --- a/builtin/merge.c
> +++ b/builtin/merge.c
> @@ -82,6 +82,7 @@ static int show_progress = -1;
>   static int default_to_upstream = 1;
>   static int signoff;
>   static const char *sign_commit;
> +static int autostash;
>   static int no_verify;
>   
>   static struct strategy all_strategy[] = {
> @@ -286,6 +287,7 @@ static struct option builtin_merge_options[] = {
>   	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
>   	{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
>   	  N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
> +	OPT_AUTOSTASH(&autostash),
>   	OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
>   	OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
>   	OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
> @@ -475,6 +477,7 @@ static void finish(struct commit *head_commit,
>   	/* Run a post-merge hook */
>   	run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
>   
> +	apply_autostash(git_path_merge_autostash(the_repository));

I think this is the right place to apply it

>   	strbuf_release(&reflog_message);
>   }
>   
> @@ -634,6 +637,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
>   		return 0;
>   	} else if (!strcmp(k, "gpg.mintrustlevel")) {
>   		check_trust_level = 0;
> +	} else if (!strcmp(k, "merge.autostash")) {
> +		autostash = git_config_bool(k, v);
> +		return 0;
>   	}
>   
>   	status = fmt_merge_msg_config(k, v, cb);
> @@ -1281,6 +1287,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
>   	if (abort_current_merge) {
>   		int nargc = 2;
>   		const char *nargv[] = {"reset", "--merge", NULL};
> +		struct strbuf stash_oid = STRBUF_INIT;
>   
>   		if (orig_argc != 2)
>   			usage_msg_opt(_("--abort expects no arguments"),
> @@ -1289,8 +1296,19 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
>   		if (!file_exists(git_path_merge_head(the_repository)))
>   			die(_("There is no merge to abort (MERGE_HEAD missing)."));
>   
> +		if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),
> +			   READ_ONELINER_SKIP_IF_EMPTY)) {

dodgy indentation

> +			strbuf_trim(&stash_oid);

do we really need to trim the buffer?

> +			unlink(git_path_merge_autostash(the_repository));
> +		}
> +
>   		/* Invoke 'git reset --merge' */
>   		ret = cmd_reset(nargc, nargv, prefix);
> +
> +		if (stash_oid.len)
> +			apply_autostash_oid(stash_oid.buf);
> +
> +		strbuf_release(&stash_oid);
>   		goto done;
>   	}
>   
> @@ -1513,6 +1531,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
>   			goto done;
>   		}
>   
> +		if (autostash)
> +			create_autostash(the_repository,
> +					 git_path_merge_autostash(the_repository),
> +					 "merge");
>   		if (checkout_fast_forward(the_repository,
>   					  &head_commit->object.oid,
>   					  &commit->object.oid,
> @@ -1579,6 +1601,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
>   	if (fast_forward == FF_ONLY)
>   		die(_("Not possible to fast-forward, aborting."));
>   
> +	if (autostash)
> +		create_autostash(the_repository,
> +				 git_path_merge_autostash(the_repository),
> +				 "merge");
> +
>   	/* We are going to make a new commit. */
>   	git_committer_info(IDENT_STRICT);
>   
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index b800c8bfca..e681ff58de 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -1371,8 +1371,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
>   		{ OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
>   			N_("GPG-sign commits"),
>   			PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
> -		OPT_BOOL(0, "autostash", &options.autostash,
> -			 N_("automatically stash/stash pop before and after")),
> +		OPT_AUTOSTASH(&options.autostash),
>   		OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
>   				N_("add exec lines after each commit of the "
>   				   "editable list")),
> diff --git a/builtin/reset.c b/builtin/reset.c
> index 18228c312e..0d155101f4 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -25,6 +25,7 @@
>   #include "cache-tree.h"
>   #include "submodule.h"
>   #include "submodule-config.h"
> +#include "sequencer.h"
>   
>   #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
>   
> @@ -437,8 +438,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>   		if (reset_type == HARD && !update_ref_status && !quiet)
>   			print_new_head_line(lookup_commit_reference(the_repository, &oid));
>   	}
> -	if (!pathspec.nr)
> +	if (!pathspec.nr) {
>   		remove_branch_state(the_repository, 0);
> +	}

No need to add these braces any longer

Best Wishes

Phillip

>   
>   	return update_ref_status;
>   }
> diff --git a/parse-options.h b/parse-options.h
> index fece5ba628..46af942093 100644
> --- a/parse-options.h
> +++ b/parse-options.h
> @@ -336,5 +336,6 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);
>   #define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
>   #define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file"))
>   #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character"))
> +#define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after"))
>   
>   #endif
> diff --git a/path.c b/path.c
> index 88cf593007..d764738146 100644
> --- a/path.c
> +++ b/path.c
> @@ -1535,5 +1535,6 @@ REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
>   REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
>   REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE")
>   REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
> +REPO_GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
>   REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")
>   REPO_GIT_PATH_FUNC(shallow, "shallow")
> diff --git a/path.h b/path.h
> index 14d6dcad16..1f1bf8f87a 100644
> --- a/path.h
> +++ b/path.h
> @@ -177,11 +177,12 @@ struct path_cache {
>   	const char *merge_rr;
>   	const char *merge_mode;
>   	const char *merge_head;
> +	const char *merge_autostash;
>   	const char *fetch_head;
>   	const char *shallow;
>   };
>   
> -#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
> +#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
>   
>   const char *git_path_cherry_pick_head(struct repository *r);
>   const char *git_path_revert_head(struct repository *r);
> @@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);
>   const char *git_path_merge_rr(struct repository *r);
>   const char *git_path_merge_mode(struct repository *r);
>   const char *git_path_merge_head(struct repository *r);
> +const char *git_path_merge_autostash(struct repository *r);
>   const char *git_path_fetch_head(struct repository *r);
>   const char *git_path_shallow(struct repository *r);
>   
> diff --git a/t/t3033-merge-toplevel.sh b/t/t3033-merge-toplevel.sh
> index d314599428..e29c284b9b 100755
> --- a/t/t3033-merge-toplevel.sh
> +++ b/t/t3033-merge-toplevel.sh
> @@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' '
>   	test_must_fail git merge five
>   '
>   
> +test_expect_success 'refuse two-project merge by default, quit before --autostash happens' '
> +	t3033_reset &&
> +	git reset --hard four &&
> +	echo change >>one.t &&
> +	git diff >expect &&
> +	test_must_fail git merge --autostash five 2>err &&
> +	test_i18ngrep ! "stash" err &&
> +	git diff >actual &&
> +	test_cmp expect actual
> +'
> +
>   test_expect_success 'two-project merge with --allow-unrelated-histories' '
>   	t3033_reset &&
>   	git reset --hard four &&
> @@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' '
>   	git diff --exit-code five
>   '
>   
> +test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' '
> +	t3033_reset &&
> +	git reset --hard four &&
> +	echo change >>one.t &&
> +	git diff one.t >expect &&
> +	git merge --allow-unrelated-histories --autostash five 2>err &&
> +	test_i18ngrep "Applied autostash." err &&
> +	git diff one.t >actual &&
> +	test_cmp expect actual
> +'
> +
>   test_done
> diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
> index 4fa0ef8e3b..d809310df1 100755
> --- a/t/t7600-merge.sh
> +++ b/t/t7600-merge.sh
> @@ -30,13 +30,17 @@ Testing basic merge operations/option parsing.
>   . "$TEST_DIRECTORY"/lib-gpg.sh
>   
>   test_write_lines 1 2 3 4 5 6 7 8 9 >file
> +cp file file.orig
>   test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1
> +test_write_lines 1 2 '3 X' 4 5 6 7 8 9 >file.3
>   test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5
>   test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9
>   test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y
>   test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1
>   test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5
> +test_write_lines '1 X' 2 3 4 5 6 7 8 '9 X' >result.1-9
>   test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9
> +test_write_lines '1 X' 2 '3 X' 4 '5 X' 6 7 8 '9 X' >result.1-3-5-9
>   test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z
>   
>   create_merge_msgs () {
> @@ -675,6 +679,120 @@ test_expect_success 'refresh the index before merging' '
>   	git merge c3
>   '
>   
> +test_expect_success 'merge with --autostash' '
> +	git reset --hard c1 &&
> +	git merge-file file file.orig file.9 &&
> +	git merge --autostash c2 2>err &&
> +	test_i18ngrep "Applied autostash." err &&
> +	git show HEAD:file >merge-result &&
> +	test_cmp result.1-5 merge-result &&
> +	test_cmp result.1-5-9 file
> +'
> +
> +test_expect_success 'merge with merge.autoStash' '
> +	test_config merge.autoStash true &&
> +	git reset --hard c1 &&
> +	git merge-file file file.orig file.9 &&
> +	git merge c2 2>err &&
> +	test_i18ngrep "Applied autostash." err &&
> +	git show HEAD:file >merge-result &&
> +	test_cmp result.1-5 merge-result &&
> +	test_cmp result.1-5-9 file
> +'
> +
> +test_expect_success 'fast-forward merge with --autostash' '
> +	git reset --hard c0 &&
> +	git merge-file file file.orig file.5 &&
> +	git merge --autostash c1 2>err &&
> +	test_i18ngrep "Applied autostash." err &&
> +	test_cmp result.1-5 file
> +'
> +
> +test_expect_success 'octopus merge with --autostash' '
> +	git reset --hard c1 &&
> +	git merge-file file file.orig file.3 &&
> +	git merge --autostash c2 c3 2>err &&
> +	test_i18ngrep "Applied autostash." err &&
> +	git show HEAD:file >merge-result &&
> +	test_cmp result.1-5-9 merge-result &&
> +	test_cmp result.1-3-5-9 file
> +'
> +
> +test_expect_success 'conflicted merge with --autostash, --abort restores stash' '
> +	git reset --hard c3 &&
> +	cp file.1 file &&
> +	test_must_fail git merge --autostash c7 &&
> +	git merge --abort 2>err &&
> +	test_i18ngrep "Applied autostash." err &&
> +	test_cmp file.1 file
> +'
> +
> +test_expect_success 'completed merge (git commit) with --no-commit and --autostash' '
> +	git reset --hard c1 &&
> +	git merge-file file file.orig file.9 &&
> +	git diff >expect &&
> +	git merge --no-commit --autostash c2 &&
> +	git stash show -p MERGE_AUTOSTASH >actual &&
> +	test_cmp expect actual &&
> +	git commit 2>err &&
> +	test_i18ngrep "Applied autostash." err &&
> +	git show HEAD:file >merge-result &&
> +	test_cmp result.1-5 merge-result &&
> +	test_cmp result.1-5-9 file
> +'
> +
> +test_expect_success 'completed merge (git merge --continue) with --no-commit and --autostash' '
> +	git reset --hard c1 &&
> +	git merge-file file file.orig file.9 &&
> +	git diff >expect &&
> +	git merge --no-commit --autostash c2 &&
> +	git stash show -p MERGE_AUTOSTASH >actual &&
> +	test_cmp expect actual &&
> +	git merge --continue 2>err &&
> +	test_i18ngrep "Applied autostash." err &&
> +	git show HEAD:file >merge-result &&
> +	test_cmp result.1-5 merge-result &&
> +	test_cmp result.1-5-9 file
> +'
> +
> +test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '
> +	git reset --hard c1 &&
> +	git merge-file file file.orig file.9 &&
> +	git diff >expect &&
> +	git merge --no-commit --autostash c2 &&
> +	git stash show -p MERGE_AUTOSTASH >actual &&
> +	test_cmp expect actual &&
> +	git merge --abort 2>err &&
> +	test_i18ngrep "Applied autostash." err &&
> +	git diff >actual &&
> +	test_cmp expect actual
> +'
> +
> +test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '
> +	git reset --hard c1 &&
> +	git merge-file file file.orig file.9 &&
> +	git diff >expect &&
> +	git merge --no-commit --autostash c2 &&
> +	git stash show -p MERGE_AUTOSTASH >actual &&
> +	test_cmp expect actual &&
> +	git reset --hard 2>err &&
> +	test_i18ngrep "Autostash exists; creating a new stash entry." err &&
> +	git diff --exit-code
> +'
> +
> +test_expect_success 'merge with conflicted --autostash changes' '
> +	git reset --hard c1 &&
> +	git merge-file file file.orig file.9y &&
> +	git diff >expect &&
> +	test_when_finished "test_might_fail git stash drop" &&
> +	git merge --autostash c3 2>err &&
> +	test_i18ngrep "Applying autostash resulted in conflicts." err &&
> +	git show HEAD:file >merge-result &&
> +	test_cmp result.1-9 merge-result &&
> +	git stash show -p >actual &&
> +	test_cmp expect actual
> +'
> +
>   cat >expected.branch <<\EOF
>   Merge branch 'c5-branch' (early part)
>   EOF
> 

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 21/23] merge: teach --autostash option
  2020-04-06 15:20         ` Phillip Wood
@ 2020-04-07 13:09           ` Denton Liu
  2020-04-07 15:06             ` Phillip Wood
  0 siblings, 1 reply; 152+ messages in thread
From: Denton Liu @ 2020-04-07 13:09 UTC (permalink / raw)
  To: phillip.wood
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Phillip,

Thanks for the review. The documentation rewordings are particularly
helpful.

On Mon, Apr 06, 2020 at 04:20:24PM +0100, Phillip Wood wrote:
> >   --quit::
> >   	Forget about the current merge in progress. Leave the index
> 
> Should we be saving the stash if the user runs 'git merge --quit'?

Yes, this is its current behaviour, although it's undocumented. In my
next reroll, I've added a test case and documentation for this. This is
similar to how `git rebase --quit` behaves.

Now that I think about it, when `git rebase --quit` happens, the
autostash is just dropped out of existence, which is probably
undesirable behaviour. I'll probably submit a later series to save that
autostash so that it's not lost forever.

Thanks,

Denton

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH v4 04/23] sequencer: reuse strbuf_trim() in read_oneliner()
  2020-04-06 14:42           ` Phillip Wood
@ 2020-04-07 13:56             ` Denton Liu
  0 siblings, 0 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-07 13:56 UTC (permalink / raw)
  To: phillip.wood
  Cc: Git Mailing List, Alban Gruin, Johannes Schindelin, Junio C Hamano

Hi Phillip,

On Mon, Apr 06, 2020 at 03:42:59PM +0100, Phillip Wood wrote:
> Hi Denton
> 
> On 06/04/2020 15:03, Phillip Wood wrote:
> > Hi Denton
> > 
> > On 04/04/2020 02:11, Denton Liu wrote:
> > > In the original read_oneliner() logic, we duplicated the logic for
> > > strbuf_trim_trailing_newline() with one exception: instead of preventing
> > > buffer accesses below index 0, it would prevent buffer accesses below
> > > index `orig_len`. Although this is correct, it isn't worth having the
> > > duplicated logic around.
> > > 
> > > Reset `buf` before using it then replace the trimming logic with
> > > strbuf_trim().
> > 
> > I should have picked up on this before but this changes the semantics of
> > the function as it strips all whitespace from the start and end of the
> > strbuf. Above you talked about using strbuf_trim_trailing_newline()
> > instead which would not change the semantics of this function. You could
> > test to see if we've read anything and only call
> > strbuf_trim_trailing_newline() in that case without messing with
> > strbuf_reset(). (there is a corner case where if the buffer ends with
> > '\r' when the function is called and it reads a single '\n' then the
> > '\r' would be stripped as well but I think that is unlikely to happen in
> > the wild)
> 
> I'd also be fine with dropping this patch and leaving the trimming code as
> is

Yeah, I'll just drop this patch. I don't think it's worth holding up the
rest of the series on this small detail. I'll probably try to resurrect
this at a later time.

Thanks,

Denton

> Best Wishes
> 
> Phillip

^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH v5 00/22] merge: learn --autostash
  2020-04-04  1:11     ` [PATCH v4 00/23] merge: learn --autostash Denton Liu
                         ` (22 preceding siblings ...)
  2020-04-04  1:11       ` [PATCH v4 23/23] pull: pass --autostash to merge Denton Liu
@ 2020-04-07 14:27       ` Denton Liu
  2020-04-07 14:27         ` [PATCH v5 01/22] Makefile: ASCII-sort += lists Denton Liu
                           ` (22 more replies)
  23 siblings, 23 replies; 152+ messages in thread
From: Denton Liu @ 2020-04-07 14:27 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Alban Gruin, Johannes Schindelin, Junio C Hamano, Phillip Wood

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

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

Chagnes since v4:

* Drop 'sequencer: reuse strbuf_trim() in read_oneliner()'

* Fix commit message in 'sequencer: make apply_autostash() accept a path'

* Add more detail in commit message 'sequencer: implement save_autostash()'

* In 'merge: teach --autostash option', improve documentation, remove
  builtin/reset.c changes, add a `git merge --quit` test case

Changes since v3:

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

* Rename flag to READ_ONELINER_WARN_MISSING

* Add 'sequencer: rename stash_sha1 to stash_oid'

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

Changes since v2:

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

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

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

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

Changes since v1:

* Completely changed how the refactoring was done

* More tests and documentation

[1]: https://github.com/gitgitgadget/git/issues/394
[2]: https://lore.kernel.org/git/xmqqwoadfq8n.fsf@gitster-ct.c.googlers.com/
[3]: https://lore.kernel.org/git/xmqqwoaikehz.fsf@gitster-ct.c.googlers.com/
[4]: https://lore.kernel.org/git/20200123042906.GA29009@generichostname/

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

 Documentation/config/merge.txt  |  10 ++
 Documentation/git-merge.txt     |  11 +-
 Documentation/git-pull.txt      |   9 -
 Documentation/merge-options.txt |   8 +
 Makefile                        |  78 ++++----
 branch.c                        |   1 +
 builtin/commit.c                |   2 +
 builtin/merge.c                 |  25 +++
 builtin/pull.c                  |   9 +-
 builtin/rebase.c                | 307 +++++---------------------------
 parse-options.h                 |   1 +
 path.c                          |   1 +
 path.h                          |   4 +-
 reset.c                         | 140 +++++++++++++++
 reset.h                         |  20 +++
 sequencer.c                     | 176 +++++++++++++-----
 sequencer.h                     |  20 +++
 t/t3033-merge-toplevel.sh       |  22 +++
 t/t5520-pull.sh                 |  57 ++++--
 t/t7600-merge.sh                | 154 ++++++++++++++--
 20 files changed, 665 insertions(+), 390 deletions(-)
 create mode 100644 reset.c
 create mode 100644 reset.h

Range-diff against v4:
 1:  c26771e934 =  1:  c26771e934 Makefile: ASCII-sort += lists
 2:  059e0e8e43 =  2:  059e0e8e43 t7600: use test_write_lines()
 3:  76585e5b13 =  3:  76585e5b13 sequencer: stop leaking buf
 4:  c7a3cfa200 <  -:  ---------- sequencer: reuse strbuf_trim() in read_oneliner()
 5:  58da8e9555 !  4:  dc4674d222 sequencer: make file exists check more efficient
    @@ Commit message
         check `errno` to see if the file doesn't exist.
     
      ## sequencer.c ##
    -@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,
    - static int read_oneliner(struct strbuf *buf,
    - 	const char *path, int skip_if_empty)
    +@@ sequencer.c: static int read_oneliner(struct strbuf *buf,
      {
    --
    + 	int orig_len = buf->len;
    + 
     -	if (!file_exists(path))
     -		return 0;
     -
    - 	strbuf_reset(buf);
      	if (strbuf_read_file(buf, path, 0) < 0) {
     -		warning_errno(_("could not read '%s'"), path);
     +		if (errno != ENOENT && errno != ENOTDIR)
 6:  6ead240957 !  5:  f30ad07823 sequencer: make read_oneliner() accept flags
    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
     +#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
     +
      /*
    -  * Resets a strbuf then reads a file that was presumably written by a shell
    -  * script, i.e. with an end-of-line marker that needs to be stripped.
    +  * Reads a file that was presumably written by a shell script, i.e. with an
    +  * end-of-line marker that needs to be stripped.
     @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,
       * Returns 1 if the file was read, 0 if it could not be read or does not exist.
       */
    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
     -	const char *path, int skip_if_empty)
     +	const char *path, unsigned flags)
      {
    - 	strbuf_reset(buf);
    - 	if (strbuf_read_file(buf, path, 0) < 0) {
    + 	int orig_len = buf->len;
    + 
     @@ sequencer.c: static int read_oneliner(struct strbuf *buf,
    + 		buf->buf[buf->len] = '\0';
    + 	}
      
    - 	strbuf_trim(buf);
    - 
    --	if (skip_if_empty && !buf->len)
    -+	if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !buf->len)
    +-	if (skip_if_empty && buf->len == orig_len)
    ++	if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && buf->len == orig_len)
      		return 0;
      
      	return 1;
    @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
      				strbuf_reset(&buf);
      			else {
     @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
    - 			}
    + 			strbuf_reset(&buf);
      		}
      
     -		if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
    @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
      				opts->allow_rerere_auto = RERERE_AUTOUPDATE;
      			else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
     @@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
    - 		read_strategy_opts(opts, &buf);
    + 		strbuf_reset(&buf);
      
      		if (read_oneliner(&opts->current_fixups,
     -				  rebase_path_current_fixups(), 1)) {
 7:  2e7922b259 !  6:  8c504427e3 sequencer: configurably warn on non-existent files
    @@ Commit message
         read_oneliner() will want the ability to output warnings in the event
         that the `path` doesn't exist. Introduce the
         `READ_ONELINER_WARN_MISSING` flag which, if active, would issue a
    -    warning when a file doesn't exist by skipping the `!file_exists()` check
    -    and letting `strbuf_read_file()` handle that case.
    +    warning when a file doesn't exist by always executing warning_errno()
    +    in the case where strbuf_read_file() fails.
     
      ## sequencer.c ##
     @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,
    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
     +#define READ_ONELINER_WARN_MISSING (1 << 1)
      
      /*
    -  * Resets a strbuf then reads a file that was presumably written by a shell
    +  * Reads a file that was presumably written by a shell script, i.e. with an
     @@ sequencer.c: static int read_oneliner(struct strbuf *buf,
    - {
    - 	strbuf_reset(buf);
    + 	int orig_len = buf->len;
    + 
      	if (strbuf_read_file(buf, path, 0) < 0) {
     -		if (errno != ENOENT && errno != ENOTDIR)
     +		if ((flags & READ_ONELINER_WARN_MISSING) ||
    -+				(errno != ENOENT && errno != ENOTDIR))
    ++		    (errno != ENOENT && errno != ENOTDIR))
      			warning_errno(_("could not read '%s'"), path);
      		return 0;
      	}
 8:  6c3c37994b !  7:  50e93770e7 sequencer: make read_oneliner() extern
    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
     -#define READ_ONELINER_WARN_MISSING (1 << 1)
     -
     -/*
    -- * Resets a strbuf then reads a file that was presumably written by a shell
    -- * script, i.e. with an end-of-line marker that needs to be stripped.
    +- * Reads a file that was presumably written by a shell script, i.e. with an
    +- * end-of-line marker that needs to be stripped.
     - *
     - * Note that only the last end-of-line marker is stripped, consistent with the
     - * behavior of "$(cat path)" in a shell script.
    @@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
     +int read_oneliner(struct strbuf *buf,
      	const char *path, unsigned flags)
      {
    - 	strbuf_reset(buf);
    + 	int orig_len = buf->len;
     
      ## sequencer.h ##
     @@ sequencer.h: void print_commit_summary(struct repository *repo,
    @@ sequencer.h: void print_commit_summary(struct repository *repo,
     +#define READ_ONELINER_WARN_MISSING (1 << 1)
     +
     +/*
    -+ * Resets a strbuf then reads a file that was presumably written by a shell
    -+ * script, i.e. with an end-of-line marker that needs to be stripped.
    ++ * Reads a file that was presumably written by a shell script, i.e. with an
    ++ * end-of-line marker that needs to be stripped.
     + *
     + * Note that only the last end-of-line marker is stripped, consistent with the
     + * behavior of "$(cat path)" in a shell script.
 9:  689f34a2a5 !  8:  0cc279fc14 rebase: use read_oneliner()
    @@ Commit message
     
         Since in sequencer.c, read_one() basically duplicates the functionality
         of read_oneliner(), reduce code duplication by replacing read_one() with
    -    read_oneliner(). Also, delete strbuf_reset() calls prior to
    -    read_oneliner() as read_oneliner() already resets the strbuf.
    +    read_oneliner().
     
    -    This was mostly done with the following Coccinelle script
    +    This was done with the following Coccinelle script
     
                 @@
                 expression a, b;
    @@ Commit message
                 - read_one(a, b)
                 + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)
     
    -    Long lines were broken up and strbuf_reset()s were deleted manually.
    +    and long lines were manually broken up.
     
      ## builtin/rebase.c ##
     @@ builtin/rebase.c: static const char *state_dir_path(const char *filename, struct rebase_options *o
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      	opts->head_name = starts_with(head_name.buf, "refs/") ?
      		xstrdup(head_name.buf) : NULL;
     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    - 	 * head. Fall back to reading from head to cover for the case that the
    - 	 * user upgraded git with an ongoing interactive rebase.
      	 */
    --	strbuf_reset(&buf);
    + 	strbuf_reset(&buf);
      	if (file_exists(state_dir_path("orig-head", opts))) {
     -		if (read_one(state_dir_path("orig-head", opts), &buf))
     +		if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      	if (get_oid(buf.buf, &opts->orig_head))
      		return error(_("invalid orig-head: '%s'"), buf.buf);
     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    - 	}
      
      	if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
    --		strbuf_reset(&buf);
    + 		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
     -			    &buf))
     +		if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      		if (!strcmp(buf.buf, "--rerere-autoupdate"))
      			opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
     @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
    - 	}
      
      	if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
    --		strbuf_reset(&buf);
    + 		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("gpg_sign_opt", opts),
     -			    &buf))
     +		if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
    @@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      			return -1;
      		free(opts->gpg_sign_opt);
      		opts->gpg_sign_opt = xstrdup(buf.buf);
    - 	}
    +@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      
      	if (file_exists(state_dir_path("strategy", opts))) {
    --		strbuf_reset(&buf);
    + 		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("strategy", opts), &buf))
     +		if (!read_oneliner(&buf, state_dir_path("strategy", opts),
     +				   READ_ONELINER_WARN_MISSING))
      			return -1;
      		free(opts->strategy);
      		opts->strategy = xstrdup(buf.buf);
    - 	}
    +@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
      
      	if (file_exists(state_dir_path("strategy_opts", opts))) {
    --		strbuf_reset(&buf);
    + 		strbuf_reset(&buf);
     -		if (read_one(state_dir_path("strategy_opts", opts), &buf))
     +		if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
     +				   READ_ONELINER_WARN_MISSING))
10:  e0b8e409af !  9:  3dbed2a832 sequencer: make apply_rebase() accept a path
    @@ Metadata
     Author: Denton Liu <liu.denton@gmail.com>
     
      ## Commit message ##
    -    sequencer: make apply_rebase() accept a path
    +    sequencer: make apply_autostash() accept a path
     
    -    In order to make apply_rebase() more generic for future extraction, make
    +    In order to make apply_autostash() more generic for future extraction, make
         it accept a `path` argument so that the location from where to read the
         reference to the autostash commit can be customized. Remove the `opts`
         argument since it was unused before anyway.
11:  a52583beff = 10:  937b4964d1 sequencer: rename stash_sha1 to stash_oid
12:  389b17df33 = 11:  98a7f5280c rebase: use apply_autostash() from sequencer.c
13:  fe57e21dbc = 12:  67f3b5c225 rebase: generify reset_head()
14:  045668620d = 13:  af5e808667 reset: extract reset_head() from rebase
15:  d5f51cd80e = 14:  114d9a7655 rebase: extract create_autostash()
16:  9ab10d23d4 = 15:  c2ed4cd39d rebase: generify create_autostash()
17:  26cca49be6 = 16:  3be4a27dfe sequencer: extract perform_autostash() from rebase
18:  e703022fda = 17:  129d5d97ae sequencer: unlink autostash in apply_autostash()
19:  75dc3f10a1 ! 18:  7e04ce8d8e sequencer: implement save_autostash()
    @@ Commit message
         function will be used in a future commit.
     
         The difference between save_autostash() and apply_autostash() is that
    -    the latter does not try to apply the stash. It skips that step and
    +    the former does not try to apply the stash. It skips that step and
         just stores the created entry in the stash reflog.
     
    +    This is useful in the case where we abort an operation when an autostash
    +    is present but we don't want to dirty the worktree with the application
    +    of the stash. For example, in a future commit, we will implement
    +    `git merge --autostash`. Since merges can be aborted using
    +    `git reset --hard`, we'd make use of save_autostash() to save the
    +    autostash entry instead of applying it to the worktree thus keeping the
    +    worktree undirtied.
    +
      ## sequencer.c ##
     @@ sequencer.c: void create_autostash(struct repository *r, const char *path,
      	strbuf_release(&buf);
20:  598ddea6c1 = 19:  732b3f9945 sequencer: implement apply_autostash_oid()
21:  7adf794192 ! 20:  f9f698b79a merge: teach --autostash option
    @@ Commit message
         `git reset --hard`, the autostash is saved into the stash reflog
         instead keeping the worktree clean.
     
    +    Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
         Suggested-by: Alban Gruin <alban.gruin@gmail.com>
     
      ## Documentation/config/merge.txt ##
    @@ Documentation/git-merge.txt: will be appended to the specified message.
      	Abort the current conflict resolution process, and
     -	try to reconstruct the pre-merge state.
     +	try to reconstruct the pre-merge state. If an autostash entry is
    -+	present, apply it back to the worktree.
    ++	present, apply it to the worktree.
      +
      If there were uncommitted worktree changes present when the merge
      started, 'git merge --abort' will in some cases be unable to
    @@ Documentation/git-merge.txt: reconstruct these changes. It is therefore recommen
      +
      'git merge --abort' is equivalent to 'git reset --merge' when
     -`MERGE_HEAD` is present.
    -+`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present where
    -+the stash entry is applied to the worktree.
    ++`MERGE_HEAD` is present unless `MERGE_AUTOSTASH` is also present in
    ++which case 'git merge --abort' applies the stash entry to the worktree
    ++whereas 'git reset --merge' will save the stashed changes in the stash
    ++reflog.
      
      --quit::
      	Forget about the current merge in progress. Leave the index
    +-	and the working tree as-is.
    ++	and the working tree as-is. If `MERGE_AUTOSTASH` is present, the
    ++	stash entry will be saved to the stash reflog.
    + 
    + --continue::
    + 	After a 'git merge' stops due to conflicts you can conclude the
     
      ## Documentation/merge-options.txt ##
     @@ Documentation/merge-options.txt: ifndef::git-pull[]
    @@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
      			die(_("There is no merge to abort (MERGE_HEAD missing)."));
      
     +		if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),
    -+			   READ_ONELINER_SKIP_IF_EMPTY)) {
    -+			strbuf_trim(&stash_oid);
    ++		    READ_ONELINER_SKIP_IF_EMPTY))
     +			unlink(git_path_merge_autostash(the_repository));
    -+		}
     +
      		/* Invoke 'git reset --merge' */
      		ret = cmd_reset(nargc, nargv, prefix);
    @@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
      				N_("add exec lines after each commit of the "
      				   "editable list")),
     
    - ## builtin/reset.c ##
    -@@
    - #include "cache-tree.h"
    - #include "submodule.h"
    - #include "submodule-config.h"
    -+#include "sequencer.h"
    - 
    - #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
    - 
    -@@ builtin/reset.c: int cmd_reset(int argc, const char **argv, const char *prefix)
    - 		if (reset_type == HARD && !update_ref_status && !quiet)
    - 			print_new_head_line(lookup_commit_reference(the_repository, &oid));
    - 	}
    --	if (!pathspec.nr)
    -+	if (!pathspec.nr) {
    - 		remove_branch_state(the_repository, 0);
    -+	}
    - 
    - 	return update_ref_status;
    - }
    -
      ## parse-options.h ##
     @@ parse-options.h: int parse_opt_passthru_argv(const struct option *, const char *, int);
      #define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
    @@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '
     +	git diff --exit-code
     +'
     +
    ++test_expect_success 'quit merge with --no-commit and --autostash' '
    ++	git reset --hard c1 &&
    ++	git merge-file file file.orig file.9 &&
    ++	git diff >expect &&
    ++	git merge --no-commit --autostash c2 &&
    ++	git stash show -p MERGE_AUTOSTASH >actual &&
    ++	test_cmp expect actual &&
    ++	git diff HEAD >expect &&
    ++	git merge --quit 2>err &&
    ++	test_i18ngrep "Autostash exists; creating a new stash entry." err &&
    ++	git diff HEAD >actual &&
    ++	test_cmp expect actual
    ++'
    ++
     +test_expect_success 'merge