git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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
                     ` (17 more replies)
  7 siblings, 18 replies; 52+ 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] 52+ 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
                     ` (16 subsequent siblings)
  17 siblings, 1 reply; 52+ 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] 52+ 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
                     ` (15 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
                     ` (14 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
                     ` (13 subsequent siblings)
  17 siblings, 1 reply; 52+ 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] 52+ 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
                     ` (12 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
                     ` (11 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
                     ` (10 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
                     ` (9 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
                     ` (8 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
                     ` (7 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
                     ` (6 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
                     ` (5 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
                     ` (4 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
                     ` (3 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
                     ` (2 subsequent siblings)
  17 siblings, 0 replies; 52+ 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] 52+ 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
  2019-12-30 21:49   ` [PATCH v2 00/17] merge: learn --autostash Junio C Hamano
  17 siblings, 0 replies; 52+ 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] 52+ 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
  17 siblings, 0 replies; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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
  17 siblings, 2 replies; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ 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; 52+ 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] 52+ messages in thread

end of thread, back to index

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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-17 18:07   ` Junio C Hamano
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
2019-10-21 19:54         ` Jeff King
2019-10-22 11:34           ` Junio C Hamano
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
2019-10-16 17:26 ` [RFC PATCH 3/7] autostash: extract apply_autostash() " Denton Liu
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
2019-10-16 17:26 ` [RFC PATCH 5/7] autostash: extract perform_autostash() " 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
2019-10-18  9:41   ` Phillip Wood
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
2019-12-24 10:05     ` Denton Liu
2019-12-24 10:12       ` Denton Liu
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-30 21:38     ` Junio C Hamano
2019-12-24 11:04   ` [PATCH v2 02/17] t7600: use test_write_lines() Denton Liu
2019-12-24 11:05   ` [PATCH v2 03/17] sequencer: use file strbuf for read_oneliner() Denton Liu
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
2019-12-24 11:05   ` [PATCH v2 05/17] sequencer: make read_oneliner() extern Denton Liu
2019-12-24 11:05   ` [PATCH v2 06/17] rebase: use read_oneliner() Denton Liu
2019-12-24 11:05   ` [PATCH v2 07/17] sequencer: make apply_rebase() accept a path Denton Liu
2019-12-24 11:05   ` [PATCH v2 08/17] rebase: use apply_autostash() from sequencer.c Denton Liu
2019-12-24 11:05   ` [PATCH v2 09/17] rebase: generify reset_head() Denton Liu
2019-12-24 11:05   ` [PATCH v2 10/17] reset: extract reset_head() from rebase Denton Liu
2019-12-24 11:05   ` [PATCH v2 11/17] rebase: extract create_autostash() Denton Liu
2019-12-24 11:05   ` [PATCH v2 12/17] rebase: generify create_autostash() Denton Liu
2019-12-24 11:05   ` [PATCH v2 13/17] sequencer: extract perform_autostash() from rebase Denton Liu
2019-12-24 11:05   ` [PATCH v2 14/17] sequencer: unlink autostash in apply_autostash() Denton Liu
2019-12-24 11:05   ` [PATCH v2 15/17] merge: teach --autostash option Denton Liu
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   ` [PATCH v2 17/17] pull: pass --autostash to merge Denton Liu
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-10 14:44         ` Phillip Wood
2020-01-15 16:20           ` Denton Liu
2020-01-01  7:48     ` Denton Liu

git@vger.kernel.org list mirror (unofficial, one of many)

Archives are clonable:
	git clone --mirror http://public-inbox.org/git
	git clone --mirror http://ou63pmih66umazou.onion/git
	git clone --mirror http://czquwvybam4bgbro.onion/git
	git clone --mirror http://hjrcffqmbrq6wope.onion/git

Example config snippet for mirrors

Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.version-control.git
	nntp://ou63pmih66umazou.onion/inbox.comp.version-control.git
	nntp://czquwvybam4bgbro.onion/inbox.comp.version-control.git
	nntp://hjrcffqmbrq6wope.onion/inbox.comp.version-control.git
	nntp://news.gmane.io/gmane.comp.version-control.git

 note: .onion URLs require Tor: https://www.torproject.org/

AGPL code for this site: git clone https://public-inbox.org/public-inbox.git