merge: learn --autostash
Subsystems (2)
cover merge: learn --autostash
Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.
The actual logic isn't too bad. That change can be found in the last patch. The remainder is refactoring so that the change can be made easily. One thing I would like some attention on is if apply_autostash() is called in the right place. It's called somewhere above close_object_store() and also in the --abort case, just like in rebase, but I'm not quite sure if that's right.
Since this is an RFC, we're missing tests and documentation but I've tested it and it seems to work reasonably well.
[1]: https://github.com/gitgitgadget/git/issues/394
Denton Liu (7):
Makefile: alphabetically sort += lists
autostash: extract read_one() from rebase
autostash: extract apply_autostash() from rebase
autostash: extract reset_head() from rebase
autostash: extract perform_autostash() from rebase autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS
merge: teach --autostash option
Makefile | 75 +++++++-------- autostash.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++ autostash.h | 24 +++++ builtin/merge.c | 13 +++ builtin/pull.c | 9 +- builtin/rebase.c | 240 +---------------------------------------------- t/t5520-pull.sh | 8 -- 7 files changed, 323 insertions(+), 285 deletions(-) create mode 100644 autostash.c create mode 100644 autostash.h
Signature · 1 line
2.23.0.897.g0a19638b1e
RFC PATCH 1/7 Makefile: alphabetically sort += lists
There are many += lists in the Makefile and, over time, they have gotten slightly out of order, alphabetically. Alphabetically sort all += lists to bring them back in order.
--- Makefile | 75 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 37 deletions(-)
Makefile +38 -37
@@ -604,12 +604,12 @@ unexport CDPATH SCRIPT_SH += git-bisect.sh SCRIPT_SH += git-difftool--helper.sh SCRIPT_SH += git-filter-branch.sh +SCRIPT_SH += git-legacy-stash.sh SCRIPT_SH += git-merge-octopus.sh SCRIPT_SH += git-merge-one-file.sh SCRIPT_SH += git-merge-resolve.sh SCRIPT_SH += git-mergetool.sh SCRIPT_SH += git-quiltimport.sh -SCRIPT_SH += git-legacy-stash.sh SCRIPT_SH += git-request-pull.sh SCRIPT_SH += git-submodule.sh SCRIPT_SH += git-web--browse.sh
@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh SCRIPT_LIB += git-mergetool--lib SCRIPT_LIB += git-parse-remote SCRIPT_LIB += git-rebase--preserve-merges -SCRIPT_LIB += git-sh-setup SCRIPT_LIB += git-sh-i18n +SCRIPT_LIB += git-sh-setup SCRIPT_PERL += git-add--interactive.perl SCRIPT_PERL += git-archimport.perl
@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o PROGRAM_OBJS += fast-import.o PROGRAM_OBJS += http-backend.o PROGRAM_OBJS += imap-send.o +PROGRAM_OBJS += remote-testsvn.o PROGRAM_OBJS += sh-i18n--envsubst.o PROGRAM_OBJS += shell.o -PROGRAM_OBJS += remote-testsvn.o # Binary suffix, set to .exe for Windows builds X =
@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o TEST_BUILTINS_OBJS += test-example-decorate.o TEST_BUILTINS_OBJS += test-genrandom.o TEST_BUILTINS_OBJS += test-genzeros.o +TEST_BUILTINS_OBJS += test-hash-speed.o TEST_BUILTINS_OBJS += test-hash.o TEST_BUILTINS_OBJS += test-hashmap.o -TEST_BUILTINS_OBJS += test-hash-speed.o TEST_BUILTINS_OBJS += test-index-version.o TEST_BUILTINS_OBJS += test-json-writer.o TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@ -735,8 +735,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o TEST_BUILTINS_OBJS += test-run-command.o TEST_BUILTINS_OBJS += test-scrap-cache-tree.o TEST_BUILTINS_OBJS += test-serve-v2.o -TEST_BUILTINS_OBJS += test-sha1.o TEST_BUILTINS_OBJS += test-sha1-array.o +TEST_BUILTINS_OBJS += test-sha1.o TEST_BUILTINS_OBJS += test-sha256.o TEST_BUILTINS_OBJS += test-sigchain.o TEST_BUILTINS_OBJS += test-strcmp-offset.o
@@ -746,10 +746,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o TEST_BUILTINS_OBJS += test-subprocess.o TEST_BUILTINS_OBJS += test-trace2.o TEST_BUILTINS_OBJS += test-urlmatch-normalization.o -TEST_BUILTINS_OBJS += test-xml-encode.o TEST_BUILTINS_OBJS += test-wildmatch.o TEST_BUILTINS_OBJS += test-windows-named-pipe.o TEST_BUILTINS_OBJS += test-write-cache.o +TEST_BUILTINS_OBJS += test-xml-encode.o # Do not add more tests here unless they have extra dependencies. Add # them in TEST_BUILTINS_OBJS above.
@@ -786,10 +786,10 @@ OTHER_PROGRAMS = git$X # what test wrappers are needed and 'install' will install, in bindir BINDIR_PROGRAMS_NEED_X += git -BINDIR_PROGRAMS_NEED_X += git-upload-pack BINDIR_PROGRAMS_NEED_X += git-receive-pack -BINDIR_PROGRAMS_NEED_X += git-upload-archive BINDIR_PROGRAMS_NEED_X += git-shell +BINDIR_PROGRAMS_NEED_X += git-upload-archive +BINDIR_PROGRAMS_NEED_X += git-upload-pack BINDIR_PROGRAMS_NO_X += git-cvsserver
@@ -827,11 +827,12 @@ LIB_OBJS += advice.o LIB_OBJS += alias.o LIB_OBJS += alloc.o LIB_OBJS += apply.o -LIB_OBJS += archive.o LIB_OBJS += archive-tar.o LIB_OBJS += archive-zip.o +LIB_OBJS += archive.o LIB_OBJS += argv-array.o LIB_OBJS += attr.o +LIB_OBJS += autostash.o LIB_OBJS += base85.o LIB_OBJS += bisect.o LIB_OBJS += blame.o
@@ -845,9 +846,9 @@ LIB_OBJS += checkout.o LIB_OBJS += color.o LIB_OBJS += column.o LIB_OBJS += combine-diff.o -LIB_OBJS += commit.o LIB_OBJS += commit-graph.o LIB_OBJS += commit-reach.o +LIB_OBJS += commit.o LIB_OBJS += compat/obstack.o LIB_OBJS += compat/terminal.o LIB_OBJS += config.o
@@ -861,17 +862,17 @@ LIB_OBJS += ctype.o LIB_OBJS += date.o LIB_OBJS += decorate.o LIB_OBJS += delta-islands.o +LIB_OBJS += diff-delta.o +LIB_OBJS += diff-lib.o +LIB_OBJS += diff-no-index.o +LIB_OBJS += diff.o LIB_OBJS += diffcore-break.o LIB_OBJS += diffcore-delta.o LIB_OBJS += diffcore-order.o LIB_OBJS += diffcore-pickaxe.o LIB_OBJS += diffcore-rename.o -LIB_OBJS += diff-delta.o -LIB_OBJS += diff-lib.o -LIB_OBJS += diff-no-index.o -LIB_OBJS += diff.o -LIB_OBJS += dir.o LIB_OBJS += dir-iterator.o +LIB_OBJS += dir.o LIB_OBJS += editor.o LIB_OBJS += entry.o LIB_OBJS += environment.o
@@ -889,7 +890,6 @@ LIB_OBJS += gpg-interface.o LIB_OBJS += graph.o LIB_OBJS += grep.o LIB_OBJS += hashmap.o -LIB_OBJS += linear-assignment.o LIB_OBJS += help.o LIB_OBJS += hex.o LIB_OBJS += ident.o
@@ -899,9 +899,10 @@ LIB_OBJS += kwset.o LIB_OBJS += levenshtein.o LIB_OBJS += line-log.o LIB_OBJS += line-range.o -LIB_OBJS += list-objects.o -LIB_OBJS += list-objects-filter.o +LIB_OBJS += linear-assignment.o LIB_OBJS += list-objects-filter-options.o +LIB_OBJS += list-objects-filter.o +LIB_OBJS += list-objects.o LIB_OBJS += ll-merge.o LIB_OBJS += lockfile.o LIB_OBJS += log-tree.o
@@ -910,31 +911,31 @@ LIB_OBJS += mailinfo.o LIB_OBJS += mailmap.o LIB_OBJS += match-trees.o LIB_OBJS += mem-pool.o -LIB_OBJS += merge.o LIB_OBJS += merge-blobs.o LIB_OBJS += merge-recursive.o +LIB_OBJS += merge.o LIB_OBJS += mergesort.o LIB_OBJS += midx.o LIB_OBJS += name-hash.o LIB_OBJS += negotiator/default.o LIB_OBJS += negotiator/skipping.o -LIB_OBJS += notes.o LIB_OBJS += notes-cache.o LIB_OBJS += notes-merge.o LIB_OBJS += notes-utils.o +LIB_OBJS += notes.o LIB_OBJS += object.o LIB_OBJS += oidmap.o LIB_OBJS += oidset.o -LIB_OBJS += packfile.o -LIB_OBJS += pack-bitmap.o LIB_OBJS += pack-bitmap-write.o +LIB_OBJS += pack-bitmap.o LIB_OBJS += pack-check.o LIB_OBJS += pack-objects.o LIB_OBJS += pack-revindex.o LIB_OBJS += pack-write.o +LIB_OBJS += packfile.o LIB_OBJS += pager.o -LIB_OBJS += parse-options.o LIB_OBJS += parse-options-cb.o +LIB_OBJS += parse-options.o LIB_OBJS += patch-delta.o LIB_OBJS += patch-ids.o LIB_OBJS += path.o
@@ -952,6 +953,7 @@ LIB_OBJS += range-diff.o LIB_OBJS += reachable.o LIB_OBJS += read-cache.o LIB_OBJS += rebase-interactive.o +LIB_OBJS += ref-filter.o LIB_OBJS += reflog-walk.o LIB_OBJS += refs.o LIB_OBJS += refs/files-backend.o
@@ -959,7 +961,6 @@ LIB_OBJS += refs/iterator.o LIB_OBJS += refs/packed-backend.o LIB_OBJS += refs/ref-cache.o LIB_OBJS += refspec.o -LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace-object.o LIB_OBJS += repo-settings.o
@@ -974,8 +975,8 @@ LIB_OBJS += serve.o LIB_OBJS += server-info.o LIB_OBJS += setup.o LIB_OBJS += sha1-array.o -LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1-file.o +LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1-name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o
@@ -985,9 +986,9 @@ LIB_OBJS += stable-qsort.o LIB_OBJS += strbuf.o LIB_OBJS += streaming.o LIB_OBJS += string-list.o -LIB_OBJS += submodule.o -LIB_OBJS += submodule-config.o LIB_OBJS += sub-process.o +LIB_OBJS += submodule-config.o +LIB_OBJS += submodule.o LIB_OBJS += symlinks.o LIB_OBJS += tag.o LIB_OBJS += tempfile.o
@@ -1006,11 +1007,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o LIB_OBJS += trace2/tr2_tgt_perf.o LIB_OBJS += trace2/tr2_tls.o LIB_OBJS += trailer.o -LIB_OBJS += transport.o LIB_OBJS += transport-helper.o +LIB_OBJS += transport.o LIB_OBJS += tree-diff.o -LIB_OBJS += tree.o LIB_OBJS += tree-walk.o +LIB_OBJS += tree.o LIB_OBJS += unpack-trees.o LIB_OBJS += upload-pack.o LIB_OBJS += url.o
@@ -1050,9 +1051,9 @@ BUILTIN_OBJS += builtin/checkout.o BUILTIN_OBJS += builtin/clean.o BUILTIN_OBJS += builtin/clone.o BUILTIN_OBJS += builtin/column.o +BUILTIN_OBJS += builtin/commit-graph.o BUILTIN_OBJS += builtin/commit-tree.o BUILTIN_OBJS += builtin/commit.o -BUILTIN_OBJS += builtin/commit-graph.o BUILTIN_OBJS += builtin/config.o BUILTIN_OBJS += builtin/count-objects.o BUILTIN_OBJS += builtin/credential.o
@@ -1083,13 +1084,13 @@ BUILTIN_OBJS += builtin/ls-remote.o BUILTIN_OBJS += builtin/ls-tree.o BUILTIN_OBJS += builtin/mailinfo.o BUILTIN_OBJS += builtin/mailsplit.o -BUILTIN_OBJS += builtin/merge.o BUILTIN_OBJS += builtin/merge-base.o BUILTIN_OBJS += builtin/merge-file.o BUILTIN_OBJS += builtin/merge-index.o BUILTIN_OBJS += builtin/merge-ours.o BUILTIN_OBJS += builtin/merge-recursive.o BUILTIN_OBJS += builtin/merge-tree.o +BUILTIN_OBJS += builtin/merge.o BUILTIN_OBJS += builtin/mktag.o BUILTIN_OBJS += builtin/mktree.o BUILTIN_OBJS += builtin/multi-pack-index.o
@@ -1109,9 +1110,9 @@ BUILTIN_OBJS += builtin/read-tree.o BUILTIN_OBJS += builtin/rebase.o BUILTIN_OBJS += builtin/receive-pack.o BUILTIN_OBJS += builtin/reflog.o -BUILTIN_OBJS += builtin/remote.o BUILTIN_OBJS += builtin/remote-ext.o BUILTIN_OBJS += builtin/remote-fd.o +BUILTIN_OBJS += builtin/remote.o BUILTIN_OBJS += builtin/repack.o BUILTIN_OBJS += builtin/replace.o BUILTIN_OBJS += builtin/rerere.o
@@ -2325,16 +2326,16 @@ reconfigure config.mak.autogen: config.status endif XDIFF_OBJS += xdiff/xdiffi.o -XDIFF_OBJS += xdiff/xprepare.o -XDIFF_OBJS += xdiff/xutils.o XDIFF_OBJS += xdiff/xemit.o +XDIFF_OBJS += xdiff/xhistogram.o XDIFF_OBJS += xdiff/xmerge.o XDIFF_OBJS += xdiff/xpatience.o -XDIFF_OBJS += xdiff/xhistogram.o +XDIFF_OBJS += xdiff/xprepare.o +XDIFF_OBJS += xdiff/xutils.o +VCSSVN_OBJS += vcs-svn/fast_export.o VCSSVN_OBJS += vcs-svn/line_buffer.o VCSSVN_OBJS += vcs-svn/sliding_window.o -VCSSVN_OBJS += vcs-svn/fast_export.o VCSSVN_OBJS += vcs-svn/svndiff.o VCSSVN_OBJS += vcs-svn/svndump.o
@@ -3143,9 +3144,9 @@ endif # ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) ALL_COMMANDS += git +ALL_COMMANDS += git-gui git-citool ALL_COMMANDS += gitk ALL_COMMANDS += gitweb -ALL_COMMANDS += git-gui git-citool .PHONY: check-docs check-docs::
Signature · 1 line
2.23.0.897.g0a19638b1e
RFC PATCH 2/7 autostash: extract read_one() from rebase
In reply to Denton LiuBegin the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.
This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.
--- autostash.c | 12 ++++++++++++ autostash.h | 9 +++++++++ builtin/rebase.c | 10 +--------- 3 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 autostash.c create mode 100644 autostash.h
autostash.c new file +12 -0
@@ -0,0 +1,12 @@ +#include "git-compat-util.h" +#include "autostash.h" +#include "gettext.h" +#include "strbuf.h" + +int read_one (const char *path,struct strbuf *buf) +{ +if (strbuf_read_file (buf, path,0 ) <0 ) +return error_errno (_ ("could not read '%s'" ), path); +strbuf_trim_trailing_newline (buf); +return 0 ; +}
autostash.h new file +9 -0
@@ -0,0 +1,9 @@ +#ifndef AUTOSTASH_H +#define AUTOSTASH_H + +#include "strbuf.h" + +/* Read one file, then strip line endings */ +int read_one (const char *path,struct strbuf *buf); + +#endif
builtin/rebase.c +1 -9
@@ -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 *oreturn path.buf; } -/* Read one file, then strip line endings */ -static int read_one (const char *path,struct strbuf *buf) -{ -if (strbuf_read_file (buf, path,0 ) <0 ) -return error_errno (_ ("could not read '%s'" ), path); -strbuf_trim_trailing_newline (buf); -return 0 ; -} -/* Initialize the rebase options from the state directory. */ static int read_basic_state (struct rebase_options *opts) {
Signature · 1 line
2.23.0.897.g0a19638b1e
RFC PATCH 3/7 autostash: extract apply_autostash() from rebase
In reply to Denton LiuContinue the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.
This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.
--- autostash.c | 46 +++++++++++++++++++++++++++++++++++++++++++++ autostash.h | 2 ++ builtin/rebase.c | 49 ++---------------------------------------------- 3 files changed, 50 insertions(+), 47 deletions(-)
autostash.c +46 -0
@@ -1,6 +1,8 @@#include "git-compat-util.h" #include "autostash.h" +#include "dir.h" #include "gettext.h" +#include "run-command.h" #include "strbuf.h" int read_one (const char *path,struct strbuf *buf)
@@ -10,3 +12,47 @@ int read_one(const char *path, struct strbuf *buf)strbuf_trim_trailing_newline (buf);return 0 ; } + +int apply_autostash (const char *path) +{ +struct strbuf autostash =STRBUF_INIT ; +struct child_process stash_apply =CHILD_PROCESS_INIT ; + +if (!file_exists (path)) +return 0 ; + +if (read_one (path, &autostash)) +return error (_ ("Could not read '%s'" ), path); +/* Ensure that the hash is not mistaken for a number */ +strbuf_addstr (&autostash,"^0" ); +argv_array_pushl (&stash_apply.args, +"stash" ,"apply" , autostash.buf,NULL ); + stash_apply.git_cmd =1 ; + stash_apply.no_stderr = stash_apply.no_stdout = + stash_apply.no_stdin =1 ; +if (!run_command (&stash_apply)) +printf (_ ("Applied autostash.\n" )); +else { +struct argv_array args =ARGV_ARRAY_INIT ; +int res =0 ; + +argv_array_pushl (&args, +"stash" ,"store" ,"-m" ,"autostash" ,"-q" , + autostash.buf,NULL ); +if (run_command_v_opt (args.argv,RUN_GIT_CMD )) + res =error (_ ("Cannot store %s" ), autostash.buf); +argv_array_clear (&args); +strbuf_release (&autostash); +if (res) +return res; + +fprintf (stderr, +_ ("Applying autostash resulted in conflicts.\n" +"Your changes are safe in the stash.\n" +"You can run \"git stash pop\" or \"git stash drop\" " +"at any time.\n" )); + } + +strbuf_release (&autostash); +return 0 ; +}
autostash.h +2 -0
@@ -6,4 +6,6 @@/* Read one file, then strip line endings */ int read_one (const char *path,struct strbuf *buf); +int apply_autostash (const char *path); +#endif
builtin/rebase.c +2 -47
67 lines not on this page · raw message
Signature · 1 line
2.23.0.897.g0a19638b1e
RFC PATCH 4/7 autostash: extract reset_head() from rebase
In reply to Denton LiuBegin the process of lib-ifying the autostash code. In a future commit,
This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.
this will be used to implement --autostash in other builtins.
--- autostash.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++ autostash.h | 12 +++++ builtin/rebase.c | 137 ----------------------------------------------- 3 files changed, 149 insertions(+), 137 deletions(-)
autostash.c +137 -0
@@ -1,9 +1,17 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS +#include "git-compat-util.h" #include "autostash.h" +#include "cache-tree.h" #include "dir.h" #include "gettext.h" +#include "lockfile.h" +#include "refs.h" #include "run-command.h" #include "strbuf.h" +#include "tree-walk.h" +#include "tree.h" +#include "unpack-trees.h" int read_one (const char *path,struct strbuf *buf) {
@@ -13,6 +21,135 @@ int read_one(const char *path, struct strbuf *buf)return 0 ; } +int reset_head (struct object_id *oid,const char *action, +const char *switch_to_branch,unsigned flags, +const char *reflog_orig_head,const char *reflog_head) +{ +unsigned detach_head = flags &RESET_HEAD_DETACH ; +unsigned reset_hard = flags &RESET_HEAD_HARD ; +unsigned run_hook = flags &RESET_HEAD_RUN_POST_CHECKOUT_HOOK ; +unsigned refs_only = flags &RESET_HEAD_REFS_ONLY ; +unsigned update_orig_head = flags &RESET_ORIG_HEAD ; +struct object_id head_oid; +struct tree_desc desc[2 ] = { {NULL }, {NULL } }; +struct lock_file lock =LOCK_INIT ; +struct unpack_trees_options unpack_tree_opts; +struct tree *tree; +const char *reflog_action; +struct strbuf msg =STRBUF_INIT ; +size_t prefix_len; +struct object_id *orig =NULL , oid_orig, + *old_orig =NULL , oid_old_orig; +int ret =0 , nr =0 ; + +if (switch_to_branch && !starts_with (switch_to_branch,"refs/" )) +BUG ("Not a fully qualified branch: '%s'" , switch_to_branch); + +if (!refs_only &&hold_locked_index (&lock,LOCK_REPORT_ON_ERROR ) <0 ) { + ret =-1 ; + goto leave_reset_head; + } + +if ((!oid || !reset_hard) &&get_oid ("HEAD" , &head_oid)) { + ret =error (_ ("could not determine HEAD revision" )); + goto leave_reset_head; + } + +if (!oid) + oid = &head_oid; + +if (refs_only) + goto reset_head_refs; + +memset (&unpack_tree_opts,0 ,sizeof (unpack_tree_opts)); +setup_unpack_trees_porcelain (&unpack_tree_opts, action); + unpack_tree_opts.head_idx =1 ; + unpack_tree_opts.src_index = the_repository->index; + unpack_tree_opts.dst_index = the_repository->index; + unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; + unpack_tree_opts.update =1 ; + unpack_tree_opts.merge =1 ; +if (!detach_head) + unpack_tree_opts.reset =1 ; + +if (repo_read_index_unmerged (the_repository) <0 ) { + ret =error (_ ("could not read index" )); + goto leave_reset_head; + } + +if (!reset_hard && !fill_tree_descriptor (the_repository, &desc[nr++], &head_oid)) { + ret =error (_ ("failed to find tree of %s" ), +oid_to_hex (&head_oid)); + goto leave_reset_head; + } + +if (!fill_tree_descriptor (the_repository, &desc[nr++], oid)) { + ret =error (_ ("failed to find tree of %s" ),oid_to_hex (oid)); + goto leave_reset_head; + } + +if (unpack_trees (nr, desc, &unpack_tree_opts)) { + ret =-1 ; + goto leave_reset_head; + } + + tree =parse_tree_indirect (oid); +prime_cache_tree (the_repository, the_repository->index, tree); + +if (write_locked_index (the_repository->index, &lock,COMMIT_LOCK ) <0 ) { + ret =error (_ ("could not write index" )); + goto leave_reset_head; + } + +reset_head_refs: + reflog_action =getenv (GIT_REFLOG_ACTION_ENVIRONMENT ); +strbuf_addf (&msg,"%s: " , reflog_action ? reflog_action :"rebase" ); + prefix_len = msg.len; + +if (update_orig_head) { +if (!get_oid ("ORIG_HEAD" , &oid_old_orig)) + old_orig = &oid_old_orig; +if (!get_oid ("HEAD" , &oid_orig)) { + orig = &oid_orig; +if (!reflog_orig_head) { +strbuf_addstr (&msg,"updating ORIG_HEAD" ); + reflog_orig_head = msg.buf; + } +update_ref (reflog_orig_head,"ORIG_HEAD" , orig, + old_orig,0 ,UPDATE_REFS_MSG_ON_ERR ); + }else if (old_orig) +delete_ref (NULL ,"ORIG_HEAD" , old_orig,0 ); + } + +if (!reflog_head) { +strbuf_setlen (&msg, prefix_len); +strbuf_addstr (&msg,"updating HEAD" ); + reflog_head = msg.buf; + } +if (!switch_to_branch) + ret =update_ref (reflog_head,"HEAD" , oid, orig, + detach_head ?REF_NO_DEREF :0 , +UPDATE_REFS_MSG_ON_ERR ); +else { + ret =update_ref (reflog_head, switch_to_branch, oid, +NULL ,0 ,UPDATE_REFS_MSG_ON_ERR ); +if (!ret) + ret =create_symref ("HEAD" , switch_to_branch, + reflog_head); + } +if (run_hook) +run_hook_le (NULL ,"post-checkout" , +oid_to_hex (orig ? orig : &null_oid), +oid_to_hex (oid),"1" ,NULL ); + +leave_reset_head: +strbuf_release (&msg); +rollback_lock_file (&lock); +while (nr) +free ((void *)desc[--nr].buffer); +return ret; +} +int apply_autostash (const char *path) {struct strbuf autostash =STRBUF_INIT ;
autostash.h +12 -0
@@ -6,6 +6,18 @@/* Read one file, then strip line endings */ int read_one (const char *path,struct strbuf *buf); +#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" + +#define RESET_HEAD_DETACH (1<<0) +#define RESET_HEAD_HARD (1<<1) +#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) +#define RESET_HEAD_REFS_ONLY (1<<3) +#define RESET_ORIG_HEAD (1<<4) + +int reset_head (struct object_id *oid,const char *action, +const char *switch_to_branch,unsigned flags, +const char *reflog_orig_head,const char *reflog_head); +int apply_autostash (const char *path);#endif
builtin/rebase.c +0 -137
143 lines not on this page · raw message
Signature · 1 line
2.23.0.897.g0a19638b1e
RFC PATCH 5/7 autostash: extract perform_autostash() from rebase
In reply to Denton LiuContinue the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.
This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.
--- autostash.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ autostash.h | 1 + builtin/rebase.c | 44 +------------------------------------------- 3 files changed, 48 insertions(+), 43 deletions(-)
autostash.c +46 -0
@@ -12,6 +12,7 @@#include "tree-walk.h" #include "tree.h" #include "unpack-trees.h" +#include "wt-status.h" int read_one (const char *path,struct strbuf *buf) {
@@ -150,6 +151,51 @@ int reset_head(struct object_id *oid, const char *action,return ret; } +void perform_autostash (const char *path) +{ +struct strbuf buf =STRBUF_INIT ; +struct lock_file lock_file =LOCK_INIT ; +int fd; + + fd =hold_locked_index (&lock_file,0 ); +refresh_cache (REFRESH_QUIET ); +if (0 <= fd) +repo_update_index_if_able (the_repository, &lock_file); +rollback_lock_file (&lock_file); + +if (has_unstaged_changes (the_repository,1 ) || +has_uncommitted_changes (the_repository,1 )) { +struct child_process stash =CHILD_PROCESS_INIT ; +struct object_id oid; + +argv_array_pushl (&stash.args, +"stash" ,"create" ,"autostash" ,NULL ); + stash.git_cmd =1 ; + stash.no_stdin =1 ; +if (capture_command (&stash, &buf,GIT_MAX_HEXSZ )) +die (_ ("Cannot autostash" )); +strbuf_trim_trailing_newline (&buf); +if (get_oid (buf.buf, &oid)) +die (_ ("Unexpected stash response: '%s'" ), + buf.buf); +strbuf_reset (&buf); +strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); + +if (safe_create_leading_directories_const (path)) +die (_ ("Could not create directory for '%s'" ), + path); +write_file (path,"%s" ,oid_to_hex (&oid)); +printf (_ ("Created autostash: %s\n" ), buf.buf); +if (reset_head (NULL ,"reset --hard" , +NULL ,RESET_HEAD_HARD ,NULL ,NULL ) <0 ) +die (_ ("could not reset --hard" )); + +if (discard_index (the_repository->index) <0 || +repo_read_index (the_repository) <0 ) +die (_ ("could not read index" )); + } +} +int apply_autostash (const char *path) {struct strbuf autostash =STRBUF_INIT ;
autostash.h +1 -0
@@ -18,6 +18,7 @@ int reset_head(struct object_id *oid, const char *action, const char *switch_to_branch, unsigned flags, const char *reflog_orig_head, const char *reflog_head); +void perform_autostash (const char *path);int apply_autostash (const char *path);#endif
builtin/rebase.c +1 -43
50 lines not on this page · raw message
Signature · 1 line
2.23.0.897.g0a19638b1e
RFC PATCH 6/7 autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS
In reply to Denton LiuSince autostash.c was recently introduced, we should avoid USE_THE_INDEX_COMPATIBILITY_MACROS since we are trying to move away from this in the rest of the codebase. Rewrite the autostash code to not need it and remove its definition.
--- autostash.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
autostash.c +3 -5
@@ -1,5 +1,3 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "git-compat-util.h" #include "autostash.h" #include "cache-tree.h"
@@ -46,7 +44,7 @@ int reset_head(struct object_id *oid, const char *action,if (switch_to_branch && !starts_with (switch_to_branch,"refs/" ))BUG ("Not a fully qualified branch: '%s'" , switch_to_branch); -if (!refs_only &&hold_locked_index (&lock,LOCK_REPORT_ON_ERROR ) <0 ) { +if (!refs_only &&repo_hold_locked_index (the_repository, &lock,LOCK_REPORT_ON_ERROR ) <0 ) { ret =-1 ; goto leave_reset_head; }
@@ -157,8 +155,8 @@ void perform_autostash(const char *path)struct lock_file lock_file =LOCK_INIT ;int fd; - fd =hold_locked_index (&lock_file,0 ); -refresh_cache (REFRESH_QUIET ); + fd =repo_hold_locked_index (the_repository, &lock_file,0 ); +refresh_index (the_repository->index,REFRESH_QUIET ,NULL ,NULL ,NULL );if (0 <= fd)repo_update_index_if_able (the_repository, &lock_file);rollback_lock_file (&lock_file);
Signature · 1 line
2.23.0.897.g0a19638b1e
RFC PATCH 7/7 merge: teach --autostash option
In reply to Denton LiuIn rebase, one can pass the --autostash option to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.
Implement the --autostash option and corresponding merge.autoStash option in merge which stashes before merging and then pops after.
--- builtin/merge.c | 13 +++++++++++++ builtin/pull.c | 9 +++++---- t/t5520-pull.sh | 8 -------- 3 files changed, 18 insertions(+), 12 deletions(-)
builtin/merge.c +13 -0
@@ -40,6 +40,7 @@#include "branch.h" #include "commit-reach.h" #include "wt-status.h" +#include "autostash.h" #define DEFAULT_TWOHEAD (1<<0)#define DEFAULT_OCTOPUS (1<<1)
@@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {NULL }; +static GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH") +static int show_diffstat =1 , shortlog_len =-1 , squash;static int option_commit =-1 ;static int option_edit =-1 ;
@@ -81,6 +84,7 @@ static int show_progress = -1;static int default_to_upstream =1 ;static int signoff;static const char *sign_commit; +static int autostash;static int no_verify;static struct strategy all_strategy[] = {
@@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {OPT_SET_INT (0 ,"progress" , &show_progress,N_ ("force progress reporting" ),1 ), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, +OPT_BOOL (0 ,"autostash" , &autostash, +N_ ("automatically stash/stash pop before and after" )),OPT_BOOL (0 ,"overwrite-ignore" , &overwrite_ignore,N_ ("update ignored files (default)" )),OPT_BOOL (0 ,"signoff" , &signoff,N_ ("add Signed-off-by:" )), OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,strbuf_addf (&reflog_message,"%s: %s" ,getenv ("GIT_REFLOG_ACTION" ), msg); } +apply_autostash (merge_autostash ());if (squash) {squash_message (head_commit, remoteheads); }else {
@@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb) }else if (!strcmp (k,"commit.gpgsign" )) { sign_commit =git_config_bool (k, v) ?"" :NULL ;return 0 ; + }else if (!strcmp (k,"merge.autostash" )) { + autostash =git_config_bool (k, v); +return 0 ; } status =fmt_merge_msg_config (k, v, cb);
@@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,for (j = common; j; j = j->next)commit_list_insert (j->item, &reversed); +if (autostash) +perform_autostash (merge_autostash ());hold_locked_index (&lock,LOCK_DIE_ON_ERROR ); clean =merge_recursive (&o, head, remoteheads->item, reversed, &result);
@@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)/* Invoke 'git reset --merge' */ ret =cmd_reset (nargc, nargv, prefix); +apply_autostash (merge_autostash ()); goto done; }
builtin/pull.c +5 -4
@@ -183,7 +183,7 @@ static struct option pull_options[] = { N_("verify that the named commit has a valid GPG signature"), PARSE_OPT_NOARG),OPT_BOOL (0 ,"autostash" , &opt_autostash, -N_ ("automatically stash/stash pop before and after rebase" )), +N_ ("automatically stash/stash pop before and after" )),OPT_PASSTHRU_ARGV ('s' ,"strategy" , &opt_strategies,N_ ("strategy" ),N_ ("merge strategy to use" ), 0),
@@ -671,6 +671,10 @@ static int run_merge(void)argv_array_pushv (&args, opt_strategy_opts.argv);if (opt_gpg_sign)argv_array_push (&args, opt_gpg_sign); +if (opt_autostash ==0 ) +argv_array_push (&args,"--no-autostash" ); +else if (opt_autostash ==1 ) +argv_array_push (&args,"--autostash" );if (opt_allow_unrelated_histories >0 )argv_array_push (&args,"--allow-unrelated-histories" );
@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)if (get_oid ("HEAD" , &orig_head))oidclr (&orig_head); -if (!opt_rebase && opt_autostash !=-1 ) -die (_ ("--[no-]autostash option is only valid with --rebase." )); - autostash = config_autostash;if (opt_rebase) {if (opt_autostash !=-1 )
t/t5520-pull.sh +0 -8
@@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' ' test_pull_autostash_fail --rebase --no-autostash ' -for i in --autostash --no-autostash -do - test_expect_success "pull $i (without --rebase) is illegal" ' - test_must_fail git pull $i . copy 2>err && - test_i18ngrep "only valid with --rebase" err - ' -done - test_expect_success 'pull.rebase' ' git reset --hard before-rebase && test_config pull.rebase true &&
Signature · 1 line
2.23.0.897.g0a19638b1e
Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
In reply to Denton LiuQuoted 4 lines · Denton Liu
There are many += lists in the
Makefileand, over time, they have gotten slightly out of order, alphabetically. Alphabetically sort all += lists to bring them back in order.
...
Hmm. I like the general thrust, but ...
Makefile LIB_OBJS += checkout.o · line 851
Quoted 5 lines
LIB_OBJS+= combine-diff.oMakefile +1
-1-LIB_OBJS += commit.o LIB_OBJS += commit-graph.o LIB_OBJS += commit-reach.o +LIB_OBJS += commit.o
... I do not particularly see this change (there may be similar ones) desirable. I'd find it it be much more natural to sort "commit-anything" after "commit", and that is true with or without the common extension ".o" added to these entries.
In short, flipping these entries because '.' sorts later than '-' is making the result look "less sorted", at least to me.
Thanks.
Re: [RFC PATCH 2/7] autostash: extract read_one() from rebase
In reply to Denton LiuHi Denton
Quoted 23 lines · Denton Liu
Begin the process of lib-ifying the autostash code. In a future commit, this will be used to implement
--autostashin other builtins.This patch is best viewed with
--color-movedand--color-moved-ws=allow-indentation-change.Signed-off-by Denton Liu <[email protected]>--- autostash.c | 12 ++++++++++++ autostash.h | 9 +++++++++ builtin/rebase.c | 10 +--------- 3 files changed, 22 insertions(+), 9 deletions(-)create mode 100644
autostash.c
create mode 100644autostash.hautostash.c new file +2
-0@@ -0,0 +1,12 @@ +#include "git-compat-util.h" +#include "autostash.h"
autostash.c line 12
Quoted 10 lines
autostash.c new file +10
-0+#include "gettext.h" +#include "strbuf.h" + +int read_one (const char *path,struct strbuf *buf) +{ +if (strbuf_read_file (buf, path,0 ) <0 ) +return error_errno (_ ("could not read '%s'" ), path); +strbuf_trim_trailing_newline (buf); +return 0 ; +}
This looks like it's doing a similar job to read_oneliner() in sequencer.c, is it possible to make that public and use it instead? (There may be a difference if the file is missing but that function already takes a flag so it could probably be modified easily enough.)
Best Wishes
Phillip
Quoted 44 lines
autostash.h new file +9
-0@@ -0,0 +1,9 @@ +#ifndef AUTOSTASH_H +#define AUTOSTASH_H + +#include "strbuf.h" + +/* Read one file, then strip line endings */ +int read_one (const char *path,struct strbuf *buf); + +#endif builtin/rebase.c +1
-9@@ -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 *oreturn 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) {
Re: [RFC PATCH 4/7] autostash: extract reset_head() from rebase
In reply to Denton LiuHi Denton
It's great to see this being libified, I've had it in mind to do this so we can avoid forking 'git checkout' in sequencer.c
Quoted 18 lines · Denton Liu
Begin the process of lib-ifying the autostash code. In a future commit,
This patch is best viewed with
--color-movedand--color-moved-ws=allow-indentation-change.
this will be used to implement--autostashin other builtins.Signed-off-by Denton Liu <[email protected]>--- autostash.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++ autostash.h | 12 +++++ builtin/rebase.c | 137 ----------------------------------------------- 3 files changed, 149 insertions(+), 137 deletions(-)autostash.c +0
-0
autostash.c line 1
Quoted 1 line
autostash.c +1
-0@@ -1,9 +1,17 @@ +#define USE_THE_INDEX_COMPATIBILITY_MACROS
It might be nicer to have a preparatory step that fixes this by adding a 'struct repository *r' argument to the function in builtin/rebase.c before moving the function. You could also do the same for next patch and then move both functions together.
Best Wishes
Phillip
Quoted 324 lines
autostash.c +136
-0+#include "git-compat-util.h" #include "autostash.h" +#include "cache-tree.h" #include "dir.h" #include "gettext.h" +#include "lockfile.h" +#include "refs.h" #include "run-command.h" #include "strbuf.h" +#include "tree-walk.h" +#include "tree.h" +#include "unpack-trees.h" int read_one (const char *path,struct strbuf *buf) {@@ -13,6 +21,135 @@ int read_one(const char *path, struct strbuf *buf)return 0 ; } +int reset_head (struct object_id *oid,const char *action, +const char *switch_to_branch,unsigned flags, +const char *reflog_orig_head,const char *reflog_head) +{ +unsigned detach_head = flags &RESET_HEAD_DETACH ; +unsigned reset_hard = flags &RESET_HEAD_HARD ; +unsigned run_hook = flags &RESET_HEAD_RUN_POST_CHECKOUT_HOOK ; +unsigned refs_only = flags &RESET_HEAD_REFS_ONLY ; +unsigned update_orig_head = flags &RESET_ORIG_HEAD ; +struct object_id head_oid; +struct tree_desc desc[2 ] = { {NULL }, {NULL } }; +struct lock_file lock =LOCK_INIT ; +struct unpack_trees_options unpack_tree_opts; +struct tree *tree; +const char *reflog_action; +struct strbuf msg =STRBUF_INIT ; +size_t prefix_len; +struct object_id *orig =NULL , oid_orig, + *old_orig =NULL , oid_old_orig; +int ret =0 , nr =0 ; + +if (switch_to_branch && !starts_with (switch_to_branch,"refs/" )) +BUG ("Not a fully qualified branch: '%s'" , switch_to_branch); + +if (!refs_only &&hold_locked_index (&lock,LOCK_REPORT_ON_ERROR ) <0 ) { + ret =-1 ; + goto leave_reset_head; + } + +if ((!oid || !reset_hard) &&get_oid ("HEAD" , &head_oid)) { + ret =error (_ ("could not determine HEAD revision" )); + goto leave_reset_head; + } + +if (!oid) + oid = &head_oid; + +if (refs_only) + goto reset_head_refs; + +memset (&unpack_tree_opts,0 ,sizeof (unpack_tree_opts)); +setup_unpack_trees_porcelain (&unpack_tree_opts, action); + unpack_tree_opts.head_idx =1 ; + unpack_tree_opts.src_index = the_repository->index; + unpack_tree_opts.dst_index = the_repository->index; + unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; + unpack_tree_opts.update =1 ; + unpack_tree_opts.merge =1 ; +if (!detach_head) + unpack_tree_opts.reset =1 ; + +if (repo_read_index_unmerged (the_repository) <0 ) { + ret =error (_ ("could not read index" )); + goto leave_reset_head; + } + +if (!reset_hard && !fill_tree_descriptor (the_repository, &desc[nr++], &head_oid)) { + ret =error (_ ("failed to find tree of %s" ), +oid_to_hex (&head_oid)); + goto leave_reset_head; + } + +if (!fill_tree_descriptor (the_repository, &desc[nr++], oid)) { + ret =error (_ ("failed to find tree of %s" ),oid_to_hex (oid)); + goto leave_reset_head; + } + +if (unpack_trees (nr, desc, &unpack_tree_opts)) { + ret =-1 ; + goto leave_reset_head; + } + + tree =parse_tree_indirect (oid); +prime_cache_tree (the_repository, the_repository->index, tree); + +if (write_locked_index (the_repository->index, &lock,COMMIT_LOCK ) <0 ) { + ret =error (_ ("could not write index" )); + goto leave_reset_head; + } + +reset_head_refs: + reflog_action =getenv (GIT_REFLOG_ACTION_ENVIRONMENT ); +strbuf_addf (&msg,"%s: " , reflog_action ? reflog_action :"rebase" ); + prefix_len = msg.len; + +if (update_orig_head) { +if (!get_oid ("ORIG_HEAD" , &oid_old_orig)) + old_orig = &oid_old_orig; +if (!get_oid ("HEAD" , &oid_orig)) { + orig = &oid_orig; +if (!reflog_orig_head) { +strbuf_addstr (&msg,"updating ORIG_HEAD" ); + reflog_orig_head = msg.buf; + } +update_ref (reflog_orig_head,"ORIG_HEAD" , orig, + old_orig,0 ,UPDATE_REFS_MSG_ON_ERR ); + }else if (old_orig) +delete_ref (NULL ,"ORIG_HEAD" , old_orig,0 ); + } + +if (!reflog_head) { +strbuf_setlen (&msg, prefix_len); +strbuf_addstr (&msg,"updating HEAD" ); + reflog_head = msg.buf; + } +if (!switch_to_branch) + ret =update_ref (reflog_head,"HEAD" , oid, orig, + detach_head ?REF_NO_DEREF :0 , +UPDATE_REFS_MSG_ON_ERR ); +else { + ret =update_ref (reflog_head, switch_to_branch, oid, +NULL ,0 ,UPDATE_REFS_MSG_ON_ERR ); +if (!ret) + ret =create_symref ("HEAD" , switch_to_branch, + reflog_head); + } +if (run_hook) +run_hook_le (NULL ,"post-checkout" , +oid_to_hex (orig ? orig : &null_oid), +oid_to_hex (oid),"1" ,NULL ); + +leave_reset_head: +strbuf_release (&msg); +rollback_lock_file (&lock); +while (nr) +free ((void *)desc[--nr].buffer); +return ret; +} +int apply_autostash (const char *path) {struct strbuf autostash =STRBUF_INIT ;autostash.h +12
-0@@ -6,6 +6,18 @@/* Read one file, then strip line endings */ int read_one (const char *path,struct strbuf *buf); +#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" + +#define RESET_HEAD_DETACH (1<<0) +#define RESET_HEAD_HARD (1<<1) +#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) +#define RESET_HEAD_REFS_ONLY (1<<3) +#define RESET_ORIG_HEAD (1<<4) + +int reset_head (struct object_id *oid,const char *action, +const char *switch_to_branch,unsigned flags, +const char *reflog_orig_head,const char *reflog_head); +int apply_autostash (const char *path);#endif builtin/rebase.c +0
-137@@ -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 ;
Re: [RFC PATCH 6/7] autostash.c: undefine USE_THE_INDEX_COMPATIBILITY_MACROS
In reply to Denton LiuHi Denton
Quoted 21 lines · Denton Liu
Since
autostash.cwas recently introduced, we should avoidUSE_THE_INDEX_COMPATIBILITY_MACROSsince 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 <[email protected]>--- autostash.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)autostash.c +0
-2@@ -1,5 +1,3 @@ -#define USE_THE_INDEX_COMPATIBILITY_MACROS -#include "git-compat-util.h" #include "autostash.h" #include "cache-tree.h"
autostash.c int reset_head(struct object_id *oid, const char *action, · line 47
Quoted 5 lines
autostash.c +1
-1@@ -46,7 +44,7 @@ int reset_head(struct object_id *oid, const char *action,if (switch_to_branch && !starts_with (switch_to_branch,"refs/" ))BUG ("Not a fully qualified branch: '%s'" , switch_to_branch); -if (!refs_only &&hold_locked_index (&lock,LOCK_REPORT_ON_ERROR ) <0 ) { +if (!refs_only &&repo_hold_locked_index (the_repository, &lock,LOCK_REPORT_ON_ERROR ) <0 ) {
As I understand it the reason for moving away from hold_locked_index() is that it relies on a global variable. While replacing it with an explicit reference the the_repository removes the implicit dependency on global state, it does not move us away from depending on a global variable. I think it would be nicer to change these functions to take a struct repository* before moving them.
Best Wishes
Phillip
Quoted 15 lines
autostash.c +2
-2ret =-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);
Re: [RFC PATCH 7/7] merge: teach --autostash option
In reply to Denton LiuHi Denton
Quoted 3 lines · Denton Liu
In rebase, one can pass the
--autostashoption to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.
It might be helpful to say why this option is useful. I can see one might want to stash before doing git pull in the same way as one might before a rebase but what's the motivation for doing it before a normal merge?
Quoted 90 lines
Implement the
--autostashoption and correspondingmerge.autoStashoption in merge which stashes before merging and then pops after.Reported-by Alban Gruin <[email protected]>Signed-off-by Denton Liu <[email protected]>--- builtin/merge.c | 13 +++++++++++++ builtin/pull.c | 9 +++++---- t/t5520-pull.sh | 8 -------- 3 files changed, 18 insertions(+), 12 deletions(-)builtin/merge.c +13
-0@@ -40,6 +40,7 @@#include "branch.h" #include "commit-reach.h" #include "wt-status.h" +#include "autostash.h" #define DEFAULT_TWOHEAD (1<<0)#define DEFAULT_OCTOPUS (1<<1)@@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {NULL }; +static GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH") +static int show_diffstat =1 , shortlog_len =-1 , squash;static int option_commit =-1 ;static int option_edit =-1 ;@@ -81,6 +84,7 @@ static int show_progress = -1;static int default_to_upstream =1 ;static int signoff;static const char *sign_commit; +static int autostash;static int no_verify;static struct strategy all_strategy[] = {@@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {OPT_SET_INT (0 ,"progress" , &show_progress,N_ ("force progress reporting" ),1 ), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, +OPT_BOOL (0 ,"autostash" , &autostash, +N_ ("automatically stash/stash pop before and after" )),OPT_BOOL (0 ,"overwrite-ignore" , &overwrite_ignore,N_ ("update ignored files (default)" )),OPT_BOOL (0 ,"signoff" , &signoff,N_ ("add Signed-off-by:" )), OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),@@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,strbuf_addf (&reflog_message,"%s: %s" ,getenv ("GIT_REFLOG_ACTION" ), msg); } +apply_autostash (merge_autostash ());if (squash) {squash_message (head_commit, remoteheads); }else {@@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb) }else if (!strcmp (k,"commit.gpgsign" )) { sign_commit =git_config_bool (k, v) ?"" :NULL ;return 0 ; + }else if (!strcmp (k,"merge.autostash" )) { + autostash =git_config_bool (k, v); +return 0 ; } status =fmt_merge_msg_config (k, v, cb);@@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,for (j = common; j; j = j->next)commit_list_insert (j->item, &reversed); +if (autostash) +perform_autostash (merge_autostash ());hold_locked_index (&lock,LOCK_DIE_ON_ERROR ); clean =merge_recursive (&o, head, remoteheads->item, reversed, &result);@@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)/* Invoke 'git reset --merge' */ ret =cmd_reset (nargc, nargv, prefix); +apply_autostash (merge_autostash ()); goto done; }builtin/pull.c +0
-0
builtin/pull.c static struct option pull_options[] = { · line 186
Quoted 5 lines
builtin/pull.c +1
-1@@ -183,7 +183,7 @@ static struct option pull_options[] = { N_("verify that the named commit has a valid GPG signature"), PARSE_OPT_NOARG),OPT_BOOL (0 ,"autostash" , &opt_autostash, - N_("automatically stash/stash pop before and after rebase")), + N_("automatically stash/stash pop before and after")),
I've not looked closely at the code in this patch but noticed this. I think it would read better if it said
automatically stash/stash pop before and after pulling
Best Wishes
Phillip
Quoted 44 lines
builtin/pull.c +4
-3OPT_PASSTHRU_ARGV ('s' ,"strategy" , &opt_strategies,N_ ("strategy" ),N_ ("merge strategy to use" ), 0),@@ -671,6 +671,10 @@ static int run_merge(void)argv_array_pushv (&args, opt_strategy_opts.argv);if (opt_gpg_sign)argv_array_push (&args, opt_gpg_sign); +if (opt_autostash ==0 ) +argv_array_push (&args,"--no-autostash" ); +else if (opt_autostash ==1 ) +argv_array_push (&args,"--autostash" );if (opt_allow_unrelated_histories >0 )argv_array_push (&args,"--allow-unrelated-histories" );@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)if (get_oid ("HEAD" , &orig_head))oidclr (&orig_head); -if (!opt_rebase && opt_autostash !=-1 ) -die (_ ("--[no-]autostash option is only valid with --rebase." )); - autostash = config_autostash;if (opt_rebase) {if (opt_autostash !=-1 )t/t5520-pull.sh +0
-8@@ -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 &&
Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
In reply to Junio C HamanoHi Junio,
Quoted 20 lines · Junio C Hamano
Quoted 4 lines · Denton Liu
There are many += lists in the
Makefileand, over time, they have gotten slightly out of order, alphabetically. Alphabetically sort all += lists to bring them back in order.
...Hmm. I like the general thrust, but ...
Makefile LIB_OBJS += checkout.o · line 851
Quoted 5 lines
LIB_OBJS+= combine-diff.oMakefile +1
-1-LIB_OBJS += commit.o LIB_OBJS += commit-graph.o LIB_OBJS += commit-reach.o +LIB_OBJS += commit.o... I do not particularly see this change (there may be similar ones) desirable. I'd find it it be much more natural to sort "commit-anything" after "commit", and that is true with or without the common extension ".o" added to these entries.
Quoted 2 lines
In short, flipping these entries because '.' sorts later than '-' is making the result look "less sorted", at least to me.
The problem with this argument is that it disagrees with ASCII, as - has code 0x2d while . has code 0x2e, i.e. it is lexicographically _larger_.
So Denton's patch does the correct thing.
Ciao,
Dscho
Re: [RFC PATCH 2/7] autostash: extract read_one() from rebase
In reply to Phillip WoodHi,
Quoted 33 lines · Phillip Wood
Hi Denton
Quoted 30 lines · Denton Liu
Begin the process of lib-ifying the autostash code. In a future commit, this will be used to implement
--autostashin other builtins.This patch is best viewed with
--color-movedand--color-moved-ws=allow-indentation-change.Signed-off-by Denton Liu <[email protected]>--- autostash.c | 12 ++++++++++++ autostash.h | 9 +++++++++ builtin/rebase.c | 10 +--------- 3 files changed, 22 insertions(+), 9 deletions(-)create mode 100644
autostash.c
create mode 100644autostash.hautostash.c new file +9
-0@@ -0,0 +1,12 @@ +#include "git-compat-util.h" +#include "autostash.h" +#include "gettext.h" +#include "strbuf.h" + +int read_one (const char *path,struct strbuf *buf) +{ +if (strbuf_read_file (buf, path,0 ) <0 ) +return error_errno (_ ("could not read '%s'" ), path);
Quoted 8 lines
autostash.c line 12
Quoted 3 lines
autostash.c new file +3
-0+strbuf_trim_trailing_newline (buf); +return 0 ; +}This looks like it's doing a similar job to
read_oneliner()insequencer.c, is it possible to make that public and use it instead? (There may be a difference if the file is missing but that function already takes a flag so it could probably be modified easily enough.)
Oh, I would _love_ to see those two functions reconciled.
Thanks,
Dscho
Quoted 52 lines
Best Wishes
Phillip
Quoted 45 lines
autostash.h new file +9
-0@@ -0,0 +1,9 @@ +#ifndef AUTOSTASH_H +#define AUTOSTASH_H + +#include "strbuf.h" + +/* Read one file, then strip line endings */ +int read_one (const char *path,struct strbuf *buf); + +#endif builtin/rebase.c +1
-0@@ -27,6 +27,7 @@#include "branch.h" #include "sequencer.h" #include "rebase-interactive.h" +#include "autostash.h" static char const *const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] "@@ -561,15 +562,6 @@ static const char *state_dir_path(const char *filename,struct
rebase_options*o
return path.buf;
}builtin/rebase.c +0
-9-/* Read one file, then strip line endings */ -static int read_one (const char *path,struct strbuf *buf) -{ -if (strbuf_read_file (buf, path,0 ) <0 ) -return error_errno (_ ("could not read '%s'" ), path); -strbuf_trim_trailing_newline (buf); -return 0 ; -} -/* Initialize the rebase options from the state directory. */ static int read_basic_state (struct rebase_options *opts) {
Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
In reply to Johannes SchindelinHi Johannes,
Quoted 24 lines · Johannes Schindelin
Hi Junio,
Quoted 20 lines · Junio C Hamano
Quoted 4 lines · Denton Liu
There are many += lists in the
Makefileand, over time, they have gotten slightly out of order, alphabetically. Alphabetically sort all += lists to bring them back in order.
...Hmm. I like the general thrust, but ...
Makefile LIB_OBJS += checkout.o · line 851
Quoted 5 lines
LIB_OBJS+= combine-diff.oMakefile +1
-1-LIB_OBJS += commit.o LIB_OBJS += commit-graph.o LIB_OBJS += commit-reach.o +LIB_OBJS += commit.o... I do not particularly see this change (there may be similar ones) desirable. I'd find it it be much more natural to sort "commit-anything" after "commit", and that is true with or without the common extension ".o" added to these entries.
Quoted 8 lines
Quoted 2 lines
In short, flipping these entries because '.' sorts later than '-' is making the result look "less sorted", at least to me.
The problem with this argument is that it disagrees with ASCII, as
-has code0x2dwhile.has code0x2e, i.e. it is lexicographically _larger_.So Denton's patch does the correct thing.
I actually agree with Junio on this one. Without the prefixes, "commit" would go before "commit-graph" so I think it would make more sense to order with the prefixes removed instead of taking the naive ordering by just sorting each block.
Thanks,
Denton
Quoted 3 lines
Ciao,
Dscho
Re: [RFC PATCH 4/7] autostash: extract reset_head() from rebase
In reply to Phillip WoodHi,
[sorry, Phillip, my reply-all fu deserts me today, apparently.]
Quoted 19 lines · Phillip Wood
Hi Denton
It's great to see this being libified, I've had it in mind to do this so we can avoid forking 'git checkout' in
sequencer.cQuoted 13 lines · Denton Liu
Begin the process of lib-ifying the autostash code. In a future commit,
This patch is best viewed with
--color-movedand--color-moved-ws=allow-indentation-change.
this will be used to implement--autostashin other builtins.Signed-off-by Denton Liu <[email protected]>--- autostash.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++ autostash.h | 12 +++++ builtin/rebase.c | 137 ----------------------------------------------- 3 files changed, 149 insertions(+), 137 deletions(-)
Quoted 11 lines
autostash.c line 1
Quoted 6 lines
autostash.c +1
-0@@ -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.cbefore moving the function. You could also do the same for next patch and then move both functions together.
In addition to that, I think that reset_head()
- should live in its own file, not be hidden in autostash.c,
- its default reflog action should _not_ be "rebase".
- ideally be made the working horse of builtin/reset.c,
- in addition to that struct repository *r, it should probably accept a struct index_state *index and a const char *worktree_directory, but that can easily come in the future, as needed.
Thanks,
Dscho
Quoted 337 lines
Best Wishes
Phillip
Quoted 331 lines
autostash.c +136
-0+#include "git-compat-util.h" #include "autostash.h" +#include "cache-tree.h" #include "dir.h" #include "gettext.h" +#include "lockfile.h" +#include "refs.h" #include "run-command.h" #include "strbuf.h" +#include "tree-walk.h" +#include "tree.h" +#include "unpack-trees.h" int read_one (const char *path,struct strbuf *buf) {@@ -13,6 +21,135 @@ int read_one(const char *path, struct strbuf *buf)return 0 ; } +int reset_head (struct object_id *oid,const char *action, +const char *switch_to_branch,unsigned flags, +const char *reflog_orig_head,const char *reflog_head) +{ +unsigned detach_head = flags &RESET_HEAD_DETACH ; +unsigned reset_hard = flags &RESET_HEAD_HARD ; +unsigned run_hook = flags &RESET_HEAD_RUN_POST_CHECKOUT_HOOK ; +unsigned refs_only = flags &RESET_HEAD_REFS_ONLY ; +unsigned update_orig_head = flags &RESET_ORIG_HEAD ; +struct object_id head_oid; +struct tree_desc desc[2 ] = { {NULL }, {NULL } }; +struct lock_file lock =LOCK_INIT ; +struct unpack_trees_options unpack_tree_opts; +struct tree *tree; +const char *reflog_action; +struct strbuf msg =STRBUF_INIT ; +size_t prefix_len; +struct object_id *orig =NULL , oid_orig, + *old_orig =NULL , oid_old_orig; +int ret =0 , nr =0 ; + +if (switch_to_branch && !starts_with (switch_to_branch,"refs/" )) +BUG ("Not a fully qualified branch: '%s'" , switch_to_branch); + +if (!refs_only &&hold_locked_index (&lock,LOCK_REPORT_ON_ERROR ) <0 ) { + ret =-1 ; + goto leave_reset_head; + } + +if ((!oid || !reset_hard) &&get_oid ("HEAD" , &head_oid)) { + ret =error (_ ("could not determine HEAD revision" )); + goto leave_reset_head; + } + +if (!oid) + oid = &head_oid; + +if (refs_only) + goto reset_head_refs; + +memset (&unpack_tree_opts,0 ,sizeof (unpack_tree_opts)); +setup_unpack_trees_porcelain (&unpack_tree_opts, action); + unpack_tree_opts.head_idx =1 ; + unpack_tree_opts.src_index = the_repository->index; + unpack_tree_opts.dst_index = the_repository->index; + unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; + unpack_tree_opts.update =1 ; + unpack_tree_opts.merge =1 ; +if (!detach_head) + unpack_tree_opts.reset =1 ; + +if (repo_read_index_unmerged (the_repository) <0 ) { + ret =error (_ ("could not read index" )); + goto leave_reset_head; + } + +if (!reset_hard && !fill_tree_descriptor (the_repository, &desc[nr++], &head_oid)) { + ret =error (_ ("failed to find tree of %s" ), +oid_to_hex (&head_oid)); + goto leave_reset_head; + } + +if (!fill_tree_descriptor (the_repository, &desc[nr++], oid)) { + ret =error (_ ("failed to find tree of %s" ),oid_to_hex (oid)); + goto leave_reset_head; + } + +if (unpack_trees (nr, desc, &unpack_tree_opts)) { + ret =-1 ; + goto leave_reset_head; + } + + tree =parse_tree_indirect (oid); +prime_cache_tree (the_repository, the_repository->index, tree); + +if (write_locked_index (the_repository->index, &lock,COMMIT_LOCK ) <0 ) { + ret =error (_ ("could not write index" )); + goto leave_reset_head; + } + +reset_head_refs: + reflog_action =getenv (GIT_REFLOG_ACTION_ENVIRONMENT ); +strbuf_addf (&msg,"%s: " , reflog_action ? reflog_action :"rebase" ); + prefix_len = msg.len; + +if (update_orig_head) { +if (!get_oid ("ORIG_HEAD" , &oid_old_orig)) + old_orig = &oid_old_orig; +if (!get_oid ("HEAD" , &oid_orig)) { + orig = &oid_orig; +if (!reflog_orig_head) { +strbuf_addstr (&msg,"updating ORIG_HEAD" ); + reflog_orig_head = msg.buf; + } +update_ref (reflog_orig_head,"ORIG_HEAD" , orig, + old_orig,0 ,UPDATE_REFS_MSG_ON_ERR ); + }else if (old_orig) +delete_ref (NULL ,"ORIG_HEAD" , old_orig,0 ); + } + +if (!reflog_head) { +strbuf_setlen (&msg, prefix_len); +strbuf_addstr (&msg,"updating HEAD" ); + reflog_head = msg.buf; + } +if (!switch_to_branch) + ret =update_ref (reflog_head,"HEAD" , oid, orig, + detach_head ?REF_NO_DEREF :0 , +UPDATE_REFS_MSG_ON_ERR ); +else { + ret =update_ref (reflog_head, switch_to_branch, oid, +NULL ,0 ,UPDATE_REFS_MSG_ON_ERR ); +if (!ret) + ret =create_symref ("HEAD" , switch_to_branch, + reflog_head); + } +if (run_hook) +run_hook_le (NULL ,"post-checkout" , +oid_to_hex (orig ? orig : &null_oid), +oid_to_hex (oid),"1" ,NULL ); + +leave_reset_head: +strbuf_release (&msg); +rollback_lock_file (&lock); +while (nr) +free ((void *)desc[--nr].buffer); +return ret; +} +int apply_autostash(const char *path) { struct strbuf autostash = STRBUF_INIT;autostash.h +12
-0@@ -6,6 +6,18 @@/* Read one file, then strip line endings */ int read_one (const char *path,struct strbuf *buf); +#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" + +#define RESET_HEAD_DETACH (1<<0) +#define RESET_HEAD_HARD (1<<1) +#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) +#define RESET_HEAD_REFS_ONLY (1<<3) +#define RESET_ORIG_HEAD (1<<4) + +int reset_head (struct object_id *oid,const char *action, +const char *switch_to_branch,unsigned flags, +const char *reflog_orig_head,const char *reflog_head); +int apply_autostash (const char *path);#endif builtin/rebase.c +0
-84@@ -734,143 +734,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value) } } -#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" - -#define RESET_HEAD_DETACH (1<<0) -#define RESET_HEAD_HARD (1<<1) -#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) -#define RESET_HEAD_REFS_ONLY (1<<3) -#define RESET_ORIG_HEAD (1<<4) - -static int reset_head (struct object_id *oid,const char *action, -const char *switch_to_branch,unsigned flags, -const char *reflog_orig_head,const char *reflog_head) -{ -unsigned detach_head = flags &RESET_HEAD_DETACH ; -unsigned reset_hard = flags &RESET_HEAD_HARD ; -unsigned run_hook = flags &RESET_HEAD_RUN_POST_CHECKOUT_HOOK ; -unsigned refs_only = flags &RESET_HEAD_REFS_ONLY ; -unsigned update_orig_head = flags &RESET_ORIG_HEAD ; -struct object_id head_oid; -struct tree_desc desc[2 ] = { {NULL }, {NULL } }; -struct lock_file lock =LOCK_INIT ; -struct unpack_trees_options unpack_tree_opts; -struct tree *tree; -const char *reflog_action; -struct strbuf msg =STRBUF_INIT ; -size_t prefix_len; -struct object_id *orig =NULL , oid_orig, - *old_orig =NULL , oid_old_orig; -int ret =0 , nr =0 ; - -if (switch_to_branch && !starts_with (switch_to_branch,"refs/" )) -BUG ("Not a fully qualified branch: '%s'" , switch_to_branch); - -if (!refs_only &&hold_locked_index (&lock,LOCK_REPORT_ON_ERROR ) <0 ) { - ret =-1 ; - goto leave_reset_head; - } - -if ((!oid || !reset_hard) &&get_oid ("HEAD" , &head_oid)) { - ret =error (_ ("could not determine HEAD revision" )); - goto leave_reset_head; - } - -if (!oid) - oid = &head_oid; - -if (refs_only) - goto reset_head_refs; - -memset (&unpack_tree_opts,0 ,sizeof (unpack_tree_opts)); -setup_unpack_trees_porcelain (&unpack_tree_opts, action); - unpack_tree_opts.head_idx =1 ; - unpack_tree_opts.src_index = the_repository->index; - unpack_tree_opts.dst_index = the_repository->index; - unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; - unpack_tree_opts.update =1 ; - unpack_tree_opts.merge =1 ; -if (!detach_head) - unpack_tree_opts.reset =1 ; - -if (repo_read_index_unmerged (the_repository) <0 ) { - ret =error (_ ("could not read index" )); - goto leave_reset_head; - } - -if (!reset_hard && !fill_tree_descriptor (the_repository, &desc[nr++], &head_oid)) { - ret =error (_ ("failed to find tree of %s" ), -oid_to_hex (&head_oid)); - goto leave_reset_head; - } - -if (!fill_tree_descriptor (the_repository, &desc[nr++], oid)) { - ret =error (_ ("failed to find tree of %s" ),oid_to_hex (oid)); - goto leave_reset_head; - } - -if (unpack_trees (nr, desc, &unpack_tree_opts)) { - ret =-1 ; - goto leave_reset_head; - } - - tree =parse_tree_indirect (oid); -prime_cache_tree (the_repository, the_repository->index, tree); - -if (write_locked_index (the_repository->index, &lock,COMMIT_LOCK ) <0 ){
builtin/rebase.c +0
-53- ret =error (_ ("could not write index" )); - goto leave_reset_head; - } - -reset_head_refs: - reflog_action =getenv (GIT_REFLOG_ACTION_ENVIRONMENT ); -strbuf_addf (&msg,"%s: " , reflog_action ? reflog_action :"rebase" ); - prefix_len = msg.len; - -if (update_orig_head) { -if (!get_oid ("ORIG_HEAD" , &oid_old_orig)) - old_orig = &oid_old_orig; -if (!get_oid ("HEAD" , &oid_orig)) { - orig = &oid_orig; -if (!reflog_orig_head) { -strbuf_addstr (&msg,"updating ORIG_HEAD" ); - reflog_orig_head = msg.buf; - } -update_ref (reflog_orig_head,"ORIG_HEAD" , orig, - old_orig,0 ,UPDATE_REFS_MSG_ON_ERR ); - }else if (old_orig) -delete_ref (NULL ,"ORIG_HEAD" , old_orig,0 ); - } - -if (!reflog_head) { -strbuf_setlen (&msg, prefix_len); -strbuf_addstr (&msg,"updating HEAD" ); - reflog_head = msg.buf; - } -if (!switch_to_branch) - ret =update_ref (reflog_head,"HEAD" , oid, orig, - detach_head ?REF_NO_DEREF :0 , -UPDATE_REFS_MSG_ON_ERR ); -else { - ret =update_ref (reflog_head, switch_to_branch, oid, -NULL ,0 ,UPDATE_REFS_MSG_ON_ERR ); -if (!ret) - ret =create_symref ("HEAD" , switch_to_branch, - reflog_head); - } -if (run_hook) -run_hook_le (NULL ,"post-checkout" , -oid_to_hex (orig ? orig : &null_oid), -oid_to_hex (oid),"1" ,NULL ); - -leave_reset_head: -strbuf_release (&msg); -rollback_lock_file (&lock); -while (nr) -free ((void *)desc[--nr].buffer); -return ret; -} -static int move_to_original_branch (struct rebase_options *opts) {struct strbuf orig_head_reflog =STRBUF_INIT , head_reflog =STRBUF_INIT ;
Re: [RFC PATCH 5/7] autostash: extract perform_autostash() from rebase
In reply to Denton LiuHi Denton,
Quoted 26 lines · Denton Liu
Continue the process of lib-ifying the autostash code. In a future commit, this will be used to implement
--autostashin other builtins.This patch is best viewed with
--color-movedand--color-moved-ws=allow-indentation-change.Signed-off-by Denton Liu <[email protected]>--- autostash.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ autostash.h | 1 + builtin/rebase.c | 44 +------------------------------------------- 3 files changed, 48 insertions(+), 43 deletions(-)autostash.c +1
-0@@ -12,6 +12,7 @@#include "tree-walk.h" #include "tree.h" #include "unpack-trees.h" +#include "wt-status.h" int read_one (const char *path,struct strbuf *buf) {
autostash.c int reset_head(struct object_id *oid, const char *action, · line 154
Quoted 4 lines
autostash.c +1
-0@@ -150,6 +151,51 @@ int reset_head(struct object_id *oid, const char *action,return ret; } +void perform_autostash (const char *path)
Maybe we can find a better name than "perform_autostash"? It is not clear whether this is the "saving" or "applying" part of the autostash, I think.
Maybe save_autostash()? And maybe instead of path, the parameter could be save_hash_to_path or something similar?
Now that I think of it, I forgot to mention in a reply to an earlier patch in this series that reset_head() might be too generic a name to be a global function...
Ciao,
Dscho
Quoted 118 lines
autostash.c +44
-0+{ +struct strbuf buf =STRBUF_INIT ; +struct lock_file lock_file =LOCK_INIT ; +int fd; + + fd =hold_locked_index (&lock_file,0 ); +refresh_cache (REFRESH_QUIET ); +if (0 <= fd) +repo_update_index_if_able (the_repository, &lock_file); +rollback_lock_file (&lock_file); + +if (has_unstaged_changes (the_repository,1 ) || +has_uncommitted_changes (the_repository,1 )) { +struct child_process stash =CHILD_PROCESS_INIT ; +struct object_id oid; + +argv_array_pushl (&stash.args, +"stash" ,"create" ,"autostash" ,NULL ); + stash.git_cmd =1 ; + stash.no_stdin =1 ; +if (capture_command (&stash, &buf,GIT_MAX_HEXSZ )) +die (_ ("Cannot autostash" )); +strbuf_trim_trailing_newline (&buf); +if (get_oid (buf.buf, &oid)) +die (_ ("Unexpected stash response: '%s'" ), + buf.buf); +strbuf_reset (&buf); +strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); + +if (safe_create_leading_directories_const (path)) +die (_ ("Could not create directory for '%s'" ), + path); +write_file (path,"%s" ,oid_to_hex (&oid)); +printf (_ ("Created autostash: %s\n" ), buf.buf); +if (reset_head (NULL ,"reset --hard" , +NULL ,RESET_HEAD_HARD ,NULL ,NULL ) <0 ) +die (_ ("could not reset --hard" )); + +if (discard_index (the_repository->index) <0 || +repo_read_index (the_repository) <0 ) +die (_ ("could not read index" )); + } +} +int apply_autostash (const char *path) {struct strbuf autostash =STRBUF_INIT ;autostash.h +1
-0@@ -18,6 +18,7 @@ int reset_head(struct object_id *oid, const char *action, const char *switch_to_branch, unsigned flags, const char *reflog_orig_head, const char *reflog_head); +void perform_autostash (const char *path);int apply_autostash (const char *path);#endif builtin/rebase.c +1
-44@@ -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
Re: [RFC PATCH 7/7] merge: teach --autostash option
In reply to Denton LiuHi Denton,
Quoted 8 lines · Denton Liu
In rebase, one can pass the
--autostashoption to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.Implement the
--autostashoption and correspondingmerge.autoStashoption in merge which stashes before merging and then pops after.Reported-by Alban Gruin <[email protected]>
Maybe "Suggested-by" would be more accurate, it is not like this feature request was a bug report...
Quoted 46 lines
Signed-off-by Denton Liu <[email protected]>--- builtin/merge.c | 13 +++++++++++++ builtin/pull.c | 9 +++++---- t/t5520-pull.sh | 8 -------- 3 files changed, 18 insertions(+), 12 deletions(-)builtin/merge.c +6
-0@@ -40,6 +40,7 @@#include "branch.h" #include "commit-reach.h" #include "wt-status.h" +#include "autostash.h" #define DEFAULT_TWOHEAD (1<<0)#define DEFAULT_OCTOPUS (1<<1)@@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {NULL }; +static GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH") +static int show_diffstat =1 , shortlog_len =-1 , squash;static int option_commit =-1 ;static int option_edit =-1 ;@@ -81,6 +84,7 @@ static int show_progress = -1;static int default_to_upstream =1 ;static int signoff;static const char *sign_commit; +static int autostash;static int no_verify;static struct strategy all_strategy[] = {@@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {OPT_SET_INT (0 ,"progress" , &show_progress,N_ ("force progress reporting" ),1 ), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, +OPT_BOOL (0 ,"autostash" , &autostash, +N_ ("automatically stash/stash pop before and after" )),OPT_BOOL (0 ,"overwrite-ignore" , &overwrite_ignore,N_ ("update ignored files (default)" )),OPT_BOOL (0 ,"signoff" , &signoff,N_ ("add Signed-off-by:" )), OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
builtin/merge.c static void finish(struct commit *head_commit, · line 449
Quoted 4 lines
builtin/merge.c +1
-0@@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,strbuf_addf (&reflog_message,"%s: %s" ,getenv ("GIT_REFLOG_ACTION" ), msg); } +apply_autostash (merge_autostash ());
Should this not be guarded by if (autostash)?
Quoted 23 lines
builtin/merge.c +5
-0if (squash) {squash_message (head_commit, remoteheads); }else {@@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb) }else if (!strcmp (k,"commit.gpgsign" )) { sign_commit =git_config_bool (k, v) ?"" :NULL ;return 0 ; + }else if (!strcmp (k,"merge.autostash" )) { + autostash =git_config_bool (k, v); +return 0 ; } status =fmt_merge_msg_config (k, v, cb);@@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,for (j = common; j; j = j->next)commit_list_insert (j->item, &reversed); +if (autostash) +perform_autostash (merge_autostash ());hold_locked_index (&lock,LOCK_DIE_ON_ERROR ); clean =merge_recursive (&o, head, remoteheads->item, reversed, &result);
builtin/merge.c int cmd_merge(int argc, const char **argv, const char *prefix) · line 1303
Quoted 4 lines
builtin/merge.c +1
-0@@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)/* Invoke 'git reset --merge' */ ret =cmd_reset (nargc, nargv, prefix); +apply_autostash (merge_autostash ());
Again, this should be guarded by if (autostash), methinks.
builtin/pull.c static struct option pull_options[] = { · line 186
Quoted 13 lines
builtin/merge.c +0
-0goto done; }builtin/pull.c +1
-1@@ -183,7 +183,7 @@ static struct option pull_options[] = { N_("verify that the named commit has a valid GPG signature"), PARSE_OPT_NOARG),OPT_BOOL (0 ,"autostash" , &opt_autostash, - N_("automatically stash/stash pop before and after rebase")), + N_("automatically stash/stash pop before and after")),
Makes sense; this is now shared between the rebase and the merge modes.
builtin/pull.c static int run_merge(void) · line 677
Quoted 11 lines
builtin/pull.c +4
-0OPT_PASSTHRU_ARGV ('s' ,"strategy" , &opt_strategies,N_ ("strategy" ),N_ ("merge strategy to use" ), 0),@@ -671,6 +671,10 @@ static int run_merge(void)argv_array_pushv (&args, opt_strategy_opts.argv);if (opt_gpg_sign)argv_array_push (&args, opt_gpg_sign); +if (opt_autostash ==0 ) +argv_array_push (&args,"--no-autostash" ); +else if (opt_autostash ==1 ) +argv_array_push (&args,"--autostash" );
Or shorter:
argv_array_pushf(&args, "%s-autostash", opt_autostash ? "-" : "--no");
Ah, but that would mishandle -1. I bet I will be puzzled by this again. Maybe it would make sense to mention in a code comment that it can be -1 in which case we leave it to rebase to use the config settings to determine whether or not to autostash.
Quoted 19 lines
builtin/pull.c +0
-3if (opt_allow_unrelated_histories >0 )argv_array_push (&args,"--allow-unrelated-histories" );@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)if (get_oid ("HEAD" , &orig_head))oidclr (&orig_head); -if (!opt_rebase && opt_autostash !=-1 ) -die (_ ("--[no-]autostash option is only valid with --rebase." )); - autostash = config_autostash;if (opt_rebase) {if (opt_autostash !=-1 )t/t5520-pull.sh +0
-0@@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' ' test_pull_autostash_fail --rebase --no-autostash
t/t5520-pull.sh test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' ' · line 375
Quoted 10 lines
t/t5520-pull.sh +0
-8' -for i in --autostash --no-autostash -do - test_expect_success "pull $i (without --rebase) is illegal" ' - test_must_fail git pull $i . copy 2>err && - test_i18ngrep "only valid with --rebase" err - ' -done -
Nice!
Dscho
Quoted 7 lines
t/t5520-pull.sh +0
-1test_expect_success 'pull.rebase' ' git reset --hard before-rebase && test_config pull.rebase true && --2.23.0.897.g0a19638b1e
Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
In reply to Johannes SchindelinQuoted 11 lines · Johannes Schindelin
Quoted 7 lines
... I do not particularly see this change (there may be similar ones) desirable. I'd find it it be much more natural to sort "commit-anything" after "commit", and that is true with or without the common extension ".o" added to these entries.
In short, flipping these entries because '.' sorts later than '-' is making the result look "less sorted", at least to me.
The problem with this argument is that it disagrees with ASCII, as
-has code0x2dwhile.has code0x2e, 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 ;-)
Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
Quoted 14 lines · Junio C Hamano
Quoted 11 lines · Johannes Schindelin
Quoted 7 lines
... I do not particularly see this change (there may be similar ones) desirable. I'd find it it be much more natural to sort "commit-anything" after "commit", and that is true with or without the common extension ".o" added to these entries.
In short, flipping these entries because '.' sorts later than '-' is making the result look "less sorted", at least to me.
The problem with this argument is that it disagrees with ASCII, as
-has code0x2dwhile.has code0x2e, i.e. it is lexicographically _larger_.
Quoted 8 lines
I am saying that sorting these in ASCII order did not produce result that is easy to the eyes.
You are saying that Denton's patch sorted these lines in ASCII order.
I agree with you that it did correctly sort them in ASCII order.
That does not make the patch right ;-)
What's the purpose of sorting them, though? I thought it was less for aesthetics and more to to keep lines deterministic (to avoid two branches adding the same line in different places, thus causing weirdness when the two are merged). In that case, I think we care less about the exact order and more that anybody can easily reproduce the same sort (by running "10:!sort" or whatever you weird emacs-types would type).
-Peff
Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
Quoted 10 lines · Jeff King
Quoted 2 lines
...
I agree with you that it did correctly sort them in ASCII order.What's the purpose of sorting them, though? I thought it was less for aesthetics and more to to keep lines deterministic (to avoid two branches adding the same line in different places, thus causing weirdness when the two are merged). In that case, I think we care less about the exact order and more that anybody can easily reproduce the same sort (by running "10:!sort" or whatever you weird emacs-types would type).
In the ideal world, "sort" would have a handy option we can tell it to reshuffle the ASCII table in such a way that all punctuations come before alphanumeric, making sure "/" and "." are the first two letters in the alphabet, and everybody can use it to sort the lines reproducibly and also readably. But I do not know of such a widely used implementation of "sort", so...
If we had known better, we would have used such a custom sort order to sort the index entries, making sure that slash sorts before any other byte ;-)
Re: [RFC PATCH 1/7] Makefile: alphabetically sort += lists
In reply to Denton LiuHi,
Quoted 34 lines · Denton Liu
Hi Johannes,
Quoted 31 lines · Johannes Schindelin
Hi Junio,
Quoted 20 lines · Junio C Hamano
Quoted 4 lines · Denton Liu
There are many += lists in the
Makefileand, over time, they have gotten slightly out of order, alphabetically. Alphabetically sort all += lists to bring them back in order.
...Hmm. I like the general thrust, but ...
Makefile LIB_OBJS += checkout.o · line 851
Quoted 5 lines
LIB_OBJS+= combine-diff.oMakefile +1
-1-LIB_OBJS += commit.o LIB_OBJS += commit-graph.o LIB_OBJS += commit-reach.o +LIB_OBJS += commit.o... I do not particularly see this change (there may be similar ones) desirable. I'd find it it be much more natural to sort "commit-anything" after "commit", and that is true with or without the common extension ".o" added to these entries.
Quoted 2 lines
In short, flipping these entries because '.' sorts later than '-' is making the result look "less sorted", at least to me.
The problem with this argument is that it disagrees with ASCII, as
-has code0x2dwhile.has code0x2e, i.e. it is lexicographically _larger_.
Quoted 6 lines
Quoted 1 line
So Denton's patch does the correct thing.
I actually agree with Junio on this one. Without the prefixes, "commit" would go before "commit-graph" so I think it would make more sense to order with the prefixes removed instead of taking the naive ordering by just sorting each block.
That will make it harder on other contributors like me, who prefer to mark the lines in vim and then call :sort on them, and then not care about it any further.
Any decision that makes automating tedious tasks harder puts more burden on human beings. I don't like that.
Ciao,
Dscho
Quoted 9 lines
Thanks,
Denton
Quoted 3 lines
Ciao,
Dscho
Re: [RFC PATCH 7/7] merge: teach --autostash option
In reply to Phillip WoodHi Phillip,
Quoted 10 lines · Phillip Wood
Hi Denton
Quoted 3 lines · Denton Liu
In rebase, one can pass the
--autostashoption 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 pullin the same way as one might before a rebase but what's the motivation for doing it before a normal merge?
Will do.
Quoted 93 lines
Quoted 84 lines
Implement the
--autostashoption and correspondingmerge.autoStashoption in merge which stashes before merging and then pops after.Reported-by Alban Gruin <[email protected]>Signed-off-by Denton Liu <[email protected]>--- builtin/merge.c | 13 +++++++++++++ builtin/pull.c | 9 +++++---- t/t5520-pull.sh | 8 -------- 3 files changed, 18 insertions(+), 12 deletions(-)builtin/merge.c +13
-0@@ -40,6 +40,7 @@#include "branch.h" #include "commit-reach.h" #include "wt-status.h" +#include "autostash.h" #define DEFAULT_TWOHEAD (1<<0)#define DEFAULT_OCTOPUS (1<<1)@@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {NULL }; +static GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH") +static int show_diffstat =1 , shortlog_len =-1 , squash;static int option_commit =-1 ;static int option_edit =-1 ;@@ -81,6 +84,7 @@ static int show_progress = -1;static int default_to_upstream =1 ;static int signoff;static const char *sign_commit; +static int autostash;static int no_verify;static struct strategy all_strategy[] = {@@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {OPT_SET_INT (0 ,"progress" , &show_progress,N_ ("force progress reporting" ),1 ), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, +OPT_BOOL (0 ,"autostash" , &autostash, +N_ ("automatically stash/stash pop before and after" )),OPT_BOOL (0 ,"overwrite-ignore" , &overwrite_ignore,N_ ("update ignored files (default)" )),OPT_BOOL (0 ,"signoff" , &signoff,N_ ("add Signed-off-by:" )), OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),@@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,strbuf_addf (&reflog_message,"%s: %s" ,getenv ("GIT_REFLOG_ACTION" ), msg); } +apply_autostash (merge_autostash ());if (squash) {squash_message (head_commit, remoteheads); }else {@@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb) }else if (!strcmp (k,"commit.gpgsign" )) { sign_commit =git_config_bool (k, v) ?"" :NULL ;return 0 ; + }else if (!strcmp (k,"merge.autostash" )) { + autostash =git_config_bool (k, v); +return 0 ; } status =fmt_merge_msg_config (k, v, cb);@@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,for (j = common; j; j = j->next)commit_list_insert (j->item, &reversed); +if (autostash) +perform_autostash (merge_autostash ());hold_locked_index (&lock,LOCK_DIE_ON_ERROR ); clean =merge_recursive (&o, head, remoteheads->item, reversed, &result);@@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)/* Invoke 'git reset --merge' */ ret =cmd_reset (nargc, nargv, prefix); +apply_autostash (merge_autostash ()); goto done; }builtin/pull.c +0
-0builtin/pull.c static struct option pull_options[] = { · line 186
Quoted 5 lines
builtin/pull.c +1
-1@@ -183,7 +183,7 @@ static struct option pull_options[] = { N_("verify that the named commit has a valid GPG signature"), PARSE_OPT_NOARG),OPT_BOOL (0 ,"autostash" , &opt_autostash, - N_("automatically stash/stash pop before and after rebase")), + N_("automatically stash/stash pop before and after")),I've not looked closely at the code in this patch but noticed this. I think it would read better if it said
Quoted 1 line
automatically stash/stash pop before and after pulling
Since this is an argh argument, it says that we should "keep it homogenous across the repository". Since rebase (which I copied it from), merge and pull will be sharing the same argh string, I think that we should leave it as is.
Thanks,
Denton
Quoted 46 lines
Best Wishes
Phillip
Quoted 41 lines
builtin/pull.c +4
-3OPT_PASSTHRU_ARGV ('s' ,"strategy" , &opt_strategies,N_ ("strategy" ),N_ ("merge strategy to use" ), 0),@@ -671,6 +671,10 @@ static int run_merge(void)argv_array_pushv (&args, opt_strategy_opts.argv);if (opt_gpg_sign)argv_array_push (&args, opt_gpg_sign); +if (opt_autostash ==0 ) +argv_array_push (&args,"--no-autostash" ); +else if (opt_autostash ==1 ) +argv_array_push (&args,"--autostash" );if (opt_allow_unrelated_histories >0 )argv_array_push (&args,"--allow-unrelated-histories" );@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)if (get_oid ("HEAD" , &orig_head))oidclr (&orig_head); -if (!opt_rebase && opt_autostash !=-1 ) -die (_ ("--[no-]autostash option is only valid with --rebase." )); - autostash = config_autostash;if (opt_rebase) {if (opt_autostash !=-1 )t/t5520-pull.sh +0
-8@@ -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 &&
Re: [RFC PATCH 7/7] merge: teach --autostash option
In reply to Johannes SchindelinHi Dscho,
Quoted 8 lines · Johannes Schindelin
Hi Denton,
Quoted 4 lines · Denton Liu
In rebase, one can pass the
--autostashoption to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.
Quoted 7 lines
Quoted 4 lines
Implement the
--autostashoption and correspondingmerge.autoStashoption in merge which stashes before merging and then pops after.Reported-by Alban Gruin <[email protected]>Maybe "Suggested-by" would be more accurate, it is not like this feature request was a bug report...
Makes sense.
Quoted 45 lines
Quoted 45 lines
Signed-off-by Denton Liu <[email protected]>--- builtin/merge.c | 13 +++++++++++++ builtin/pull.c | 9 +++++---- t/t5520-pull.sh | 8 -------- 3 files changed, 18 insertions(+), 12 deletions(-)builtin/merge.c +6
-0@@ -40,6 +40,7 @@#include "branch.h" #include "commit-reach.h" #include "wt-status.h" +#include "autostash.h" #define DEFAULT_TWOHEAD (1<<0)#define DEFAULT_OCTOPUS (1<<1)@@ -58,6 +59,8 @@ static const char * const builtin_merge_usage[] = {NULL }; +static GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH") +static int show_diffstat =1 , shortlog_len =-1 , squash;static int option_commit =-1 ;static int option_edit =-1 ;@@ -81,6 +84,7 @@ static int show_progress = -1;static int default_to_upstream =1 ;static int signoff;static const char *sign_commit; +static int autostash;static int no_verify;static struct strategy all_strategy[] = {@@ -285,6 +289,8 @@ static struct option builtin_merge_options[] = {OPT_SET_INT (0 ,"progress" , &show_progress,N_ ("force progress reporting" ),1 ), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, +OPT_BOOL (0 ,"autostash" , &autostash, +N_ ("automatically stash/stash pop before and after" )),OPT_BOOL (0 ,"overwrite-ignore" , &overwrite_ignore,N_ ("update ignored files (default)" )), OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
Quoted 8 lines
builtin/merge.c static void finish(struct commit *head_commit, · line 449
Quoted 6 lines
builtin/merge.c +1
-0OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),@@ -440,6 +446,7 @@ static void finish(struct commit *head_commit,strbuf_addf (&reflog_message,"%s: %s" ,getenv ("GIT_REFLOG_ACTION" ), msg); } +apply_autostash (merge_autostash ());Should this not be guarded by
if (autostash)?
The apply_autostash() checks if an autostash file exists and then applies it if it does. As a result, we should be unconditionally calling apply_autostash().
I guess that an odd edge-case that might come up is that a user may run into a merge conflict, resolve the conflict, create a .git/MERGE_AUTOSTASH file and then, after they commit, the autostash file will be applied. Currently, rebase behaves the same way so I think it's safe to leave the behaviour as is. I don't think this is a common use-case that will come up anyway ;)
Quoted 61 lines
Quoted 24 lines
builtin/merge.c +5
-0if (squash) {squash_message (head_commit, remoteheads); }else {@@ -631,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb) }else if (!strcmp (k,"commit.gpgsign" )) { sign_commit =git_config_bool (k, v) ?"" :NULL ;return 0 ; + }else if (!strcmp (k,"merge.autostash" )) { + autostash =git_config_bool (k, v); +return 0 ; } status =fmt_merge_msg_config (k, v, cb);@@ -724,6 +734,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,for (j = common; j; j = j->next)commit_list_insert (j->item, &reversed); +if (autostash) +perform_autostash (merge_autostash ());hold_locked_index (&lock,LOCK_DIE_ON_ERROR ); clean =merge_recursive (&o, head, remoteheads->item, reversed, &result);builtin/merge.c int cmd_merge(int argc, const char **argv, const char *prefix) · line 1303
Quoted 4 lines
builtin/merge.c +1
-0@@ -1288,6 +1300,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)/* Invoke 'git reset --merge' */ ret =cmd_reset (nargc, nargv, prefix); +apply_autostash (merge_autostash ());Again, this should be guarded by
if (autostash), methinks.builtin/pull.c static struct option pull_options[] = { · line 186
Quoted 13 lines
builtin/merge.c +0
-0goto done; }builtin/pull.c +1
-1@@ -183,7 +183,7 @@ static struct option pull_options[] = { N_("verify that the named commit has a valid GPG signature"), PARSE_OPT_NOARG),OPT_BOOL (0 ,"autostash" , &opt_autostash, - N_("automatically stash/stash pop before and after rebase")), + N_("automatically stash/stash pop before and after")),Makes sense; this is now shared between the rebase and the merge modes.
builtin/pull.c static int run_merge(void) · line 677
Quoted 11 lines
builtin/pull.c +4
-0OPT_PASSTHRU_ARGV ('s' ,"strategy" , &opt_strategies,N_ ("strategy" ),N_ ("merge strategy to use" ), 0),@@ -671,6 +671,10 @@ static int run_merge(void)argv_array_pushv (&args, opt_strategy_opts.argv);if (opt_gpg_sign)argv_array_push (&args, opt_gpg_sign); +if (opt_autostash ==0 ) +argv_array_push (&args,"--no-autostash" ); +else if (opt_autostash ==1 ) +argv_array_push (&args,"--autostash" );Or shorter:
Quoted 6 lines
argv_array_pushf(&args, "%s-autostash", opt_autostash ? "-" : "--no");Ah, but that would mishandle
-1. I bet I will be puzzled by this again. Maybe it would make sense to mention in a code comment that it can be-1in which case we leave it torebaseto use the config settings to determine whether or not to autostash.
I copied this over from the rebase case. I'll add a comment there as well.
Quoted 22 lines
Quoted 22 lines
builtin/pull.c +0
-3if (opt_allow_unrelated_histories >0 )argv_array_push (&args,"--allow-unrelated-histories" );@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)if (get_oid ("HEAD" , &orig_head))oidclr (&orig_head); -if (!opt_rebase && opt_autostash !=-1 ) -die (_ ("--[no-]autostash option is only valid with --rebase." )); - autostash = config_autostash;if (opt_rebase) {if (opt_autostash !=-1 )t/t5520-pull.sh +0
-0@@ -365,14 +365,6 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' ' test_pull_autostash_fail --rebase --no-autostash '
Quoted 11 lines
t/t5520-pull.sh test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' ' · line 375
Quoted 8 lines
t/t5520-pull.sh +0
-8-for i in --autostash --no-autostash -do - test_expect_success "pull $i (without --rebase) is illegal" ' - test_must_fail git pull $i . copy 2>err && - test_i18ngrep "only valid with --rebase" err - ' -done -Nice!
Dscho
Thanks,
Denton
Quoted 8 lines
Quoted 8 lines
t/t5520-pull.sh +0
-1test_expect_success 'pull.rebase' ' git reset --hard before-rebase && test_config pull.rebase true && --2.23.0.897.g0a19638b1e
Re: [RFC PATCH 7/7] merge: teach --autostash option
Quoted 16 lines · Denton Liu
Quoted 16 lines
builtin/pull.c static int run_merge(void) · line 677
Quoted 11 lines
builtin/pull.c +4
-0OPT_PASSTHRU_ARGV ('s' ,"strategy" , &opt_strategies,N_ ("strategy" ),N_ ("merge strategy to use" ), 0),@@ -671,6 +671,10 @@ static int run_merge(void)argv_array_pushv (&args, opt_strategy_opts.argv);if (opt_gpg_sign)argv_array_push (&args, opt_gpg_sign); +if (opt_autostash ==0 ) +argv_array_push (&args,"--no-autostash" ); +else if (opt_autostash ==1 ) +argv_array_push (&args,"--autostash" );Or shorter:
argv_array_pushf(&args, "%s-autostash", opt_autostash ? "-" : "--no");
Quoted 7 lines
Quoted 4 lines
Ah, but that would mishandle
-1. I bet I will be puzzled by this again. Maybe it would make sense to mention in a code comment that it can be-1in which case we leave it torebaseto 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.
v2 cover merge: learn --autostash
In reply to Denton LiuAlban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.
The majority of this patchset is spent refactoring. This is so that the actual implementation in merge is done as simply as possible.
Changes since v1:
* Completely changed how the refactoring was done
* More tests and documentation
[1]: https://github.com/gitgitgadget/git/issues/394
Denton Liu (17):
Makefile: alphabetically sort += lists
t7600: use test_write_lines()
sequencer: use file strbuf for read_oneliner()
sequencer: configurably warn on non-existent files
sequencer: make read_oneliner() extern
rebase: use read_oneliner()
sequencer: make apply_rebase() accept a path
rebase: use apply_autostash() from sequencer.c
rebase: generify reset_head()
reset: extract reset_head() from rebase
rebase: extract create_autostash()
rebase: generify create_autostash()
sequencer: extract perform_autostash() from rebase
sequencer: unlink autostash in apply_autostash()
merge: teach --autostash option
t5520: make test_pull_autostash() accept expect_parent_num
pull: pass --autostash to merge
Documentation/config/merge.txt | 10 ++ Documentation/git-pull.txt | 9 - Documentation/merge-options.txt | 8 + Makefile | 76 ++++---- branch.c | 1 + builtin/commit.c | 2 + builtin/merge.c | 17 ++ builtin/pull.c | 9 +- builtin/rebase.c | 295 ++++---------------------------- path.c | 1 + path.h | 4 +- reset.c | 140 +++++++++++++++ reset.h | 20 +++ sequencer.c | 128 +++++++++----- sequencer.h | 15 ++ t/t3033-merge-toplevel.sh | 22 +++ t/t5520-pull.sh | 57 ++++-- t/t7600-merge.sh | 87 ++++++++-- 18 files changed, 522 insertions(+), 379 deletions(-) create mode 100644 reset.c create mode 100644 reset.h
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 1/17 Makefile: alphabetically sort += lists
There are many += lists in the Makefile and, over time, they have gotten slightly out of order, alphabetically. Alphabetically sort all += lists to bring them back in order.
Note that if we omit file prefixes, the lists aren't sorted in strictly alphabetical order (e.g. archive.o comes after archive-zip.o instead of before archive-tar.o). This is intentional because the purpose of maintaining the sorted list is to ensure line insertions are deterministic. As a result, we want to ensure that anybody can easily reproduce the sort order and that is very simple when we don't have to treat the prefix specially.
This patch is best viewed with --color-moved.
--- Makefile | 75 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 37 deletions(-)
Makefile +38 -37
@@ -604,12 +604,12 @@ unexport CDPATH SCRIPT_SH += git-bisect.sh SCRIPT_SH += git-difftool--helper.sh SCRIPT_SH += git-filter-branch.sh +SCRIPT_SH += git-legacy-stash.sh SCRIPT_SH += git-merge-octopus.sh SCRIPT_SH += git-merge-one-file.sh SCRIPT_SH += git-merge-resolve.sh SCRIPT_SH += git-mergetool.sh SCRIPT_SH += git-quiltimport.sh -SCRIPT_SH += git-legacy-stash.sh SCRIPT_SH += git-request-pull.sh SCRIPT_SH += git-submodule.sh SCRIPT_SH += git-web--browse.sh
@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh SCRIPT_LIB += git-mergetool--lib SCRIPT_LIB += git-parse-remote SCRIPT_LIB += git-rebase--preserve-merges -SCRIPT_LIB += git-sh-setup SCRIPT_LIB += git-sh-i18n +SCRIPT_LIB += git-sh-setup SCRIPT_PERL += git-add--interactive.perl SCRIPT_PERL += git-archimport.perl
@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o PROGRAM_OBJS += fast-import.o PROGRAM_OBJS += http-backend.o PROGRAM_OBJS += imap-send.o +PROGRAM_OBJS += remote-testsvn.o PROGRAM_OBJS += sh-i18n--envsubst.o PROGRAM_OBJS += shell.o -PROGRAM_OBJS += remote-testsvn.o # Binary suffix, set to .exe for Windows builds X =
@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o TEST_BUILTINS_OBJS += test-example-decorate.o TEST_BUILTINS_OBJS += test-genrandom.o TEST_BUILTINS_OBJS += test-genzeros.o +TEST_BUILTINS_OBJS += test-hash-speed.o TEST_BUILTINS_OBJS += test-hash.o TEST_BUILTINS_OBJS += test-hashmap.o -TEST_BUILTINS_OBJS += test-hash-speed.o TEST_BUILTINS_OBJS += test-index-version.o TEST_BUILTINS_OBJS += test-json-writer.o TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@ -736,8 +736,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o TEST_BUILTINS_OBJS += test-run-command.o TEST_BUILTINS_OBJS += test-scrap-cache-tree.o TEST_BUILTINS_OBJS += test-serve-v2.o -TEST_BUILTINS_OBJS += test-sha1.o TEST_BUILTINS_OBJS += test-sha1-array.o +TEST_BUILTINS_OBJS += test-sha1.o TEST_BUILTINS_OBJS += test-sha256.o TEST_BUILTINS_OBJS += test-sigchain.o TEST_BUILTINS_OBJS += test-strcmp-offset.o
@@ -747,10 +747,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o TEST_BUILTINS_OBJS += test-subprocess.o TEST_BUILTINS_OBJS += test-trace2.o TEST_BUILTINS_OBJS += test-urlmatch-normalization.o -TEST_BUILTINS_OBJS += test-xml-encode.o TEST_BUILTINS_OBJS += test-wildmatch.o TEST_BUILTINS_OBJS += test-windows-named-pipe.o TEST_BUILTINS_OBJS += test-write-cache.o +TEST_BUILTINS_OBJS += test-xml-encode.o # Do not add more tests here unless they have extra dependencies. Add # them in TEST_BUILTINS_OBJS above.
@@ -787,10 +787,10 @@ OTHER_PROGRAMS = git$X # what test wrappers are needed and 'install' will install, in bindir BINDIR_PROGRAMS_NEED_X += git -BINDIR_PROGRAMS_NEED_X += git-upload-pack BINDIR_PROGRAMS_NEED_X += git-receive-pack -BINDIR_PROGRAMS_NEED_X += git-upload-archive BINDIR_PROGRAMS_NEED_X += git-shell +BINDIR_PROGRAMS_NEED_X += git-upload-archive +BINDIR_PROGRAMS_NEED_X += git-upload-pack BINDIR_PROGRAMS_NO_X += git-cvsserver
@@ -829,9 +829,9 @@ LIB_OBJS += advice.o LIB_OBJS += alias.o LIB_OBJS += alloc.o LIB_OBJS += apply.o -LIB_OBJS += archive.o LIB_OBJS += archive-tar.o LIB_OBJS += archive-zip.o +LIB_OBJS += archive.o LIB_OBJS += argv-array.o LIB_OBJS += attr.o LIB_OBJS += base85.o
@@ -847,9 +847,9 @@ LIB_OBJS += checkout.o LIB_OBJS += color.o LIB_OBJS += column.o LIB_OBJS += combine-diff.o -LIB_OBJS += commit.o LIB_OBJS += commit-graph.o LIB_OBJS += commit-reach.o +LIB_OBJS += commit.o LIB_OBJS += compat/obstack.o LIB_OBJS += compat/terminal.o LIB_OBJS += config.o
@@ -863,17 +863,17 @@ LIB_OBJS += ctype.o LIB_OBJS += date.o LIB_OBJS += decorate.o LIB_OBJS += delta-islands.o +LIB_OBJS += diff-delta.o +LIB_OBJS += diff-lib.o +LIB_OBJS += diff-no-index.o +LIB_OBJS += diff.o LIB_OBJS += diffcore-break.o LIB_OBJS += diffcore-delta.o LIB_OBJS += diffcore-order.o LIB_OBJS += diffcore-pickaxe.o LIB_OBJS += diffcore-rename.o -LIB_OBJS += diff-delta.o -LIB_OBJS += diff-lib.o -LIB_OBJS += diff-no-index.o -LIB_OBJS += diff.o -LIB_OBJS += dir.o LIB_OBJS += dir-iterator.o +LIB_OBJS += dir.o LIB_OBJS += editor.o LIB_OBJS += entry.o LIB_OBJS += environment.o
@@ -891,7 +891,6 @@ LIB_OBJS += gpg-interface.o LIB_OBJS += graph.o LIB_OBJS += grep.o LIB_OBJS += hashmap.o -LIB_OBJS += linear-assignment.o LIB_OBJS += help.o LIB_OBJS += hex.o LIB_OBJS += ident.o
@@ -901,9 +900,10 @@ LIB_OBJS += kwset.o LIB_OBJS += levenshtein.o LIB_OBJS += line-log.o LIB_OBJS += line-range.o -LIB_OBJS += list-objects.o -LIB_OBJS += list-objects-filter.o +LIB_OBJS += linear-assignment.o LIB_OBJS += list-objects-filter-options.o +LIB_OBJS += list-objects-filter.o +LIB_OBJS += list-objects.o LIB_OBJS += ll-merge.o LIB_OBJS += lockfile.o LIB_OBJS += log-tree.o
@@ -912,31 +912,31 @@ LIB_OBJS += mailinfo.o LIB_OBJS += mailmap.o LIB_OBJS += match-trees.o LIB_OBJS += mem-pool.o -LIB_OBJS += merge.o LIB_OBJS += merge-blobs.o LIB_OBJS += merge-recursive.o +LIB_OBJS += merge.o LIB_OBJS += mergesort.o LIB_OBJS += midx.o LIB_OBJS += name-hash.o LIB_OBJS += negotiator/default.o LIB_OBJS += negotiator/skipping.o -LIB_OBJS += notes.o LIB_OBJS += notes-cache.o LIB_OBJS += notes-merge.o LIB_OBJS += notes-utils.o +LIB_OBJS += notes.o LIB_OBJS += object.o LIB_OBJS += oidmap.o LIB_OBJS += oidset.o -LIB_OBJS += packfile.o -LIB_OBJS += pack-bitmap.o LIB_OBJS += pack-bitmap-write.o +LIB_OBJS += pack-bitmap.o LIB_OBJS += pack-check.o LIB_OBJS += pack-objects.o LIB_OBJS += pack-revindex.o LIB_OBJS += pack-write.o +LIB_OBJS += packfile.o LIB_OBJS += pager.o -LIB_OBJS += parse-options.o LIB_OBJS += parse-options-cb.o +LIB_OBJS += parse-options.o LIB_OBJS += patch-delta.o LIB_OBJS += patch-ids.o LIB_OBJS += path.o
@@ -954,6 +954,7 @@ LIB_OBJS += range-diff.o LIB_OBJS += reachable.o LIB_OBJS += read-cache.o LIB_OBJS += rebase-interactive.o +LIB_OBJS += ref-filter.o LIB_OBJS += reflog-walk.o LIB_OBJS += refs.o LIB_OBJS += refs/files-backend.o
@@ -961,7 +962,6 @@ LIB_OBJS += refs/iterator.o LIB_OBJS += refs/packed-backend.o LIB_OBJS += refs/ref-cache.o LIB_OBJS += refspec.o -LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace-object.o LIB_OBJS += repo-settings.o
@@ -976,8 +976,8 @@ LIB_OBJS += serve.o LIB_OBJS += server-info.o LIB_OBJS += setup.o LIB_OBJS += sha1-array.o -LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1-file.o +LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1-name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o
@@ -987,9 +987,9 @@ LIB_OBJS += stable-qsort.o LIB_OBJS += strbuf.o LIB_OBJS += streaming.o LIB_OBJS += string-list.o -LIB_OBJS += submodule.o -LIB_OBJS += submodule-config.o LIB_OBJS += sub-process.o +LIB_OBJS += submodule-config.o +LIB_OBJS += submodule.o LIB_OBJS += symlinks.o LIB_OBJS += tag.o LIB_OBJS += tempfile.o
@@ -1008,11 +1008,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o LIB_OBJS += trace2/tr2_tgt_perf.o LIB_OBJS += trace2/tr2_tls.o LIB_OBJS += trailer.o -LIB_OBJS += transport.o LIB_OBJS += transport-helper.o +LIB_OBJS += transport.o LIB_OBJS += tree-diff.o -LIB_OBJS += tree.o LIB_OBJS += tree-walk.o +LIB_OBJS += tree.o LIB_OBJS += unpack-trees.o LIB_OBJS += upload-pack.o LIB_OBJS += url.o
@@ -1052,9 +1052,9 @@ BUILTIN_OBJS += builtin/checkout.o BUILTIN_OBJS += builtin/clean.o BUILTIN_OBJS += builtin/clone.o BUILTIN_OBJS += builtin/column.o +BUILTIN_OBJS += builtin/commit-graph.o BUILTIN_OBJS += builtin/commit-tree.o BUILTIN_OBJS += builtin/commit.o -BUILTIN_OBJS += builtin/commit-graph.o BUILTIN_OBJS += builtin/config.o BUILTIN_OBJS += builtin/count-objects.o BUILTIN_OBJS += builtin/credential.o
@@ -1085,13 +1085,13 @@ BUILTIN_OBJS += builtin/ls-remote.o BUILTIN_OBJS += builtin/ls-tree.o BUILTIN_OBJS += builtin/mailinfo.o BUILTIN_OBJS += builtin/mailsplit.o -BUILTIN_OBJS += builtin/merge.o BUILTIN_OBJS += builtin/merge-base.o BUILTIN_OBJS += builtin/merge-file.o BUILTIN_OBJS += builtin/merge-index.o BUILTIN_OBJS += builtin/merge-ours.o BUILTIN_OBJS += builtin/merge-recursive.o BUILTIN_OBJS += builtin/merge-tree.o +BUILTIN_OBJS += builtin/merge.o BUILTIN_OBJS += builtin/mktag.o BUILTIN_OBJS += builtin/mktree.o BUILTIN_OBJS += builtin/multi-pack-index.o
@@ -1111,9 +1111,9 @@ BUILTIN_OBJS += builtin/read-tree.o BUILTIN_OBJS += builtin/rebase.o BUILTIN_OBJS += builtin/receive-pack.o BUILTIN_OBJS += builtin/reflog.o -BUILTIN_OBJS += builtin/remote.o BUILTIN_OBJS += builtin/remote-ext.o BUILTIN_OBJS += builtin/remote-fd.o +BUILTIN_OBJS += builtin/remote.o BUILTIN_OBJS += builtin/repack.o BUILTIN_OBJS += builtin/replace.o BUILTIN_OBJS += builtin/rerere.o
@@ -2328,16 +2328,16 @@ reconfigure config.mak.autogen: config.status endif XDIFF_OBJS += xdiff/xdiffi.o -XDIFF_OBJS += xdiff/xprepare.o -XDIFF_OBJS += xdiff/xutils.o XDIFF_OBJS += xdiff/xemit.o +XDIFF_OBJS += xdiff/xhistogram.o XDIFF_OBJS += xdiff/xmerge.o XDIFF_OBJS += xdiff/xpatience.o -XDIFF_OBJS += xdiff/xhistogram.o +XDIFF_OBJS += xdiff/xprepare.o +XDIFF_OBJS += xdiff/xutils.o +VCSSVN_OBJS += vcs-svn/fast_export.o VCSSVN_OBJS += vcs-svn/line_buffer.o VCSSVN_OBJS += vcs-svn/sliding_window.o -VCSSVN_OBJS += vcs-svn/fast_export.o VCSSVN_OBJS += vcs-svn/svndiff.o VCSSVN_OBJS += vcs-svn/svndump.o
@@ -3146,9 +3146,10 @@ endif # ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) ALL_COMMANDS += git +ALL_COMMANDS += git-citool +ALL_COMMANDS += git-gui ALL_COMMANDS += gitk ALL_COMMANDS += gitweb -ALL_COMMANDS += git-gui git-citool .PHONY: check-docs check-docs::
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 2/17 t7600: use test_write_lines()
In reply to Denton LiuIn t7600, we were rewriting printf '%s\n' ... to create files from parameters, one per line. However, we already have a function that wraps this for us: test_write_lines(). Rewrite these instances to use that function instead of open coding it.
--- t/t7600-merge.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-)
t/t7600-merge.sh +11 -11
@@ -29,15 +29,15 @@ Testing basic merge operations/option parsing. . ./test-lib.sh . "$TEST_DIRECTORY"/lib-gpg.sh -printf '%s\n' 1 2 3 4 5 6 7 8 9 >file -printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >file.1 -printf '%s\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5 -printf '%s\n' 1 2 3 4 5 6 7 8 '9 X' >file.9 -printf '%s\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y -printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >result.1 -printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5 -printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9 -printf '%s\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z +test_write_lines 1 2 3 4 5 6 7 8 9 >file +test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1 +test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5 +test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9 +test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y +test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1 +test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5 +test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9 +test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z create_merge_msgs () { echo "Merge tag 'c2'" >msg.1-5 &&
@@ -81,7 +81,7 @@ verify_head () { } verify_parents () { - printf '%s\n' "$@" >parents.expected && + test_write_lines "$@" >parents.expected && >parents.actual && i=1 && while test $i -le $#
@@ -95,7 +95,7 @@ verify_parents () { } verify_mergeheads () { - printf '%s\n' "$@" >mergehead.expected && + test_write_lines "$@" >mergehead.expected && while read sha1 rest do git rev-parse $sha1
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 3/17 sequencer: use file strbuf for read_oneliner()
In reply to Denton LiuIn the original read_oneliner logic, we duplicated the logic for strbuf_trim_trailing_newline() with one exception: instead of preventing buffer accesses below index 0, it would prevent buffer accesses below index orig_len. Although this is correct, it isn't worth having the duplicated logic around.
Add a second strbuf to which files are read and run strbuf_trim_trailing_newline() directly on this strbuf then concatenate this strbuf with the argument strbuf at the end of the function. The function's external behaviour is unchanged.
--- sequencer.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-)
sequencer.c +13 -11
@@ -429,26 +429,28 @@ static int write_message(const void *buf, size_t len, const char *filename,static int read_oneliner (struct strbuf *buf,const char *path,int skip_if_empty) { -int orig_len = buf->len; +int ret =0 ; +struct strbuf file_buf =STRBUF_INIT ;if (!file_exists (path))return 0 ; -if (strbuf_read_file (buf, path,0 ) <0 ) { +if (strbuf_read_file (&file_buf, path,0 ) <0 ) {warning_errno (_ ("could not read '%s'" ), path); -return 0 ; + goto done; } -if (buf->len > orig_len && buf->buf[buf->len -1 ] =='\n' ) { -if (--buf->len > orig_len && buf->buf[buf->len -1 ] =='\r' ) - --buf->len; - buf->buf[buf->len] ='\0' ; - } +strbuf_trim_trailing_newline (&file_buf); -if (skip_if_empty && buf->len == orig_len) -return 0 ; +if (skip_if_empty && !file_buf.len) + goto done; -return 1 ; +strbuf_addbuf (buf, &file_buf); + ret =1 ; + +done: +strbuf_release (&file_buf); +return ret; }static struct tree *empty_tree (struct repository *r)
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 4/17 sequencer: configurably warn on non-existent files
In reply to Denton LiuIn the future, we plan on externing read_oneliner(). Future users of read_oneliner() will want the ability to output warnings in the event that the path doesn't exist. Introduce the warn_nonexistence parameter which, if true, would issue a warning when a file doesn't exist by skipping the !file_exists() check and letting strbuf_read_file() handle that case.
Mechanically replace uses of read_oneliner() in sequencer.c with the following spatch so that current behaviour is maintained:
@@ expression a, b, c; @@ read_oneliner(a, b, c + , 0 )
--- sequencer.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
sequencer.c +16 -16
@@ -426,13 +426,13 @@ static int write_message(const void *buf, size_t len, const char *filename,* * Returns 1 if the file was read, 0 if it could not be read or does not exist. */ -static int read_oneliner (struct strbuf *buf, -const char *path,int skip_if_empty) +static int read_oneliner (struct strbuf *buf,const char *path, +int skip_if_empty,int warn_nonexistence) {int ret =0 ;struct strbuf file_buf =STRBUF_INIT ; -if (!file_exists (path)) +if (!warn_nonexistence && !file_exists (path))return 0 ;if (strbuf_read_file (&file_buf, path,0 ) <0 ) {
@@ -2558,10 +2558,10 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)static void read_strategy_opts (struct replay_opts *opts,struct strbuf *buf) {strbuf_reset (buf); -if (!read_oneliner (buf,rebase_path_strategy (),0 )) +if (!read_oneliner (buf,rebase_path_strategy (),0 ,0 ))return ; opts->strategy =strbuf_detach (buf,NULL ); -if (!read_oneliner (buf,rebase_path_strategy_opts (),0 )) +if (!read_oneliner (buf,rebase_path_strategy_opts (),0 ,0 ))return ;parse_strategy_opts (opts, buf->buf);
@@ -2572,7 +2572,7 @@ static int read_populate_opts(struct replay_opts *opts)if (is_rebase_i (opts)) {struct strbuf buf =STRBUF_INIT ; -if (read_oneliner (&buf,rebase_path_gpg_sign_opt (),1 )) { +if (read_oneliner (&buf,rebase_path_gpg_sign_opt (),1 ,0 )) {if (!starts_with (buf.buf,"-S" ))strbuf_reset (&buf);else {
@@ -2582,7 +2582,7 @@ static int read_populate_opts(struct replay_opts *opts)strbuf_reset (&buf); } -if (read_oneliner (&buf,rebase_path_allow_rerere_autoupdate (),1 )) { +if (read_oneliner (&buf,rebase_path_allow_rerere_autoupdate (),1 ,0 )) {if (!strcmp (buf.buf,"--rerere-autoupdate" )) opts->allow_rerere_auto =RERERE_AUTOUPDATE ;else if (!strcmp (buf.buf,"--no-rerere-autoupdate" ))
@@ -2618,7 +2618,7 @@ static int read_populate_opts(struct replay_opts *opts)strbuf_release (&buf);if (read_oneliner (&opts->current_fixups, -rebase_path_current_fixups (),1 )) { +rebase_path_current_fixups (),1 ,0 )) {const char *p = opts->current_fixups.buf; opts->current_fixup_count =1 ;while ((p =strchr (p,'\n' ))) {
@@ -2627,7 +2627,7 @@ static int read_populate_opts(struct replay_opts *opts) } } -if (read_oneliner (&buf,rebase_path_squash_onto (),0 )) { +if (read_oneliner (&buf,rebase_path_squash_onto (),0 ,0 )) {if (get_oid_hex (buf.buf, &opts->squash_onto) <0 )return error (_ ("unusable squash-onto" )); opts->have_squash_onto =1 ;
@@ -3759,7 +3759,7 @@ static int apply_autostash(struct replay_opts *opts)struct child_process child =CHILD_PROCESS_INIT ;int ret =0 ; -if (!read_oneliner (&stash_sha1,rebase_path_autostash (),1 )) { +if (!read_oneliner (&stash_sha1,rebase_path_autostash (),1 ,0 )) {strbuf_release (&stash_sha1);return 0 ; }
@@ -4093,7 +4093,7 @@ static int pick_commits(struct repository *r,if (todo_list->current < todo_list->nr)return 0 ; -if (read_oneliner (&head_ref,rebase_path_head_name (),0 ) && +if (read_oneliner (&head_ref,rebase_path_head_name (),0 ,0 ) &&starts_with (head_ref.buf,"refs/" )) {const char *msg;struct object_id head, orig;
@@ -4106,13 +4106,13 @@ static int pick_commits(struct repository *r,strbuf_release (&buf);return res; } -if (!read_oneliner (&buf,rebase_path_orig_head (),0 ) || +if (!read_oneliner (&buf,rebase_path_orig_head (),0 ,0 ) ||get_oid_hex (buf.buf, &orig)) { res =error (_ ("could not read orig-head" )); goto cleanup_head_ref; }strbuf_reset (&buf); -if (!read_oneliner (&buf,rebase_path_onto (),0 )) { +if (!read_oneliner (&buf,rebase_path_onto (),0 ,0 )) { res =error (_ ("could not read 'onto'" )); goto cleanup_head_ref; }
@@ -4145,7 +4145,7 @@ static int pick_commits(struct repository *r,DIFF_FORMAT_DIFFSTAT ; log_tree_opt.disable_stdin =1 ; -if (read_oneliner (&buf,rebase_path_orig_head (),0 ) && +if (read_oneliner (&buf,rebase_path_orig_head (),0 ,0 ) && !get_oid (buf.buf, &orig) && !get_oid ("HEAD" , &head)) { diff_tree_oid(&orig, &head, "",
@@ -4230,7 +4230,7 @@ static int commit_staged_changes(struct repository *r,if (get_oid ("HEAD" , &head))return error (_ ("cannot amend non-existing commit" )); -if (!read_oneliner (&rev,rebase_path_amend (),0 )) +if (!read_oneliner (&rev,rebase_path_amend (),0 ,0 ))return error (_ ("invalid file: '%s'" ),rebase_path_amend ());if (get_oid_hex (rev.buf, &to_amend)) return error(_("invalid contents: '%s'"),
@@ -4391,7 +4391,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)struct strbuf buf =STRBUF_INIT ;struct object_id oid; -if (read_oneliner (&buf,rebase_path_stopped_sha (),1 ) && +if (read_oneliner (&buf,rebase_path_stopped_sha (),1 ,0 ) && !get_oid_committish (buf.buf, &oid))record_in_rewritten (&oid,peek_command (&todo_list,0 ));strbuf_release (&buf);
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 6/17 rebase: use read_oneliner()
In reply to Denton LiuSince in sequencer.c, read_one() basically duplicates the functionality of read_oneliner(), reduce code duplication by replacing read_one() with read_oneliner().
This was done with the following Coccinelle script:
@@ expression a, b; @@ - read_one(a, b) + !read_oneliner(b, a, 0, 1)
--- builtin/rebase.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-)
builtin/rebase.c +9 -20
@@ -582,15 +582,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *oreturn path.buf; } -/* Read one file, then strip line endings */ -static int read_one (const char *path,struct strbuf *buf) -{ -if (strbuf_read_file (buf, path,0 ) <0 ) -return error_errno (_ ("could not read '%s'" ), path); -strbuf_trim_trailing_newline (buf); -return 0 ; -} -/* Initialize the rebase options from the state directory. */ static int read_basic_state (struct rebase_options *opts) {
@@ -598,8 +589,8 @@ static int read_basic_state(struct rebase_options *opts)struct strbuf buf =STRBUF_INIT ;struct object_id oid; -if (read_one (state_dir_path ("head-name" , opts), &head_name) || -read_one (state_dir_path ("onto" , opts), &buf)) +if (!read_oneliner (&head_name,state_dir_path ("head-name" , opts),0 ,1 ) || + !read_oneliner (&buf,state_dir_path ("onto" , opts),0 ,1 ))return -1 ; opts->head_name =starts_with (head_name.buf,"refs/" ) ?xstrdup (head_name.buf) :NULL ;
@@ -615,9 +606,9 @@ static int read_basic_state(struct rebase_options *opts)*/ strbuf_reset (&buf);if (file_exists (state_dir_path ("orig-head" , opts))) { -if (read_one (state_dir_path ("orig-head" , opts), &buf)) +if (!read_oneliner (&buf,state_dir_path ("orig-head" , opts),0 ,1 ))return -1 ; - }else if (read_one (state_dir_path ("head" , opts), &buf)) + }else if (!read_oneliner (&buf,state_dir_path ("head" , opts),0 ,1 ))return -1 ;if (get_oid (buf.buf, &opts->orig_head))return error (_ ("invalid orig-head: '%s'" ), buf.buf);
@@ -637,8 +628,7 @@ static int read_basic_state(struct rebase_options *opts)if (file_exists (state_dir_path ("allow_rerere_autoupdate" , opts))) {strbuf_reset (&buf); -if (read_one (state_dir_path ("allow_rerere_autoupdate" , opts), - &buf)) +if (!read_oneliner (&buf,state_dir_path ("allow_rerere_autoupdate" , opts),0 ,1 ))return -1 ;if (!strcmp (buf.buf,"--rerere-autoupdate" )) opts->allow_rerere_autoupdate =RERERE_AUTOUPDATE ;
@@ -651,8 +641,7 @@ static int read_basic_state(struct rebase_options *opts)if (file_exists (state_dir_path ("gpg_sign_opt" , opts))) {strbuf_reset (&buf); -if (read_one (state_dir_path ("gpg_sign_opt" , opts), - &buf)) +if (!read_oneliner (&buf,state_dir_path ("gpg_sign_opt" , opts),0 ,1 ))return -1 ;free (opts->gpg_sign_opt); opts->gpg_sign_opt =xstrdup (buf.buf);
@@ -660,7 +649,7 @@ static int read_basic_state(struct rebase_options *opts)if (file_exists (state_dir_path ("strategy" , opts))) {strbuf_reset (&buf); -if (read_one (state_dir_path ("strategy" , opts), &buf)) +if (!read_oneliner (&buf,state_dir_path ("strategy" , opts),0 ,1 ))return -1 ;free (opts->strategy); opts->strategy =xstrdup (buf.buf);
@@ -668,7 +657,7 @@ static int read_basic_state(struct rebase_options *opts)if (file_exists (state_dir_path ("strategy_opts" , opts))) {strbuf_reset (&buf); -if (read_one (state_dir_path ("strategy_opts" , opts), &buf)) +if (!read_oneliner (&buf,state_dir_path ("strategy_opts" , opts),0 ,1 ))return -1 ;free (opts->strategy_opts); opts->strategy_opts =xstrdup (buf.buf);
@@ -720,7 +709,7 @@ static int apply_autostash(struct rebase_options *opts)if (!file_exists (path))return 0 ; -if (read_one (path, &autostash)) +if (!read_oneliner (&autostash, path,0 ,1 ))return error (_ ("Could not read '%s'" ), path);/* Ensure that the hash is not mistaken for a number */ strbuf_addstr (&autostash,"^0" );
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 7/17 sequencer: make apply_rebase() accept a path
In reply to Denton LiuIn order to make apply_rebase() more generic for future extraction, make it accept a path argument so that the location from where to read the reference to the autostash commit can be customized. Remove the opts argument since it was unused before anyway.
--- sequencer.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
sequencer.c +7 -7
@@ -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 flatodo_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 flaif (res ==-1 )return -1 ;else if (res ==-2 ) { -apply_autostash (opts); +apply_autostash (rebase_path_autostash ());sequencer_remove_state (opts);return -1 ; }else if (res ==-3 ) { -apply_autostash (opts); +apply_autostash (rebase_path_autostash ());sequencer_remove_state (opts);todo_list_release (&new_todo);
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 8/17 rebase: use apply_autostash() from sequencer.c
In reply to Denton LiuThe apply_autostash() function in builtin/rebase.c is similar enough to the apply_autostash() function in sequencer.c that they are almost interchangeable. Make the sequencer.c version extern and use it in rebase.
The rebase version was introduced in 6defce2b02 (builtin rebase: support --autostash option, 2018-09-04) as part of the shell to C conversion. It opted to duplicate the function because, at the time, there was another in-progress project converting interactive rebase from shell to C as well and they did not want to clash with them by refactoring sequencer.c version of apply_autostash(). Since both efforts are long done, we can freely combine them together now.
--- builtin/rebase.c | 49 ++---------------------------------------------- sequencer.c | 2 +- sequencer.h | 2 ++ 3 files changed, 5 insertions(+), 48 deletions(-)
builtin/rebase.c +2 -47
@@ -700,51 +700,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)return 0 ; } -static int apply_autostash (struct rebase_options *opts) -{ -const char *path =state_dir_path ("autostash" , opts); -struct strbuf autostash =STRBUF_INIT ; -struct child_process stash_apply =CHILD_PROCESS_INIT ; - -if (!file_exists (path)) -return 0 ; - -if (!read_oneliner (&autostash, path,0 ,1 )) -return error (_ ("Could not read '%s'" ), path); -/* Ensure that the hash is not mistaken for a number */ -strbuf_addstr (&autostash,"^0" ); -argv_array_pushl (&stash_apply.args, -"stash" ,"apply" , autostash.buf,NULL ); - stash_apply.git_cmd =1 ; - stash_apply.no_stderr = stash_apply.no_stdout = - stash_apply.no_stdin =1 ; -if (!run_command (&stash_apply)) -printf (_ ("Applied autostash.\n" )); -else { -struct argv_array args =ARGV_ARRAY_INIT ; -int res =0 ; - -argv_array_pushl (&args, -"stash" ,"store" ,"-m" ,"autostash" ,"-q" , - autostash.buf,NULL ); -if (run_command_v_opt (args.argv,RUN_GIT_CMD )) - res =error (_ ("Cannot store %s" ), autostash.buf); -argv_array_clear (&args); -strbuf_release (&autostash); -if (res) -return res; - -fprintf (stderr, -_ ("Applying autostash resulted in conflicts.\n" -"Your changes are safe in the stash.\n" -"You can run \"git stash pop\" or \"git stash drop\" " -"at any time.\n" )); - } - -strbuf_release (&autostash); -return 0 ; -} -static int finish_rebase (struct rebase_options *opts) {struct strbuf dir =STRBUF_INIT ;
@@ -752,7 +707,7 @@ static int finish_rebase(struct rebase_options *opts)int ret =0 ;delete_ref (NULL ,"REBASE_HEAD" ,NULL ,REF_NO_DEREF ); -apply_autostash (opts); +apply_autostash (state_dir_path ("autostash" , opts));close_object_store (the_repository->objects);/* * We ignore errors in 'gc --auto', since the
@@ -1206,7 +1161,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) }else if (status ==2 ) {struct strbuf dir =STRBUF_INIT ; -apply_autostash (opts); +apply_autostash (state_dir_path ("autostash" , opts));strbuf_addstr (&dir, opts->state_dir);remove_dir_recursively (&dir,0 );strbuf_release (&dir);
sequencer.c +1 -1
@@ -3744,7 +3744,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)return -1 ; } -static int apply_autostash (const char *path) +int apply_autostash (const char *path) {struct strbuf stash_sha1 =STRBUF_INIT ;struct child_process child =CHILD_PROCESS_INIT ;
sequencer.h +2 -0
@@ -195,6 +195,8 @@ void commit_post_rewrite(struct repository *r,int prepare_branch_to_be_rebased (struct repository *r,struct replay_opts *opts,const char *commit); +int apply_autostash (const char *path); +#define SUMMARY_INITIAL_COMMIT (1 << 0)#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1) void print_commit_summary(struct repository *repo,
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 5/17 sequencer: make read_oneliner() extern
In reply to Denton LiuThe function read_oneliner() is a generally useful util function. Instead of hiding it as a static function within sequencer.c, extern it so that it can be reused by others.
--- sequencer.c | 13 ++----------- sequencer.h | 11 +++++++++++ 2 files changed, 13 insertions(+), 11 deletions(-)
sequencer.c +2 -11
@@ -417,17 +417,8 @@ static int write_message(const void *buf, size_t len, const char *filename,return 0 ; } -/* -* Reads a file that was presumably written by a shell script, i.e. with an -* end-of-line marker that needs to be stripped. -* -* Note that only the last end-of-line marker is stripped, consistent with the -* behavior of "$(cat path)" in a shell script. -* -* Returns 1 if the file was read, 0 if it could not be read or does not exist. -*/ -static int read_oneliner (struct strbuf *buf,const char *path, -int skip_if_empty,int warn_nonexistence) +int read_oneliner (struct strbuf *buf,const char *path, +int skip_if_empty,int warn_nonexistence) {int ret =0 ;struct strbuf file_buf =STRBUF_INIT ;
sequencer.h +11 -0
@@ -202,6 +202,17 @@ void print_commit_summary(struct repository *repo,const struct object_id *oid, unsigned int flags); +/* +* Reads a file that was presumably written by a shell script, i.e. with an +* end-of-line marker that needs to be stripped. +* +* Note that only the last end-of-line marker is stripped, consistent with the +* behavior of "$(cat path)" in a shell script. +* +* Returns 1 if the file was read, 0 if it could not be read. +*/ +int read_oneliner (struct strbuf *buf,const char *path, +int skip_if_empty,int warn_nonexistence);int read_author_script (const char *path,char **name,char **email,char **date,int allow_missing);void parse_strategy_opts (struct replay_opts *opts,char *raw_opts);
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 10/17 reset: extract reset_head() from rebase
In reply to Denton LiuContinue the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.
This patch is best viewed with --color-moved.
--- Makefile | 1 + builtin/rebase.c | 139 +--------------------------------------------- reset.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++ reset.h | 20 +++++++ 4 files changed, 162 insertions(+), 138 deletions(-) create mode 100644 reset.c create mode 100644 reset.h
Makefile +1 -0
@@ -967,6 +967,7 @@ LIB_OBJS += replace-object.o LIB_OBJS += repo-settings.o LIB_OBJS += repository.o LIB_OBJS += rerere.o +LIB_OBJS += reset.o LIB_OBJS += resolve-undo.o LIB_OBJS += revision.o LIB_OBJS += run-command.o
builtin/rebase.c +1 -138
151 lines not on this page · raw message
reset.c new file +140 -0
140 lines not on this page · raw message
reset.h new file +20 -0
@@ -0,0 +1,20 @@ +#ifndef RESET_H +#define RESET_H + +#include "hash.h" +#include "repository.h" + +#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" + +#define RESET_HEAD_DETACH (1<<0) +#define RESET_HEAD_HARD (1<<1) +#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) +#define RESET_HEAD_REFS_ONLY (1<<3) +#define RESET_ORIG_HEAD (1<<4) + +int reset_head (struct repository *r,struct object_id *oid,const char *action, +const char *switch_to_branch,unsigned flags, +const char *reflog_orig_head,const char *reflog_head, +const char *default_reflog_action); + +#endif
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 12/17 rebase: generify create_autostash()
In reply to Denton LiuIn the future, we plan on lib-ifying create_autostash() so we need it to be more generic. Make it more generic by making it accept a struct repository argument instead of implicitly using the non-repo functions and the_repository. Also, make it accept a path argument so that we no longer rely have to rely on struct rebase_options. Finally, make it accept a default_reflog_action argument so we no longer have to rely on DEFAULT_REFLOG_ACTION.
--- builtin/rebase.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
builtin/rebase.c +16 -16
@@ -1232,22 +1232,21 @@ static int check_exec_cmd(const char *cmd)return 0 ; } -static void create_autostash (struct rebase_options *options) +static void create_autostash (struct repository *r,const char *path, +const char *default_reflog_action) {struct strbuf buf =STRBUF_INIT ;struct lock_file lock_file =LOCK_INIT ;int fd; - fd =hold_locked_index (&lock_file,0 ); -refresh_cache (REFRESH_QUIET ); + fd =repo_hold_locked_index (r, &lock_file,0 ); +refresh_index (r->index,REFRESH_QUIET ,NULL ,NULL ,NULL );if (0 <= fd) -repo_update_index_if_able (the_repository, &lock_file); +repo_update_index_if_able (r, &lock_file);rollback_lock_file (&lock_file); -if (has_unstaged_changes (the_repository,1 ) || -has_uncommitted_changes (the_repository,1 )) { -const char *autostash = -state_dir_path ("autostash" , options); +if (has_unstaged_changes (r,1 ) || +has_uncommitted_changes (r,1 )) {struct child_process stash =CHILD_PROCESS_INIT ;struct object_id oid;
@@ -1265,18 +1264,18 @@ static void create_autostash(struct rebase_options *options)strbuf_reset (&buf);strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); -if (safe_create_leading_directories_const (autostash)) +if (safe_create_leading_directories_const (path))die (_ ("Could not create directory for '%s'" ), - options->state_dir); -write_file (autostash,"%s" ,oid_to_hex (&oid)); + path); +write_file (path,"%s" ,oid_to_hex (&oid));printf (_ ("Created autostash: %s\n" ), buf.buf); -if (reset_head (the_repository,NULL ,"reset --hard" , +if (reset_head (r,NULL ,"reset --hard" ,NULL ,RESET_HEAD_HARD ,NULL ,NULL , -DEFAULT_REFLOG_ACTION ) <0 ) + default_reflog_action) <0 )die (_ ("could not reset --hard" )); -if (discard_index (the_repository->index) <0 || -repo_read_index (the_repository) <0 ) +if (discard_index (r->index) <0 || +repo_read_index (r) <0 )die (_ ("could not read index" )); }strbuf_release (&buf);
@@ -1881,7 +1880,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)die (_ ("could not read index" ));if (options.autostash) { -create_autostash (&options); +create_autostash (the_repository,state_dir_path ("autostash" , &options), +DEFAULT_REFLOG_ACTION ); } if (require_clean_work_tree(the_repository, "rebase",
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 13/17 sequencer: extract perform_autostash() from rebase
In reply to Denton LiuLib-ify the autostash code by extracting perform_autostash() from rebase into sequencer. In a future commit, this will be used to implement --autostash in other builtins.
This patch is best viewed with --color-moved.
--- builtin/rebase.c | 49 ----------------------------------------------- sequencer.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ sequencer.h | 2 ++ 3 files changed, 52 insertions(+), 49 deletions(-)
builtin/rebase.c +0 -49
@@ -1232,55 +1232,6 @@ static int check_exec_cmd(const char *cmd)return 0 ; } -static void create_autostash (struct repository *r,const char *path, -const char *default_reflog_action) -{ -struct strbuf buf =STRBUF_INIT ; -struct lock_file lock_file =LOCK_INIT ; -int fd; - - fd =repo_hold_locked_index (r, &lock_file,0 ); -refresh_index (r->index,REFRESH_QUIET ,NULL ,NULL ,NULL ); -if (0 <= fd) -repo_update_index_if_able (r, &lock_file); -rollback_lock_file (&lock_file); - -if (has_unstaged_changes (r,1 ) || -has_uncommitted_changes (r,1 )) { -struct child_process stash =CHILD_PROCESS_INIT ; -struct object_id oid; - -argv_array_pushl (&stash.args, -"stash" ,"create" ,"autostash" ,NULL ); - stash.git_cmd =1 ; - stash.no_stdin =1 ; -strbuf_reset (&buf); -if (capture_command (&stash, &buf,GIT_MAX_HEXSZ )) -die (_ ("Cannot autostash" )); -strbuf_trim_trailing_newline (&buf); -if (get_oid (buf.buf, &oid)) -die (_ ("Unexpected stash response: '%s'" ), - buf.buf); -strbuf_reset (&buf); -strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); - -if (safe_create_leading_directories_const (path)) -die (_ ("Could not create directory for '%s'" ), - path); -write_file (path,"%s" ,oid_to_hex (&oid)); -printf (_ ("Created autostash: %s\n" ), buf.buf); -if (reset_head (r,NULL ,"reset --hard" , -NULL ,RESET_HEAD_HARD ,NULL ,NULL , - default_reflog_action) <0 ) -die (_ ("could not reset --hard" )); - -if (discard_index (r->index) <0 || -repo_read_index (r) <0 ) -die (_ ("could not read index" )); - } -strbuf_release (&buf); -} -int cmd_rebase (int argc,const char **argv,const char *prefix) {struct rebase_options options =REBASE_OPTIONS_INIT ;
sequencer.c +50 -0
62 lines not on this page · raw message
sequencer.h +2 -0
@@ -195,6 +195,8 @@ void commit_post_rewrite(struct repository *r,int prepare_branch_to_be_rebased (struct repository *r,struct replay_opts *opts,const char *commit); +void create_autostash (struct repository *r,const char *path, +const char *default_reflog_action);int apply_autostash (const char *path);#define SUMMARY_INITIAL_COMMIT (1 << 0)
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 9/17 rebase: generify reset_head()
In reply to Denton LiuIn the future, we plan on lib-ifying reset_head() so we need it to be more generic. Make it more generic by making it accept a struct repository argument instead of implicitly using the non-repo functions. Also, make it accept a const char *default_reflog_action argument so that the default action of "rebase" isn't hardcoded in.
--- builtin/rebase.c | 63 +++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 27 deletions(-)
builtin/rebase.c +36 -27
@@ -28,6 +28,8 @@#include "sequencer.h" #include "rebase-interactive.h" +#define DEFAULT_REFLOG_ACTION "rebase" +static char const *const builtin_rebase_usage[] = {N_ ("git rebase [-i] [options] [--exec <cmd>] " "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]" ),
@@ -760,9 +762,10 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)#define RESET_HEAD_REFS_ONLY (1<<3)#define RESET_ORIG_HEAD (1<<4) -static int reset_head (struct object_id *oid,const char *action, +static int reset_head (struct repository *r,struct object_id *oid,const char *action,const char *switch_to_branch,unsigned flags, -const char *reflog_orig_head,const char *reflog_head) +const char *reflog_orig_head,const char *reflog_head, +const char *default_reflog_action) {unsigned detach_head = flags &RESET_HEAD_DETACH ;unsigned reset_hard = flags &RESET_HEAD_HARD ;
@@ -784,7 +787,7 @@ static int reset_head(struct object_id *oid, const char *action,if (switch_to_branch && !starts_with (switch_to_branch,"refs/" ))BUG ("Not a fully qualified branch: '%s'" , switch_to_branch); -if (!refs_only &&hold_locked_index (&lock,LOCK_REPORT_ON_ERROR ) <0 ) { +if (!refs_only &&repo_hold_locked_index (r, &lock,LOCK_REPORT_ON_ERROR ) <0 ) { ret =-1 ; goto leave_reset_head; }
@@ -803,26 +806,26 @@ static int reset_head(struct object_id *oid, const char *action,memset (&unpack_tree_opts,0 ,sizeof (unpack_tree_opts));setup_unpack_trees_porcelain (&unpack_tree_opts, action); unpack_tree_opts.head_idx =1 ; - unpack_tree_opts.src_index = the_repository->index; - unpack_tree_opts.dst_index = the_repository->index; + unpack_tree_opts.src_index = r->index; + unpack_tree_opts.dst_index = r->index; unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; unpack_tree_opts.update =1 ; unpack_tree_opts.merge =1 ;if (!detach_head) unpack_tree_opts.reset =1 ; -if (repo_read_index_unmerged (the_repository) <0 ) { +if (repo_read_index_unmerged (r) <0 ) { ret =error (_ ("could not read index" )); goto leave_reset_head; } -if (!reset_hard && !fill_tree_descriptor (the_repository, &desc[nr++], &head_oid)) { +if (!reset_hard && !fill_tree_descriptor (r, &desc[nr++], &head_oid)) { ret =error (_ ("failed to find tree of %s" ),oid_to_hex (&head_oid)); goto leave_reset_head; } -if (!fill_tree_descriptor (the_repository, &desc[nr++], oid)) { +if (!fill_tree_descriptor (r, &desc[nr++], oid)) { ret =error (_ ("failed to find tree of %s" ),oid_to_hex (oid)); goto leave_reset_head; }
@@ -833,16 +836,16 @@ static int reset_head(struct object_id *oid, const char *action, } tree =parse_tree_indirect (oid); -prime_cache_tree (the_repository, the_repository->index, tree); +prime_cache_tree (r, r->index, tree); -if (write_locked_index (the_repository->index, &lock,COMMIT_LOCK ) <0 ) { +if (write_locked_index (r->index, &lock,COMMIT_LOCK ) <0 ) { ret =error (_ ("could not write index" )); goto leave_reset_head; } reset_head_refs: reflog_action =getenv (GIT_REFLOG_ACTION_ENVIRONMENT ); -strbuf_addf (&msg,"%s: " , reflog_action ? reflog_action :"rebase" ); +strbuf_addf (&msg,"%s: " , reflog_action ? reflog_action : default_reflog_action); prefix_len = msg.len;if (update_orig_head) {
@@ -904,8 +907,10 @@ static int move_to_original_branch(struct rebase_options *opts) opts->head_name, oid_to_hex(&opts->onto->object.oid));strbuf_addf (&head_reflog,"rebase finished: returning to %s" , opts->head_name); - ret =reset_head (NULL ,"" , opts->head_name,RESET_HEAD_REFS_ONLY , - orig_head_reflog.buf, head_reflog.buf); + ret =reset_head (the_repository,NULL ,"" , opts->head_name, +RESET_HEAD_REFS_ONLY , + orig_head_reflog.buf, head_reflog.buf, +DEFAULT_REFLOG_ACTION );strbuf_release (&orig_head_reflog);strbuf_release (&head_reflog);
@@ -999,8 +1004,9 @@ static int run_am(struct rebase_options *opts)free (rebased_patches);argv_array_clear (&am.args); -reset_head (&opts->orig_head,"checkout" , opts->head_name,0 , -"HEAD" ,NULL ); +reset_head (the_repository, &opts->orig_head,"checkout" , + opts->head_name,0 , +"HEAD" ,NULL ,DEFAULT_REFLOG_ACTION ); error(_("\ngit encountered an error while preparing the " "patches to replay\n" "these revisions:\n"
@@ -1609,8 +1615,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)rerere_clear (the_repository, &merge_rr);string_list_clear (&merge_rr,1 ); -if (reset_head (NULL ,"reset" ,NULL ,RESET_HEAD_HARD , -NULL ,NULL ) <0 ) +if (reset_head (the_repository,NULL ,"reset" ,NULL ,RESET_HEAD_HARD , +NULL ,NULL ,DEFAULT_REFLOG_ACTION ) <0 )die (_ ("could not discard worktree changes" ));remove_branch_state (the_repository,0 );if (read_basic_state (&options))
@@ -1627,9 +1633,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)if (read_basic_state (&options))exit (1 ); -if (reset_head (&options.orig_head,"reset" , +if (reset_head (the_repository, &options.orig_head,"reset" , options.head_name,RESET_HEAD_HARD , -NULL ,NULL ) <0 ) +NULL ,NULL ,DEFAULT_REFLOG_ACTION ) <0 )die (_ ("could not move back to %s" ),oid_to_hex (&options.orig_head));remove_branch_state (the_repository,0 );
@@ -1998,8 +2004,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.state_dir);write_file (autostash,"%s" ,oid_to_hex (&oid));printf (_ ("Created autostash: %s\n" ), buf.buf); -if (reset_head (NULL ,"reset --hard" , -NULL ,RESET_HEAD_HARD ,NULL ,NULL ) <0 ) +if (reset_head (the_repository,NULL ,"reset --hard" , +NULL ,RESET_HEAD_HARD ,NULL ,NULL , +DEFAULT_REFLOG_ACTION ) <0 )die (_ ("could not reset --hard" )); if (discard_index(the_repository->index) < 0 ||
@@ -2045,10 +2052,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)strbuf_addf (&buf,"%s: checkout %s" ,getenv (GIT_REFLOG_ACTION_ENVIRONMENT ), options.switch_to); -if (reset_head (&oid,"checkout" , +if (reset_head (the_repository, &oid,"checkout" , options.head_name,RESET_HEAD_RUN_POST_CHECKOUT_HOOK , -NULL , buf.buf) <0 ) { +NULL , buf.buf, +DEFAULT_REFLOG_ACTION ) <0 ) { ret = !!error (_ ("could not switch to " "%s" ), options.switch_to);
@@ -2120,10 +2128,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)strbuf_addf (&msg,"%s: checkout %s" ,getenv (GIT_REFLOG_ACTION_ENVIRONMENT ), options.onto_name); -if (reset_head (&options.onto->object.oid,"checkout" ,NULL , +if (reset_head (the_repository, &options.onto->object.oid,"checkout" ,NULL ,RESET_HEAD_DETACH |RESET_ORIG_HEAD |RESET_HEAD_RUN_POST_CHECKOUT_HOOK , -NULL , msg.buf)) +NULL , msg.buf,DEFAULT_REFLOG_ACTION ))die (_ ("Could not detach HEAD" ));strbuf_release (&msg);
@@ -2138,8 +2146,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)strbuf_addf (&msg,"rebase finished: %s onto %s" , options.head_name ? options.head_name :"detached HEAD" ,oid_to_hex (&options.onto->object.oid)); -reset_head (NULL ,"Fast-forwarded" , options.head_name, -RESET_HEAD_REFS_ONLY ,"HEAD" , msg.buf); +reset_head (the_repository,NULL ,"Fast-forwarded" , options.head_name, +RESET_HEAD_REFS_ONLY ,"HEAD" , msg.buf, +DEFAULT_REFLOG_ACTION );strbuf_release (&msg); ret = !!finish_rebase (&options); goto cleanup;
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 11/17 rebase: extract create_autostash()
In reply to Denton LiuIn a future commit, we will lib-ify this code. In preparation for this, extract the code into the create_autostash() function so that it can be cleaned up before it is finally lib-ified.
This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.
--- builtin/rebase.c | 94 +++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 44 deletions(-)
builtin/rebase.c +50 -44
@@ -1232,6 +1232,55 @@ static int check_exec_cmd(const char *cmd)return 0 ; } +static void create_autostash (struct rebase_options *options) +{ +struct strbuf buf =STRBUF_INIT ; +struct lock_file lock_file =LOCK_INIT ; +int fd; + + fd =hold_locked_index (&lock_file,0 ); +refresh_cache (REFRESH_QUIET ); +if (0 <= fd) +repo_update_index_if_able (the_repository, &lock_file); +rollback_lock_file (&lock_file); + +if (has_unstaged_changes (the_repository,1 ) || +has_uncommitted_changes (the_repository,1 )) { +const char *autostash = +state_dir_path ("autostash" , options); +struct child_process stash =CHILD_PROCESS_INIT ; +struct object_id oid; + +argv_array_pushl (&stash.args, +"stash" ,"create" ,"autostash" ,NULL ); + stash.git_cmd =1 ; + stash.no_stdin =1 ; +strbuf_reset (&buf); +if (capture_command (&stash, &buf,GIT_MAX_HEXSZ )) +die (_ ("Cannot autostash" )); +strbuf_trim_trailing_newline (&buf); +if (get_oid (buf.buf, &oid)) +die (_ ("Unexpected stash response: '%s'" ), + buf.buf); +strbuf_reset (&buf); +strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); + +if (safe_create_leading_directories_const (autostash)) +die (_ ("Could not create directory for '%s'" ), + options->state_dir); +write_file (autostash,"%s" ,oid_to_hex (&oid)); +printf (_ ("Created autostash: %s\n" ), buf.buf); +if (reset_head (the_repository,NULL ,"reset --hard" , +NULL ,RESET_HEAD_HARD ,NULL ,NULL , +DEFAULT_REFLOG_ACTION ) <0 ) +die (_ ("could not reset --hard" )); + +if (discard_index (the_repository->index) <0 || +repo_read_index (the_repository) <0 ) +die (_ ("could not read index" )); + } +strbuf_release (&buf); +}int cmd_rebase (int argc,const char **argv,const char *prefix) {
@@ -1832,50 +1881,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)die (_ ("could not read index" ));if (options.autostash) { -struct lock_file lock_file =LOCK_INIT ; -int fd; - - fd =hold_locked_index (&lock_file,0 ); -refresh_cache (REFRESH_QUIET ); -if (0 <= fd) -repo_update_index_if_able (the_repository, &lock_file); -rollback_lock_file (&lock_file); - -if (has_unstaged_changes (the_repository,1 ) || -has_uncommitted_changes (the_repository,1 )) { -const char *autostash = -state_dir_path ("autostash" , &options); -struct child_process stash =CHILD_PROCESS_INIT ; -struct object_id oid; - -argv_array_pushl (&stash.args, -"stash" ,"create" ,"autostash" ,NULL ); - stash.git_cmd =1 ; - stash.no_stdin =1 ; -strbuf_reset (&buf); -if (capture_command (&stash, &buf,GIT_MAX_HEXSZ )) -die (_ ("Cannot autostash" )); -strbuf_trim_trailing_newline (&buf); -if (get_oid (buf.buf, &oid)) -die (_ ("Unexpected stash response: '%s'" ), - buf.buf); -strbuf_reset (&buf); -strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); - -if (safe_create_leading_directories_const (autostash)) -die (_ ("Could not create directory for '%s'" ), - options.state_dir); -write_file (autostash,"%s" ,oid_to_hex (&oid)); -printf (_ ("Created autostash: %s\n" ), buf.buf); -if (reset_head (the_repository,NULL ,"reset --hard" , -NULL ,RESET_HEAD_HARD ,NULL ,NULL , -DEFAULT_REFLOG_ACTION ) <0 ) -die (_ ("could not reset --hard" )); - -if (discard_index (the_repository->index) <0 || -repo_read_index (the_repository) <0 ) -die (_ ("could not read index" )); - } +create_autostash (&options); } if (require_clean_work_tree(the_repository, "rebase",
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 17/17 pull: pass --autostash to merge
In reply to Denton LiuBefore, --autostash only worked with git pull --rebase. However, in the last patch, merge learned --autostash as well so there's no reason why we should have this restriction anymore. Teach pull to pass --autostash to merge, just like it did for rebase.
--- Documentation/git-pull.txt | 9 ------- Documentation/merge-options.txt | 4 +-- builtin/pull.c | 9 ++++--- t/t5520-pull.sh | 43 +++++++++++++++++++++++++++------ 4 files changed, 42 insertions(+), 23 deletions(-)
Documentation/git-pull.txt +0 -9
@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully. --no-rebase:: Override earlier --rebase. ---autostash:: ---no-autostash:: - Before starting rebase, stash local modifications away (see - linkgit:git-stash[1]) if needed, and apply the stash entry when - done. `--no-autostash` is useful to override the `rebase.autoStash` - configuration variable (see linkgit:git-config[1]). -+ -This option is only valid when "--rebase" is used. - Options related to fetching ~~~~~~~~~~~~~~~~~~~~~~~~~~~
Documentation/merge-options.txt +2 -2
@@ -155,6 +155,8 @@ ifndef::git-pull[] Note that not all merge strategies may support progress reporting. +endif::git-pull[] + --autostash:: --no-autostash:: Automatically create a temporary stash entry before the operation
@@ -163,8 +165,6 @@ ifndef::git-pull[] with care: the final stash application after a successful rebase might result in non-trivial conflicts. -endif::git-pull[] - --allow-unrelated-histories:: By default, `git merge` command refuses to merge histories that do not share a common ancestor. This option can be
builtin/pull.c +5 -4
@@ -183,7 +183,7 @@ static struct option pull_options[] = { N_("verify that the named commit has a valid GPG signature"), PARSE_OPT_NOARG),OPT_BOOL (0 ,"autostash" , &opt_autostash, -N_ ("automatically stash/stash pop before and after rebase" )), +N_ ("automatically stash/stash pop before and after" )),OPT_PASSTHRU_ARGV ('s' ,"strategy" , &opt_strategies,N_ ("strategy" ),N_ ("merge strategy to use" ), 0),
@@ -671,6 +671,10 @@ static int run_merge(void)argv_array_pushv (&args, opt_strategy_opts.argv);if (opt_gpg_sign)argv_array_push (&args, opt_gpg_sign); +if (opt_autostash ==0 ) +argv_array_push (&args,"--no-autostash" ); +else if (opt_autostash ==1 ) +argv_array_push (&args,"--autostash" );if (opt_allow_unrelated_histories >0 )argv_array_push (&args,"--allow-unrelated-histories" );
@@ -918,9 +922,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)if (get_oid ("HEAD" , &orig_head))oidclr (&orig_head); -if (!opt_rebase && opt_autostash !=-1 ) -die (_ ("--[no-]autostash option is only valid with --rebase." )); - autostash = config_autostash;if (opt_rebase) {if (opt_autostash !=-1 )
t/t5520-pull.sh +35 -8
55 lines not on this page · raw message
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 14/17 sequencer: unlink autostash in apply_autostash()
In reply to Denton LiuExplicitly remove autostash file in apply_autostash() once it has been applied successfully.
This is currently a no-op because the only users of this function will unlink the state (including the autostash file) after this function runs. However, in the future, we will introduce a user of the function that does not explicitly remove the state so we do it here.
--- sequencer.c | 1 + 1 file changed, 1 insertion(+)
sequencer.c +1 -0
@@ -3834,6 +3834,7 @@ int apply_autostash(const char *path) " \"git stash drop\" at any time.\n")); } +unlink (path);strbuf_release (&stash_sha1);return ret; }
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 16/17 t5520: make test_pull_autostash() accept expect_parent_num
In reply to Denton LiuBefore, test_pull_autostash() was hardcoded to run test_cmp_rev HEAD^ copy to test that a rebase happened. However, in a future patch, we plan on testing merging as well. Make test_pull_autostash() accept a parent number as an argument so that, in the future, we can test if a merge happened in addition to a rebase.
--- t/t5520-pull.sh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
t/t5520-pull.sh +8 -6
@@ -10,11 +10,13 @@ modify () { } test_pull_autostash () { + expect_parent_num="$1" && + shift && git reset --hard before-rebase && echo dirty >new_file && git add new_file && git pull "$@" . copy && - test_cmp_rev HEAD^ copy && + test_cmp_rev HEAD^"$expect_parent_num" copy && echo dirty >expect && test_cmp expect new_file && echo "modified again" >expect &&
@@ -356,22 +358,22 @@ test_expect_success '--rebase fails with multiple branches' ' test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' ' test_config rebase.autostash true && - test_pull_autostash --rebase + test_pull_autostash 1 --rebase ' test_expect_success 'pull --rebase --autostash & rebase.autostash=true' ' test_config rebase.autostash true && - test_pull_autostash --rebase --autostash + test_pull_autostash 1 --rebase --autostash ' test_expect_success 'pull --rebase --autostash & rebase.autostash=false' ' test_config rebase.autostash false && - test_pull_autostash --rebase --autostash + test_pull_autostash 1 --rebase --autostash ' test_expect_success 'pull --rebase --autostash & rebase.autostash unset' ' test_unconfig rebase.autostash && - test_pull_autostash --rebase --autostash + test_pull_autostash 1 --rebase --autostash ' test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '
@@ -409,7 +411,7 @@ test_expect_success 'pull.rebase' ' test_expect_success 'pull --autostash & pull.rebase=true' ' test_config pull.rebase true && - test_pull_autostash --autostash + test_pull_autostash 1 --autostash ' test_expect_success 'pull --no-autostash & pull.rebase=true' '
Signature · 1 line
2.24.1.810.g65a2f617f4
PATCH v2 15/17 merge: teach --autostash option
In reply to Denton LiuIn rebase, one can pass the --autostash option to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.
Implement the --autostash option and corresponding merge.autoStash option in merge which stashes before merging and then pops after.
This option is useful when a developer has some local changes on a topic branch but they realize that their work depends on another branch. Previously, they had to run something like
git fetch ...
git stash push
git merge FETCH_HEAD
git stash pop
but now, that is reduced to
git fetch ... git merge --autostash FETCH_HEAD
--- Documentation/config/merge.txt | 10 +++++ Documentation/merge-options.txt | 8 ++++ branch.c | 1 + builtin/commit.c | 2 + builtin/merge.c | 17 +++++++++ path.c | 1 + path.h | 4 +- t/t3033-merge-toplevel.sh | 22 +++++++++++ t/t7600-merge.sh | 65 +++++++++++++++++++++++++++++++++ 9 files changed, 129 insertions(+), 1 deletion(-)
Documentation/config/merge.txt +10 -0
@@ -70,6 +70,16 @@ merge.stat:: Whether to print the diffstat between ORIG_HEAD and the merge result at the end of the merge. True by default. +merge.autoStash:: + When set to true, automatically create a temporary stash entry + before the operation begins, and apply it after the operation + ends. This means that you can run rebase on a dirty worktree. + However, use with care: the final stash application after a + successful rebase might result in non-trivial conflicts. + This option can be overridden by the `--no-autostash` and + `--autostash` options of linkgit:git-merge[1]. + Defaults to false. + merge.tool:: Controls which merge tool is used by linkgit:git-mergetool[1]. The list below shows the valid built-in values.
Documentation/merge-options.txt +8 -0
@@ -155,6 +155,14 @@ ifndef::git-pull[] Note that not all merge strategies may support progress reporting. +--autostash:: +--no-autostash:: + Automatically create a temporary stash entry before the operation + begins, and apply it after the operation ends. This means + that you can run rebase on a dirty worktree. However, use + with care: the final stash application after a successful + rebase might result in non-trivial conflicts. + endif::git-pull[] --allow-unrelated-histories::
branch.c +1 -0
@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)unlink (git_path_merge_rr (r));unlink (git_path_merge_msg (r));unlink (git_path_merge_mode (r)); +apply_autostash (git_path_merge_autostash (r)); }void remove_branch_state (struct repository *r,int verbose)
builtin/commit.c +2 -0
@@ -1679,6 +1679,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)unlink (git_path_merge_mode (the_repository));unlink (git_path_squash_msg (the_repository)); +apply_autostash (git_path_merge_autostash (the_repository)); +if (commit_index_files ()) die(_("repository has been updated, but unable to write\n" "new_index file. Check that disk is not full and quota is\n"
builtin/merge.c +17 -0
@@ -81,6 +81,7 @@ static int show_progress = -1;static int default_to_upstream =1 ;static int signoff;static const char *sign_commit; +static int autostash;static int no_verify;static struct strategy all_strategy[] = {
@@ -285,6 +286,8 @@ static struct option builtin_merge_options[] = {OPT_SET_INT (0 ,"progress" , &show_progress,N_ ("force progress reporting" ),1 ), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, +OPT_BOOL (0 ,"autostash" , &autostash, +N_ ("automatically stash/stash pop before and after" )),OPT_BOOL (0 ,"overwrite-ignore" , &overwrite_ignore,N_ ("update ignored files (default)" )),OPT_BOOL (0 ,"signoff" , &signoff,N_ ("add Signed-off-by:" )), OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ -440,6 +443,7 @@ static void finish(struct commit *head_commit,strbuf_addf (&reflog_message,"%s: %s" ,getenv ("GIT_REFLOG_ACTION" ), msg); } +apply_autostash (git_path_merge_autostash (the_repository));if (squash) {squash_message (head_commit, remoteheads); }else {
@@ -631,6 +635,9 @@ static int git_merge_config(const char *k, const char *v, void *cb) }else if (!strcmp (k,"commit.gpgsign" )) { sign_commit =git_config_bool (k, v) ?"" :NULL ;return 0 ; + }else if (!strcmp (k,"merge.autostash" )) { + autostash =git_config_bool (k, v); +return 0 ; } status =fmt_merge_msg_config (k, v, cb);
@@ -1288,6 +1295,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)/* Invoke 'git reset --merge' */ ret =cmd_reset (nargc, nargv, prefix); +apply_autostash (git_path_merge_autostash (the_repository)); goto done; }
@@ -1508,6 +1516,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix) goto done; } +if (autostash) +create_autostash (the_repository, +git_path_merge_autostash (the_repository), +"merge" ); if (checkout_fast_forward(the_repository, &head_commit->object.oid, &commit->object.oid,
@@ -1574,6 +1586,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)if (fast_forward ==FF_ONLY )die (_ ("Not possible to fast-forward, aborting." )); +if (autostash) +create_autostash (the_repository, +git_path_merge_autostash (the_repository), +"merge" ); +/* We are going to make a new commit. */ git_committer_info (IDENT_STRICT );
path.c +1 -0
@@ -1533,5 +1533,6 @@ REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG") REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR") REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE") REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD") +REPO_GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH") REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")REPO_GIT_PATH_FUNC (shallow,"shallow" )
path.h +3 -1
@@ -177,11 +177,12 @@ struct path_cache {const char *merge_rr;const char *merge_mode;const char *merge_head; +const char *merge_autostash;const char *fetch_head;const char *shallow; }; -#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }const char *git_path_cherry_pick_head (struct repository *r);const char *git_path_revert_head (struct repository *r);
@@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);const char *git_path_merge_rr (struct repository *r);const char *git_path_merge_mode (struct repository *r);const char *git_path_merge_head (struct repository *r); +const char *git_path_merge_autostash (struct repository *r);const char *git_path_fetch_head (struct repository *r);const char *git_path_shallow (struct repository *r);
t/t3033-merge-toplevel.sh +22 -0
@@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' ' test_must_fail git merge five ' +test_expect_success 'refuse two-project merge by default, quit before --autostash happens' ' + t3033_reset && + git reset --hard four && + echo change >>one.t && + git diff >expect && + test_must_fail git merge --autostash five 2>err && + test_i18ngrep ! "stash" err && + git diff >actual && + test_cmp expect actual +' + test_expect_success 'two-project merge with --allow-unrelated-histories' ' t3033_reset && git reset --hard four &&
@@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' ' git diff --exit-code five ' +test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' ' + t3033_reset && + git reset --hard four && + echo change >>one.t && + git diff one.t >expect && + git merge --allow-unrelated-histories --autostash five 2>err && + test_i18ngrep "Applied autostash." err && + git diff one.t >actual && + test_cmp expect actual +' + test_done
t/t7600-merge.sh +65 -0
84 lines not on this page · raw message
Signature · 1 line
2.24.1.810.g65a2f617f4
Re: [PATCH v2 04/17] sequencer: configurably warn on non-existent files
In reply to Denton Liusequencer.c static int write_message(const void *buf, size_t len, const char *filename, · line 430
Quoted 4 lines · Denton Liu
sequencer.c +2
-2-static int read_oneliner (struct strbuf *buf, -const char *path,int skip_if_empty) +static int read_oneliner (struct strbuf *buf,const char *path, +int skip_if_empty,int warn_nonexistence)
I would have to say that this results in a rather poor API in the end, and also forces an awkward API evolution, and we need to be careful especially if we plan to make this an external API in a later step in the series.
There is a topic in flight that introduces additional callsites of read_oneliner(). It is a selfish move that breaks codebase unnecessarily to unilaterally change the function signature.
Two possible ways to improve are:
* keep read_oneliner() with existing function signature working for other topics in flight, by renaming your extended variant and making read_oneliner() a thin wrapper that calls yours with the extended feature disabled (i.e. warn_nonexistence==0); or
* update skip_if_empty that wastes an int parameter to pass only a single bit into an unsigned that is a collection of bits, after vetting that new callsites contemporary topics add always pass either 0 or 1 as the parameter. Then
#define READ_ONELINER_SKIP_IF_EMPTY 01 #define READ_ONELINER_WARN_MISSING 02 static int read_oneliner(struct strbuf *buf, const char *path,
unsigned flags)
would transparently work well for those other topics.
The latter probably is more preferrable, as the end result would be a cleaner API than the former or the one presented in this series.
Thanks.
Quoted 119 lines
{
sequencer.c +14
-14int 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);
Re: [PATCH v2 01/17] Makefile: alphabetically sort += lists
In reply to Denton LiuQuoted 3 lines · Denton Liu
Note that if we omit file prefixes, the lists aren't sorted in strictly alphabetical order (e.g. archive.o comes after archive-zip.o instead of before archive-tar.o). This is intentional ...
That's fine, but then we shouldn't label it as "alphabetically sort".
Quoted 3 lines
because the purpose of
maintaining the sorted list is to ensure line insertions are deterministic.
Truly alphabetic sorting is deterministic, too, so that is *not* the reason why you are not being strictly alphabetic. The true reason is because it is easier to apply mechanical sorting in ascii order than truly sort alphabetically. Both are equally deterministic and reproducible. Alphabetic might be easier for humans to see and understand, but you value mechanical ease of sorting and that is why you chose ascii order over alphabetical sorting.
Quoted 3 lines · Denton Liu
Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.
Sigh.
I would actually have preferred to remove the autostash from rebase if the goal is to have a consistent behaviour between the two, though ;-)
But it is OK as long as it does not degrade the codepath with changes that wouldn't have been necessary if this weren't added.
Let's see how it goes. Queued.
Quoted 11 lines · Junio C Hamano
Quoted 3 lines · Denton Liu
Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.
Sigh.
I would actually have preferred to remove the autostash from rebase if the goal is to have a consistent behaviour between the two, though ;-)
I find the --autostash option to rebase useful if I want to edit a commit and I've got uncommitted changes. I wish it was called --stash though as it's not automatic as one has to set a config setting or pass it on the commandline. I'm also not convinced the config setting is a good idea as it allows rebase to run when I think I've committed everything but I haven't.
For merge --autostash I think we need to consider the interaction of --no-commit with --autostash as stash pop will refuse to run if the merge modified any of the stashed paths.
Best Wishes
Phillip
Quoted 5 lines
But it is OK as long as it does not degrade the codepath with changes that wouldn't have been necessary if this weren't added.
Let's see how it goes. Queued.
Hi Junio,
Quoted 8 lines · Junio C Hamano
Quoted 3 lines · Denton Liu
Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.
Sigh.
Quoted 8 lines
I would actually have preferred to remove the autostash from rebase if the goal is to have a consistent behaviour between the two, though ;-)
But it is OK as long as it does not degrade the codepath with changes that wouldn't have been necessary if this weren't added.
Let's see how it goes. Queued.
Sorry for the radio silence for the past week. I've been offline trying to recover from a particularly bad flu. Anyway, I have a reroll of this topic planned with the flags change you suggested and addressing Philip's concerns about --no-commit.
Since this is a new topic that definitely won't be accepted for v2.25.0, I'll probably be taking my time with this ;)
Thanks,
Denton
Hi Phillip,
Sorry for the late reply. I'm back in school so I've been pretty busy lately.
Quoted 3 lines · Phillip Wood
For
merge --autostashI think we need to consider the interaction of--no-commitwith--autostashasstash popwill refuse to run if the merge modified any of the stashed paths.
The only time when we run the stash pop is when we either commit the merge or abort it. With this in mind, what do you think of the following test cases? If they look good, I can squash them into the appropriate patch and send in a reroll.
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index e0c8a0bad4..af5db58e16 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -727,6 +727,33 @@ test_expect_success 'conflicted merge with --autostash, --abort restores stash' test_cmp file.1 file ' +test_expect_success 'completed merge with --no-commit and --autostash' ' + git reset --hard c1 && + git merge-file file file.orig file.9 && + git diff >expect && + git merge --no-commit --autostash c2 && + git stash show -p MERGE_AUTOSTASH >actual && + test_cmp expect actual && + git commit 2>err && + test_i18ngrep "Applied autostash." err && + git show HEAD:file >merge-result && + test_cmp result.1-5 merge-result && + test_cmp result.1-5-9 file +' + +test_expect_success 'aborted merge with --no-commit and --autostash' ' + git reset --hard c1 && + git merge-file file file.orig file.9 && + git diff >expect && + git merge --no-commit --autostash c2 && + git stash show -p MERGE_AUTOSTASH >actual && + test_cmp expect actual && + git merge --abort 2>err && + test_i18ngrep "Applied autostash." err && + git diff >actual && + test_cmp expect actual +' + test_expect_success 'merge with conflicted --autostash changes' ' git reset --hard c1 && git merge-file file file.orig file.9y &&
Thanks,
Denton
Quoted 4 lines
Best Wishes
Phillip
Hi Denton
Quoted 6 lines · Denton Liu
Hi Phillip,
Sorry for the late reply. I'm back in school so I've been pretty busy lately.
On Tue, Dec 31, 2019 at 10:34:30AM +0000, Phillip Wood wrote:
Quoted 8 lines
Quoted 3 lines
For
merge --autostashI think we need to consider the interaction of--no-commitwith--autostashasstash popwill refuse to run if the merge modified any of the stashed paths.The only time when we run the
stash popis when we either commit the merge or abort it. With this in mind, what do you think of the following test cases? If they look good, I can squash them into the appropriate patch and send in a reroll.
Ah I misunderstood what happened with --autostash and --no-commit, the tests basically look fine (I've got one comment below).
One other thing - if the user runs git reset or git checkout <branch> then cleanup_branch_state() removes MERGE_HEAD, MERGE_MSG etc. If we're not already doing so then I think we should remove MERGE_AUTOSTASH as well or you can get into a situation where the user does something like
git merge --autostash <something> # results in conflicts git reset --hard <somewhere else>
git merge <something> # succeeds and confusingly pops previous stash
Running git reset doesn't make sense unless they want to discard the stashed changes as well. This is a difference with rebase where you cannot lose the stashed changes by running git reset, the only way they can get lost is by running rebase --quit. We could always add a warning about it throwing away the stashed changes in the future.
I still not keen on the name --autostash as it's not automatic. --stash would make more sense to me. We'd have to deprecate rebase --autostash in favor of rebase --stash to change it but it if we want to change the name it would be better now before adding --autostash to merge and pull - what do you think?
Quoted 11 lines
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index e0c8a0bad4..af5db58e16 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -727,6 +727,33 @@ test_expect_success 'conflicted merge with --autostash, --abort restores stash' test_cmp file.1 file ' +test_expect_success 'completed merge with --no-commit and --autostash' ' + git reset --hard c1 && + git merge-file file file.orig file.9 &&
Is this a complicated way of getting some unstaged changes so we can stash them or have I missed something?
Best Wishes
Phillip
Quoted 36 lines
+ git diff >expect && + git merge --no-commit --autostash c2 && + git stash show -p MERGE_AUTOSTASH >actual && + test_cmp expect actual && + git commit 2>err && + test_i18ngrep "Applied autostash." err && + git show HEAD:file >merge-result && + test_cmp result.1-5 merge-result && + test_cmp result.1-5-9 file +' + +test_expect_success 'aborted merge with --no-commit and --autostash' ' + git reset --hard c1 && + git merge-file file file.orig file.9 && + git diff >expect && + git merge --no-commit --autostash c2 && + git stash show -p MERGE_AUTOSTASH >actual && + test_cmp expect actual && + git merge --abort 2>err && + test_i18ngrep "Applied autostash." err && + git diff >actual && + test_cmp expect actual +' + test_expect_success 'merge with conflicted --autostash changes' ' git reset --hard c1 && git merge-file file file.orig file.9y &&Thanks,
Denton
Quoted 4 lines
Best Wishes
Phillip
Quoted 18 lines · Phillip Wood
Hi Denton
Quoted 6 lines · Denton Liu
Hi Phillip,
Sorry for the late reply. I'm back in school so I've been pretty busy lately.
On Tue, Dec 31, 2019 at 10:34:30AM +0000, Phillip Wood wrote:
Quoted 8 lines
Quoted 3 lines
For
merge --autostashI think we need to consider the interaction of--no-commitwith--autostashasstash popwill refuse to run if the merge modified any of the stashed paths.The only time when we run the
stash popis when we either commit the merge or abort it. With this in mind, what do you think of the following test cases? If they look good, I can squash them into the appropriate patch and send in a reroll.
Quoted 7 lines
Ah I misunderstood what happened with --autostash and --no-commit, the tests basically look fine (I've got one comment below).
One other thing - if the user runs
git resetorgit checkout <branch>thencleanup_branch_state()removesMERGE_HEAD,MERGE_MSGetc. If we're not already doing so then I think we should removeMERGE_AUTOSTASHas well or you can get into a situation where the user does something like
In cleanup_branch_state(), I insert apply_autostash() at the very end of the function so that the stash is popped whenever this is called.
Quoted 9 lines
git merge --autostash <something> # results in conflicts git reset --hard <somewhere else>
git merge <something> # succeeds and confusingly pops previous stashRunning
git resetdoesn'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 runninggit reset, the only way they can get lost is by runningrebase --quit. We could always add a warning about it throwing away the stashed changes in the future.
Currently, in rebase, if the stash cannot be popped cleanly, it is automatically pushed onto the stash stack so that the user can deal with it later. Do we want to do a similar thing where if we reset --hard with an autostash present, we store the stash and then leave the users with a clean worktree (as they'd expect)?
Quoted 5 lines
I still not keen on the name
--autostashas it's not automatic.--stashwould make more sense to me. We'd have to deprecaterebase --autostashin favor ofrebase --stashto change it but it if we want to change the name it would be better now before adding--autostashto merge and pull - what do you think?
Even though I agree with you that --autostash would be better named as --stash, I feel that it's worse than having argument names that perform the same functionality but with different names. So I'd be inclined to keep --autostash.
Quoted 9 lines
Quoted 9 lines
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index e0c8a0bad4..af5db58e16 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -727,6 +727,33 @@ test_expect_success 'conflicted merge with --autostash, --abort restores stash' test_cmp file.1 file '
Quoted 6 lines
Quoted 3 lines
+test_expect_success 'completed merge with --no-commit and --autostash' ' + git reset --hard c1 && + git merge-file file file.orig file.9 &&Is this a complicated way of getting some unstaged changes so we can stash them or have I missed something?
Yes, that's exactly what this does.
Quoted 41 lines
Best Wishes
Phillip
Quoted 36 lines
+ git diff >expect && + git merge --no-commit --autostash c2 && + git stash show -p MERGE_AUTOSTASH >actual && + test_cmp expect actual && + git commit 2>err && + test_i18ngrep "Applied autostash." err && + git show HEAD:file >merge-result && + test_cmp result.1-5 merge-result && + test_cmp result.1-5-9 file +' + +test_expect_success 'aborted merge with --no-commit and --autostash' ' + git reset --hard c1 && + git merge-file file file.orig file.9 && + git diff >expect && + git merge --no-commit --autostash c2 && + git stash show -p MERGE_AUTOSTASH >actual && + test_cmp expect actual && + git merge --abort 2>err && + test_i18ngrep "Applied autostash." err && + git diff >actual && + test_cmp expect actual +' + test_expect_success 'merge with conflicted --autostash changes' ' git reset --hard c1 && git merge-file file file.orig file.9y &&Thanks,
Denton
Quoted 4 lines
Best Wishes
Phillip
v3 cover merge: learn --autostash
In reply to Denton LiuSorry for the delay in rerolling this. It's been quite long since I've had free time. This probaly requires a fresh review since quite a lot has changed since I posted my last version.
Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.
The majority of this patchset is spent refactoring. This is so that the actual implementation in merge is done as simply as possible.
Changes since v2:
* Change patch 1 to refer to ASCII order[2]
* Make read_oneliner() accept a collective flags arg instead of two separate args[3]
* git reset --hard now leaves the worktree clean[4]
* Rebased against 'master' since the base is quite old and a new release is coming out soon
Changes since v1:
* Completely changed how the refactoring was done
* More tests and documentation
[1]: https://github.com/gitgitgadget/git/issues/394
[2]: https://lore.kernel.org/git/[email protected]/
[3]: https://lore.kernel.org/git/[email protected]/
[4]: https://lore.kernel.org/git/20200123042906.GA29009@generichostname/
Denton Liu (19):
Makefile: ASCII-sort += lists
t7600: use test_write_lines()
sequencer: use file strbuf for read_oneliner()
sequencer: make read_oneliner() accept flags
sequencer: configurably warn on non-existent files
sequencer: make read_oneliner() extern
rebase: use read_oneliner()
sequencer: make apply_rebase() accept a path
rebase: use apply_autostash() from sequencer.c
rebase: generify reset_head()
reset: extract reset_head() from rebase
rebase: extract create_autostash()
rebase: generify create_autostash()
sequencer: extract perform_autostash() from rebase
sequencer: unlink autostash in apply_autostash()
sequencer: implement save_autostash()
merge: teach --autostash option
t5520: make test_pull_autostash() accept expect_parent_num
pull: pass --autostash to merge
Documentation/config/merge.txt | 10 ++ Documentation/git-pull.txt | 9 - Documentation/merge-options.txt | 8 + Makefile | 78 ++++---- branch.c | 1 + builtin/commit.c | 2 + builtin/merge.c | 17 ++ builtin/pull.c | 9 +- builtin/rebase.c | 304 +++++--------------------------- builtin/reset.c | 7 +- path.c | 1 + path.h | 4 +- reset.c | 140 +++++++++++++++ reset.h | 20 +++ sequencer.c | 152 +++++++++++----- sequencer.h | 19 ++ t/t3033-merge-toplevel.sh | 22 +++ t/t5520-pull.sh | 57 ++++-- t/t7600-merge.sh | 126 +++++++++++-- 19 files changed, 604 insertions(+), 382 deletions(-) create mode 100644 reset.c create mode 100644 reset.h
Range-diff against v2:
1: 31055cdac4 ! 1: 77506a39d0 Makefile: alphabetically sort += lists
@@ Metadata
Author: Denton Liu <[email protected]>
## Commit message ##
- Makefile: alphabetically sort += lists
+ Makefile: ASCII-sort += lists
There are many += lists in the Makefile and, over time, they have gotten
- slightly out of order, alphabetically. Alphabetically sort all += lists
- to bring them back in order.
+ slightly out of ASCII order. Sort all += lists to bring them back in
+ order.
- Note that if we omit file prefixes, the lists aren't sorted in strictly
- alphabetical order (e.g. archive.o comes after archive-zip.o instead of
- before archive-tar.o). This is intentional because the purpose of
- maintaining the sorted list is to ensure line insertions are
- deterministic. As a result, we want to ensure that anybody can easily
- reproduce the sort order and that is very simple when we don't have to
- treat the prefix specially.
+ ASCII sorting was chosen over strict alphabetical order even though, if
+ we omit file prefixes, the lists aren't sorted in strictly alphabetical
+ order (e.g. archive.o comes after archive-zip.o instead of before
+ archive-tar.o). This is intentional because the purpose of maintaining
+ the sorted list is to ensure line insertions are deterministic. By using
+ ASCII ordering, it is more easily mechanically reproducible in the
+ future, such as by using :sort in Vim.
This patch is best viewed with --color-moved.
@@ Makefile: LIB_OBJS += mailinfo.o
LIB_OBJS += patch-delta.o
LIB_OBJS += patch-ids.o
LIB_OBJS += path.o
-@@ Makefile: LIB_OBJS += range-diff.o
+@@ Makefile: LIB_OBJS += quote.o
+ LIB_OBJS += range-diff.o
LIB_OBJS += reachable.o
LIB_OBJS += read-cache.o
+-LIB_OBJS += rebase.o
LIB_OBJS += rebase-interactive.o
++LIB_OBJS += rebase.o
+LIB_OBJS += ref-filter.o
LIB_OBJS += reflog-walk.o
LIB_OBJS += refs.o
2: 2e0020b3e3 = 2: 70699b8b73 t7600: use test_write_lines() 3: de79e27c17 = 3: 7c37777f07 sequencer: use file strbuf for read_oneliner() 4: fd547aab49 < -: ---------- sequencer: configurably warn on non-existent files -: ---------- > 4: 4f97086828 sequencer: make read_oneliner() accept flags -: ---------- > 5: e42f4bdd4d sequencer: configurably warn on non-existent files 5: 819fb2203b ! 6: e4d0c2d902 sequencer: make read_oneliner() extern
@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
return 0;
}
+-#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
+-#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
+-
-/*
- * Reads a file that was presumably written by a shell script, i.e. with an
- * end-of-line marker that needs to be stripped.
@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
- *
- * Returns 1 if the file was read, 0 if it could not be read or does not exist.
- */
--static int read_oneliner(struct strbuf *buf, const char *path,
-- int skip_if_empty, int warn_nonexistence)
-+int read_oneliner(struct strbuf *buf, const char *path,
-+ int skip_if_empty, int warn_nonexistence)
+-static int read_oneliner(struct strbuf *buf,
++int read_oneliner(struct strbuf *buf,
+ const char *path, unsigned flags)
{
int ret = 0;
- struct strbuf file_buf = STRBUF_INIT;
## sequencer.h ##
@@ sequencer.h: void print_commit_summary(struct repository *repo,
const struct object_id *oid, unsigned int flags);
++#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
++#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
++
+/*
+ * Reads a file that was presumably written by a shell script, i.e. with an
+ * end-of-line marker that needs to be stripped.
@@ sequencer.h: void print_commit_summary(struct repository *repo,
+ *
+ * Returns 1 if the file was read, 0 if it could not be read.
+ */
-+int read_oneliner(struct strbuf *buf, const char *path,
-+ int skip_if_empty, int warn_nonexistence);
++int read_oneliner(struct strbuf *buf,
++ const char *path, unsigned flags);
int read_author_script(const char *path, char **name, char **email, char **date,
int allow_missing);
void parse_strategy_opts(struct replay_opts *opts, char *raw_opts);
6: 06c5bd48d7 ! 7: d07a50c5b2 rebase: use read_oneliner()
@@ Commit message
of read_oneliner(), reduce code duplication by replacing read_one() with
read_oneliner().
- This was done with the following Coccinelle script:
+ This was done with the following Coccinelle script
@@
expression a, b;
@@
- read_one(a, b)
- + !read_oneliner(b, a, 0, 1)
+ + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)
+
+ and long lines were manually broken up.
## builtin/rebase.c ##
@@ builtin/rebase.c: static const char *state_dir_path(const char *filename, struct rebase_options *o
@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
- if (read_one(state_dir_path("head-name", opts), &head_name) ||
- read_one(state_dir_path("onto", opts), &buf))
-+ if (!read_oneliner(&head_name, state_dir_path("head-name", opts), 0, 1) ||
-+ !read_oneliner(&buf, state_dir_path("onto", opts), 0, 1))
++ if (!read_oneliner(&head_name, state_dir_path("head-name", opts),
++ READ_ONELINER_WARN_NON_EXISTENCE) ||
++ !read_oneliner(&buf, state_dir_path("onto", opts),
++ READ_ONELINER_WARN_NON_EXISTENCE))
return -1;
opts->head_name = starts_with(head_name.buf, "refs/") ?
xstrdup(head_name.buf) : NULL;
@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
strbuf_reset(&buf);
if (file_exists(state_dir_path("orig-head", opts))) {
- if (read_one(state_dir_path("orig-head", opts), &buf))
-+ if (!read_oneliner(&buf, state_dir_path("orig-head", opts), 0, 1))
++ if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
++ READ_ONELINER_WARN_NON_EXISTENCE))
return -1;
- } else if (read_one(state_dir_path("head", opts), &buf))
-+ } else if (!read_oneliner(&buf, state_dir_path("head", opts), 0, 1))
++ } else if (!read_oneliner(&buf, state_dir_path("head", opts),
++ READ_ONELINER_WARN_NON_EXISTENCE))
return -1;
if (get_oid(buf.buf, &opts->orig_head))
return error(_("invalid orig-head: '%s'"), buf.buf);
@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
strbuf_reset(&buf);
- if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
- &buf))
-+ if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts), 0, 1))
++ if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
++ READ_ONELINER_WARN_NON_EXISTENCE))
return -1;
if (!strcmp(buf.buf, "--rerere-autoupdate"))
opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
strbuf_reset(&buf);
- if (read_one(state_dir_path("gpg_sign_opt", opts),
- &buf))
-+ if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts), 0, 1))
++ if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
++ READ_ONELINER_WARN_NON_EXISTENCE))
return -1;
free(opts->gpg_sign_opt);
opts->gpg_sign_opt = xstrdup(buf.buf);
@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
if (file_exists(state_dir_path("strategy", opts))) {
strbuf_reset(&buf);
- if (read_one(state_dir_path("strategy", opts), &buf))
-+ if (!read_oneliner(&buf, state_dir_path("strategy", opts), 0, 1))
++ if (!read_oneliner(&buf, state_dir_path("strategy", opts),
++ READ_ONELINER_WARN_NON_EXISTENCE))
return -1;
free(opts->strategy);
opts->strategy = xstrdup(buf.buf);
@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
if (file_exists(state_dir_path("strategy_opts", opts))) {
strbuf_reset(&buf);
- if (read_one(state_dir_path("strategy_opts", opts), &buf))
-+ if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts), 0, 1))
++ if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
++ READ_ONELINER_WARN_NON_EXISTENCE))
return -1;
free(opts->strategy_opts);
opts->strategy_opts = xstrdup(buf.buf);
@@ builtin/rebase.c: static int apply_autostash(struct rebase_options *opts)
return 0;
- if (read_one(path, &autostash))
-+ if (!read_oneliner(&autostash, path, 0, 1))
++ if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
return error(_("Could not read '%s'"), path);
/* Ensure that the hash is not mistaken for a number */
strbuf_addstr(&autostash, "^0");
7: 12c37d11ba ! 8: 5bce4aeb96 sequencer: make apply_rebase() accept a path
@@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
struct child_process child = CHILD_PROCESS_INIT;
int ret = 0;
-- if (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1, 0)) {
-+ if (!read_oneliner(&stash_sha1, path, 1, 0)) {
+- if (!read_oneliner(&stash_sha1, rebase_path_autostash(),
++ if (!read_oneliner(&stash_sha1, path,
+ READ_ONELINER_SKIP_IF_EMPTY)) {
strbuf_release(&stash_sha1);
return 0;
- }
@@ sequencer.c: static int checkout_onto(struct repository *r, struct replay_opts *opts,
return error(_("%s: not a valid OID"), orig_head); 8: 893a2f9825 ! 9: 03bdaeebc9 rebase: use apply_autostash() from sequencer.c
@@ builtin/rebase.c: static int rebase_write_basic_state(struct rebase_options *opt
- if (!file_exists(path))
- return 0;
-
-- if (!read_oneliner(&autostash, path, 0, 1))
+- if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
- return error(_("Could not read '%s'"), path);
- /* Ensure that the hash is not mistaken for a number */
- strbuf_addstr(&autostash, "^0");
@@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
## sequencer.h ##
@@ sequencer.h: void commit_post_rewrite(struct repository *r,
- int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
- const char *commit);
+ const struct commit *current_head,
+ const struct object_id *new_head);
+int apply_autostash(const char *path);
+
9: 8e3c0c0aec ! 10: c35c5bdb8c rebase: generify reset_head()
@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix
strbuf_addf(&buf, "%s: checkout %s",
getenv(GIT_REFLOG_ACTION_ENVIRONMENT),
options.switch_to);
-- if (reset_head(&oid, "checkout",
-+ if (reset_head(the_repository, &oid, "checkout",
+- if (reset_head(&options.orig_head, "checkout",
++ if (reset_head(the_repository,
++ &options.orig_head, "checkout",
options.head_name,
RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
- NULL, buf.buf) < 0) {
10: 981969d230 = 11: a14a2e0849 reset: extract reset_head() from rebase
11: 1b5bbe66dc = 12: 1dbae3dfbf rebase: extract create_autostash()
12: bba7586cba = 13: fffc038cd6 rebase: generify create_autostash()
13: 2ff189692f ! 14: bf55b86e2b sequencer: extract perform_autostash() from rebase @@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
## sequencer.h ##
@@ sequencer.h: void commit_post_rewrite(struct repository *r,
- int prepare_branch_to_be_rebased(struct repository *r, struct replay_opts *opts,
- const char *commit);
+ const struct commit *current_head,
+ const struct object_id *new_head);
+void create_autostash(struct repository *r, const char *path,
+ const char *default_reflog_action);
14: 2b16711680 = 15: d5af261eb8 sequencer: unlink autostash in apply_autostash()
-: ---------- > 16: e21e2c6416 sequencer: implement save_autostash()
15: 6e987052c0 ! 17: 9e3d4393ca merge: teach --autostash option
@@ Commit message
git fetch ...
git merge --autostash FETCH_HEAD
+ When `git reset --hard` is run to abort a merge, the working tree will
+ be left in a clean state, as expected, with the autostash pushed onto
+ the stash stack.
+
Suggested-by: Alban Gruin <[email protected]>
## Documentation/config/merge.txt ##
@@ builtin/merge.c: static void finish(struct commit *head_commit,
squash_message(head_commit, remoteheads);
} else {
@@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void *cb)
- } else if (!strcmp(k, "commit.gpgsign")) {
- sign_commit = git_config_bool(k, v) ? "" : NULL;
return 0;
+ } else if (!strcmp(k, "gpg.mintrustlevel")) {
+ check_trust_level = 0;
+ } else if (!strcmp(k, "merge.autostash")) {
+ autostash = git_config_bool(k, v);
+ return 0;
@@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
git_committer_info(IDENT_STRICT);
+ ## builtin/reset.c ##
+@@
+ #include "cache-tree.h"
+ #include "submodule.h"
+ #include "submodule-config.h"
++#include "sequencer.h"
+
+ #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
+
+@@ builtin/reset.c: int cmd_reset(int argc, const char **argv, const char *prefix)
+ if (reset_type == HARD && !update_ref_status && !quiet)
+ print_new_head_line(lookup_commit_reference(the_repository, &oid));
+ }
+- if (!pathspec.nr)
++ if (!pathspec.nr) {
++ if (reset_type == HARD)
++ save_autostash(git_path_merge_autostash(the_repository));
++
+ remove_branch_state(the_repository, 0);
++ }
+
+ return update_ref_status;
+ }
+
## path.c ##
@@ path.c: REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
@@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '
+ test_cmp file.1 file
+'
+
++test_expect_success 'completed merge with --no-commit and --autostash' '
++ git reset --hard c1 &&
++ git merge-file file file.orig file.9 &&
++ git diff >expect &&
++ git merge --no-commit --autostash c2 &&
++ git stash show -p MERGE_AUTOSTASH >actual &&
++ test_cmp expect actual &&
++ git commit 2>err &&
++ test_i18ngrep "Applied autostash." err &&
++ git show HEAD:file >merge-result &&
++ test_cmp result.1-5 merge-result &&
++ test_cmp result.1-5-9 file
++'
++
++test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '
++ git reset --hard c1 &&
++ git merge-file file file.orig file.9 &&
++ git diff >expect &&
++ git merge --no-commit --autostash c2 &&
++ git stash show -p MERGE_AUTOSTASH >actual &&
++ test_cmp expect actual &&
++ git merge --abort 2>err &&
++ test_i18ngrep "Applied autostash." err &&
++ git diff >actual &&
++ test_cmp expect actual
++'
++
++test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '
++ git reset --hard c1 &&
++ git merge-file file file.orig file.9 &&
++ git diff >expect &&
++ git merge --no-commit --autostash c2 &&
++ git stash show -p MERGE_AUTOSTASH >actual &&
++ test_cmp expect actual &&
++ git reset --hard 2>err &&
++ test_i18ngrep "Autostash exists; creating a new stash entry." err &&
++ git diff --exit-code
++'
++
+test_expect_success 'merge with conflicted --autostash changes' '
+ git reset --hard c1 &&
+ git merge-file file file.orig file.9y &&
16: c70643ca9f = 18: 72aa56516e t5520: make test_pull_autostash() accept expect_parent_num
17: 6ebae1d758 = 19: 17caf6d66f pull: pass --autostash to mergeSignature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 1/19 Makefile: ASCII-sort += lists
There are many += lists in the Makefile and, over time, they have gotten slightly out of ASCII order. Sort all += lists to bring them back in order.
ASCII sorting was chosen over strict alphabetical order even though, if we omit file prefixes, the lists aren't sorted in strictly alphabetical order (e.g. archive.o comes after archive-zip.o instead of before archive-tar.o). This is intentional because the purpose of maintaining the sorted list is to ensure line insertions are deterministic. By using ASCII ordering, it is more easily mechanically reproducible in the future, such as by using :sort in Vim.
This patch is best viewed with --color-moved.
--- Makefile | 77 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 38 deletions(-)
Makefile +39 -38
@@ -604,12 +604,12 @@ unexport CDPATH SCRIPT_SH += git-bisect.sh SCRIPT_SH += git-difftool--helper.sh SCRIPT_SH += git-filter-branch.sh +SCRIPT_SH += git-legacy-stash.sh SCRIPT_SH += git-merge-octopus.sh SCRIPT_SH += git-merge-one-file.sh SCRIPT_SH += git-merge-resolve.sh SCRIPT_SH += git-mergetool.sh SCRIPT_SH += git-quiltimport.sh -SCRIPT_SH += git-legacy-stash.sh SCRIPT_SH += git-request-pull.sh SCRIPT_SH += git-submodule.sh SCRIPT_SH += git-web--browse.sh
@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh SCRIPT_LIB += git-mergetool--lib SCRIPT_LIB += git-parse-remote SCRIPT_LIB += git-rebase--preserve-merges -SCRIPT_LIB += git-sh-setup SCRIPT_LIB += git-sh-i18n +SCRIPT_LIB += git-sh-setup SCRIPT_PERL += git-add--interactive.perl SCRIPT_PERL += git-archimport.perl
@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o PROGRAM_OBJS += fast-import.o PROGRAM_OBJS += http-backend.o PROGRAM_OBJS += imap-send.o +PROGRAM_OBJS += remote-testsvn.o PROGRAM_OBJS += sh-i18n--envsubst.o PROGRAM_OBJS += shell.o -PROGRAM_OBJS += remote-testsvn.o # Binary suffix, set to .exe for Windows builds X =
@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o TEST_BUILTINS_OBJS += test-example-decorate.o TEST_BUILTINS_OBJS += test-genrandom.o TEST_BUILTINS_OBJS += test-genzeros.o +TEST_BUILTINS_OBJS += test-hash-speed.o TEST_BUILTINS_OBJS += test-hash.o TEST_BUILTINS_OBJS += test-hashmap.o -TEST_BUILTINS_OBJS += test-hash-speed.o TEST_BUILTINS_OBJS += test-index-version.o TEST_BUILTINS_OBJS += test-json-writer.o TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@ -737,8 +737,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o TEST_BUILTINS_OBJS += test-run-command.o TEST_BUILTINS_OBJS += test-scrap-cache-tree.o TEST_BUILTINS_OBJS += test-serve-v2.o -TEST_BUILTINS_OBJS += test-sha1.o TEST_BUILTINS_OBJS += test-sha1-array.o +TEST_BUILTINS_OBJS += test-sha1.o TEST_BUILTINS_OBJS += test-sha256.o TEST_BUILTINS_OBJS += test-sigchain.o TEST_BUILTINS_OBJS += test-strcmp-offset.o
@@ -748,10 +748,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o TEST_BUILTINS_OBJS += test-subprocess.o TEST_BUILTINS_OBJS += test-trace2.o TEST_BUILTINS_OBJS += test-urlmatch-normalization.o -TEST_BUILTINS_OBJS += test-xml-encode.o TEST_BUILTINS_OBJS += test-wildmatch.o TEST_BUILTINS_OBJS += test-windows-named-pipe.o TEST_BUILTINS_OBJS += test-write-cache.o +TEST_BUILTINS_OBJS += test-xml-encode.o # Do not add more tests here unless they have extra dependencies. Add # them in TEST_BUILTINS_OBJS above.
@@ -788,10 +788,10 @@ OTHER_PROGRAMS = git$X # what test wrappers are needed and 'install' will install, in bindir BINDIR_PROGRAMS_NEED_X += git -BINDIR_PROGRAMS_NEED_X += git-upload-pack BINDIR_PROGRAMS_NEED_X += git-receive-pack -BINDIR_PROGRAMS_NEED_X += git-upload-archive BINDIR_PROGRAMS_NEED_X += git-shell +BINDIR_PROGRAMS_NEED_X += git-upload-archive +BINDIR_PROGRAMS_NEED_X += git-upload-pack BINDIR_PROGRAMS_NO_X += git-cvsserver
@@ -831,9 +831,9 @@ LIB_OBJS += advice.o LIB_OBJS += alias.o LIB_OBJS += alloc.o LIB_OBJS += apply.o -LIB_OBJS += archive.o LIB_OBJS += archive-tar.o LIB_OBJS += archive-zip.o +LIB_OBJS += archive.o LIB_OBJS += argv-array.o LIB_OBJS += attr.o LIB_OBJS += base85.o
@@ -849,9 +849,9 @@ LIB_OBJS += checkout.o LIB_OBJS += color.o LIB_OBJS += column.o LIB_OBJS += combine-diff.o -LIB_OBJS += commit.o LIB_OBJS += commit-graph.o LIB_OBJS += commit-reach.o +LIB_OBJS += commit.o LIB_OBJS += compat/obstack.o LIB_OBJS += compat/terminal.o LIB_OBJS += config.o
@@ -865,17 +865,17 @@ LIB_OBJS += ctype.o LIB_OBJS += date.o LIB_OBJS += decorate.o LIB_OBJS += delta-islands.o +LIB_OBJS += diff-delta.o +LIB_OBJS += diff-lib.o +LIB_OBJS += diff-no-index.o +LIB_OBJS += diff.o LIB_OBJS += diffcore-break.o LIB_OBJS += diffcore-delta.o LIB_OBJS += diffcore-order.o LIB_OBJS += diffcore-pickaxe.o LIB_OBJS += diffcore-rename.o -LIB_OBJS += diff-delta.o -LIB_OBJS += diff-lib.o -LIB_OBJS += diff-no-index.o -LIB_OBJS += diff.o -LIB_OBJS += dir.o LIB_OBJS += dir-iterator.o +LIB_OBJS += dir.o LIB_OBJS += editor.o LIB_OBJS += entry.o LIB_OBJS += environment.o
@@ -893,7 +893,6 @@ LIB_OBJS += gpg-interface.o LIB_OBJS += graph.o LIB_OBJS += grep.o LIB_OBJS += hashmap.o -LIB_OBJS += linear-assignment.o LIB_OBJS += help.o LIB_OBJS += hex.o LIB_OBJS += ident.o
@@ -903,9 +902,10 @@ LIB_OBJS += kwset.o LIB_OBJS += levenshtein.o LIB_OBJS += line-log.o LIB_OBJS += line-range.o -LIB_OBJS += list-objects.o -LIB_OBJS += list-objects-filter.o +LIB_OBJS += linear-assignment.o LIB_OBJS += list-objects-filter-options.o +LIB_OBJS += list-objects-filter.o +LIB_OBJS += list-objects.o LIB_OBJS += ll-merge.o LIB_OBJS += lockfile.o LIB_OBJS += log-tree.o
@@ -914,31 +914,31 @@ LIB_OBJS += mailinfo.o LIB_OBJS += mailmap.o LIB_OBJS += match-trees.o LIB_OBJS += mem-pool.o -LIB_OBJS += merge.o LIB_OBJS += merge-blobs.o LIB_OBJS += merge-recursive.o +LIB_OBJS += merge.o LIB_OBJS += mergesort.o LIB_OBJS += midx.o LIB_OBJS += name-hash.o LIB_OBJS += negotiator/default.o LIB_OBJS += negotiator/skipping.o -LIB_OBJS += notes.o LIB_OBJS += notes-cache.o LIB_OBJS += notes-merge.o LIB_OBJS += notes-utils.o +LIB_OBJS += notes.o LIB_OBJS += object.o LIB_OBJS += oidmap.o LIB_OBJS += oidset.o -LIB_OBJS += packfile.o -LIB_OBJS += pack-bitmap.o LIB_OBJS += pack-bitmap-write.o +LIB_OBJS += pack-bitmap.o LIB_OBJS += pack-check.o LIB_OBJS += pack-objects.o LIB_OBJS += pack-revindex.o LIB_OBJS += pack-write.o +LIB_OBJS += packfile.o LIB_OBJS += pager.o -LIB_OBJS += parse-options.o LIB_OBJS += parse-options-cb.o +LIB_OBJS += parse-options.o LIB_OBJS += patch-delta.o LIB_OBJS += patch-ids.o LIB_OBJS += path.o
@@ -955,8 +955,9 @@ LIB_OBJS += quote.o LIB_OBJS += range-diff.o LIB_OBJS += reachable.o LIB_OBJS += read-cache.o -LIB_OBJS += rebase.o LIB_OBJS += rebase-interactive.o +LIB_OBJS += rebase.o +LIB_OBJS += ref-filter.o LIB_OBJS += reflog-walk.o LIB_OBJS += refs.o LIB_OBJS += refs/files-backend.o
@@ -964,7 +965,6 @@ LIB_OBJS += refs/iterator.o LIB_OBJS += refs/packed-backend.o LIB_OBJS += refs/ref-cache.o LIB_OBJS += refspec.o -LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace-object.o LIB_OBJS += repo-settings.o
@@ -979,8 +979,8 @@ LIB_OBJS += serve.o LIB_OBJS += server-info.o LIB_OBJS += setup.o LIB_OBJS += sha1-array.o -LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1-file.o +LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1-name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o
@@ -990,9 +990,9 @@ LIB_OBJS += stable-qsort.o LIB_OBJS += strbuf.o LIB_OBJS += streaming.o LIB_OBJS += string-list.o -LIB_OBJS += submodule.o -LIB_OBJS += submodule-config.o LIB_OBJS += sub-process.o +LIB_OBJS += submodule-config.o +LIB_OBJS += submodule.o LIB_OBJS += symlinks.o LIB_OBJS += tag.o LIB_OBJS += tempfile.o
@@ -1011,11 +1011,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o LIB_OBJS += trace2/tr2_tgt_perf.o LIB_OBJS += trace2/tr2_tls.o LIB_OBJS += trailer.o -LIB_OBJS += transport.o LIB_OBJS += transport-helper.o +LIB_OBJS += transport.o LIB_OBJS += tree-diff.o -LIB_OBJS += tree.o LIB_OBJS += tree-walk.o +LIB_OBJS += tree.o LIB_OBJS += unpack-trees.o LIB_OBJS += upload-pack.o LIB_OBJS += url.o
@@ -1055,9 +1055,9 @@ BUILTIN_OBJS += builtin/checkout.o BUILTIN_OBJS += builtin/clean.o BUILTIN_OBJS += builtin/clone.o BUILTIN_OBJS += builtin/column.o +BUILTIN_OBJS += builtin/commit-graph.o BUILTIN_OBJS += builtin/commit-tree.o BUILTIN_OBJS += builtin/commit.o -BUILTIN_OBJS += builtin/commit-graph.o BUILTIN_OBJS += builtin/config.o BUILTIN_OBJS += builtin/count-objects.o BUILTIN_OBJS += builtin/credential.o
@@ -1088,13 +1088,13 @@ BUILTIN_OBJS += builtin/ls-remote.o BUILTIN_OBJS += builtin/ls-tree.o BUILTIN_OBJS += builtin/mailinfo.o BUILTIN_OBJS += builtin/mailsplit.o -BUILTIN_OBJS += builtin/merge.o BUILTIN_OBJS += builtin/merge-base.o BUILTIN_OBJS += builtin/merge-file.o BUILTIN_OBJS += builtin/merge-index.o BUILTIN_OBJS += builtin/merge-ours.o BUILTIN_OBJS += builtin/merge-recursive.o BUILTIN_OBJS += builtin/merge-tree.o +BUILTIN_OBJS += builtin/merge.o BUILTIN_OBJS += builtin/mktag.o BUILTIN_OBJS += builtin/mktree.o BUILTIN_OBJS += builtin/multi-pack-index.o
@@ -1114,9 +1114,9 @@ BUILTIN_OBJS += builtin/read-tree.o BUILTIN_OBJS += builtin/rebase.o BUILTIN_OBJS += builtin/receive-pack.o BUILTIN_OBJS += builtin/reflog.o -BUILTIN_OBJS += builtin/remote.o BUILTIN_OBJS += builtin/remote-ext.o BUILTIN_OBJS += builtin/remote-fd.o +BUILTIN_OBJS += builtin/remote.o BUILTIN_OBJS += builtin/repack.o BUILTIN_OBJS += builtin/replace.o BUILTIN_OBJS += builtin/rerere.o
@@ -2335,16 +2335,16 @@ reconfigure config.mak.autogen: config.status endif XDIFF_OBJS += xdiff/xdiffi.o -XDIFF_OBJS += xdiff/xprepare.o -XDIFF_OBJS += xdiff/xutils.o XDIFF_OBJS += xdiff/xemit.o +XDIFF_OBJS += xdiff/xhistogram.o XDIFF_OBJS += xdiff/xmerge.o XDIFF_OBJS += xdiff/xpatience.o -XDIFF_OBJS += xdiff/xhistogram.o +XDIFF_OBJS += xdiff/xprepare.o +XDIFF_OBJS += xdiff/xutils.o +VCSSVN_OBJS += vcs-svn/fast_export.o VCSSVN_OBJS += vcs-svn/line_buffer.o VCSSVN_OBJS += vcs-svn/sliding_window.o -VCSSVN_OBJS += vcs-svn/fast_export.o VCSSVN_OBJS += vcs-svn/svndiff.o VCSSVN_OBJS += vcs-svn/svndump.o
@@ -3152,9 +3152,10 @@ endif # ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) ALL_COMMANDS += git +ALL_COMMANDS += git-citool +ALL_COMMANDS += git-gui ALL_COMMANDS += gitk ALL_COMMANDS += gitweb -ALL_COMMANDS += git-gui git-citool .PHONY: check-docs check-docs::
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 2/19 t7600: use test_write_lines()
In reply to Denton LiuIn t7600, we were rewriting printf '%s\n' ... to create files from parameters, one per line. However, we already have a function that wraps this for us: test_write_lines(). Rewrite these instances to use that function instead of open coding it.
--- t/t7600-merge.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-)
t/t7600-merge.sh +11 -11
@@ -29,15 +29,15 @@ Testing basic merge operations/option parsing. . ./test-lib.sh . "$TEST_DIRECTORY"/lib-gpg.sh -printf '%s\n' 1 2 3 4 5 6 7 8 9 >file -printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >file.1 -printf '%s\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5 -printf '%s\n' 1 2 3 4 5 6 7 8 '9 X' >file.9 -printf '%s\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y -printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >result.1 -printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5 -printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9 -printf '%s\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z +test_write_lines 1 2 3 4 5 6 7 8 9 >file +test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1 +test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5 +test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9 +test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y +test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1 +test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5 +test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9 +test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z create_merge_msgs () { echo "Merge tag 'c2'" >msg.1-5 &&
@@ -81,7 +81,7 @@ verify_head () { } verify_parents () { - printf '%s\n' "$@" >parents.expected && + test_write_lines "$@" >parents.expected && >parents.actual && i=1 && while test $i -le $#
@@ -95,7 +95,7 @@ verify_parents () { } verify_mergeheads () { - printf '%s\n' "$@" >mergehead.expected && + test_write_lines "$@" >mergehead.expected && while read sha1 rest do git rev-parse $sha1
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 3/19 sequencer: use file strbuf for read_oneliner()
In reply to Denton LiuIn the original read_oneliner logic, we duplicated the logic for strbuf_trim_trailing_newline() with one exception: instead of preventing buffer accesses below index 0, it would prevent buffer accesses below index orig_len. Although this is correct, it isn't worth having the duplicated logic around.
Add a second strbuf to which files are read and run strbuf_trim_trailing_newline() directly on this strbuf then concatenate this strbuf with the argument strbuf at the end of the function. The function's external behaviour is unchanged.
--- sequencer.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-)
sequencer.c +13 -11
@@ -431,26 +431,28 @@ static int write_message(const void *buf, size_t len, const char *filename,static int read_oneliner (struct strbuf *buf,const char *path,int skip_if_empty) { -int orig_len = buf->len; +int ret =0 ; +struct strbuf file_buf =STRBUF_INIT ;if (!file_exists (path))return 0 ; -if (strbuf_read_file (buf, path,0 ) <0 ) { +if (strbuf_read_file (&file_buf, path,0 ) <0 ) {warning_errno (_ ("could not read '%s'" ), path); -return 0 ; + goto done; } -if (buf->len > orig_len && buf->buf[buf->len -1 ] =='\n' ) { -if (--buf->len > orig_len && buf->buf[buf->len -1 ] =='\r' ) - --buf->len; - buf->buf[buf->len] ='\0' ; - } +strbuf_trim_trailing_newline (&file_buf); -if (skip_if_empty && buf->len == orig_len) -return 0 ; +if (skip_if_empty && !file_buf.len) + goto done; -return 1 ; +strbuf_addbuf (buf, &file_buf); + ret =1 ; + +done: +strbuf_release (&file_buf); +return ret; }static struct tree *empty_tree (struct repository *r)
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 4/19 sequencer: make read_oneliner() accept flags
In reply to Denton LiuIn a future commit, we will need read_oneliner() to accept flags other than just skip_if_empty. Instead of having an argument for each flag, teach read_oneliner() to accept the bitfield flags instead. For now, only recognize the READ_ONELINER_SKIP_IF_EMPTY flag. More flags will be added in a future commit.
The result of this is that parallel topics which introduce invocations of read_oneliner() will still be compatible with this new function signature since, instead of passing 1 or 0 for skip_if_empty, they'll be passing 1 or 0 to flags, which gives equivalent behavior.
Mechanically fix up invocations of read_oneliner() with the following spatch
@@ expression a, b; @@ read_oneliner(a, b, - 1 + READ_ONELINER_SKIP_IF_EMPTY )
and manually break up long lines in the result.
--- sequencer.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-)
sequencer.c +14 -7
@@ -419,6 +419,8 @@ static int write_message(const void *buf, size_t len, const char *filename,return 0 ; } +#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0) +/* * Reads a file that was presumably written by a shell script, i.e. with an * end-of-line marker that needs to be stripped.
@@ -429,7 +431,7 @@ static int write_message(const void *buf, size_t len, const char *filename,* Returns 1 if the file was read, 0 if it could not be read or does not exist. */ static int read_oneliner (struct strbuf *buf, -const char *path,int skip_if_empty) +const char *path,unsigned flags) {int ret =0 ;struct strbuf file_buf =STRBUF_INIT ;
@@ -444,7 +446,7 @@ static int read_oneliner(struct strbuf *buf,strbuf_trim_trailing_newline (&file_buf); -if (skip_if_empty && !file_buf.len) +if ((flags &READ_ONELINER_SKIP_IF_EMPTY ) && !file_buf.len) goto done;strbuf_addbuf (buf, &file_buf);
@@ -2488,7 +2490,8 @@ static int read_populate_opts(struct replay_opts *opts)if (is_rebase_i (opts)) {struct strbuf buf =STRBUF_INIT ; -if (read_oneliner (&buf,rebase_path_gpg_sign_opt (),1 )) { +if (read_oneliner (&buf,rebase_path_gpg_sign_opt (), +READ_ONELINER_SKIP_IF_EMPTY )) {if (!starts_with (buf.buf,"-S" ))strbuf_reset (&buf);else {
@@ -2498,7 +2501,8 @@ static int read_populate_opts(struct replay_opts *opts)strbuf_reset (&buf); } -if (read_oneliner (&buf,rebase_path_allow_rerere_autoupdate (),1 )) { +if (read_oneliner (&buf,rebase_path_allow_rerere_autoupdate (), +READ_ONELINER_SKIP_IF_EMPTY )) {if (!strcmp (buf.buf,"--rerere-autoupdate" )) opts->allow_rerere_auto =RERERE_AUTOUPDATE ;else if (!strcmp (buf.buf,"--no-rerere-autoupdate" ))
@@ -2530,7 +2534,8 @@ static int read_populate_opts(struct replay_opts *opts)strbuf_release (&buf);if (read_oneliner (&opts->current_fixups, -rebase_path_current_fixups (),1 )) { +rebase_path_current_fixups (), +READ_ONELINER_SKIP_IF_EMPTY )) {const char *p = opts->current_fixups.buf; opts->current_fixup_count =1 ;while ((p =strchr (p,'\n' ))) {
@@ -3667,7 +3672,8 @@ static int apply_autostash(struct replay_opts *opts)struct child_process child =CHILD_PROCESS_INIT ;int ret =0 ; -if (!read_oneliner (&stash_sha1,rebase_path_autostash (),1 )) { +if (!read_oneliner (&stash_sha1,rebase_path_autostash (), +READ_ONELINER_SKIP_IF_EMPTY )) {strbuf_release (&stash_sha1);return 0 ; }
@@ -4291,7 +4297,8 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)struct strbuf buf =STRBUF_INIT ;struct object_id oid; -if (read_oneliner (&buf,rebase_path_stopped_sha (),1 ) && +if (read_oneliner (&buf,rebase_path_stopped_sha (), +READ_ONELINER_SKIP_IF_EMPTY ) && !get_oid_committish (buf.buf, &oid))record_in_rewritten (&oid,peek_command (&todo_list,0 ));strbuf_release (&buf);
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 5/19 sequencer: configurably warn on non-existent files
In reply to Denton LiuIn the future, we plan on externing read_oneliner(). Future users of read_oneliner() will want the ability to output warnings in the event that the path doesn't exist. Introduce the READ_ONELINER_WARN_NON_EXISTENCE flag which, if active, would issue a warning when a file doesn't exist by skipping the !file_exists() check and letting strbuf_read_file() handle that case.
--- sequencer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
sequencer.c +2 -1
@@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename, }#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0) +#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)/* * Reads a file that was presumably written by a shell script, i.e. with an
@@ -436,7 +437,7 @@ static int read_oneliner(struct strbuf *buf,int ret =0 ;struct strbuf file_buf =STRBUF_INIT ; -if (!file_exists (path)) +if (!(flags &READ_ONELINER_WARN_NON_EXISTENCE ) && !file_exists (path))return 0 ;if (strbuf_read_file (&file_buf, path,0 ) <0 ) {
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 6/19 sequencer: make read_oneliner() extern
In reply to Denton LiuThe function read_oneliner() is a generally useful util function. Instead of hiding it as a static function within sequencer.c, extern it so that it can be reused by others.
--- sequencer.c | 14 +------------- sequencer.h | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 13 deletions(-)
sequencer.c +1 -13
@@ -419,19 +419,7 @@ static int write_message(const void *buf, size_t len, const char *filename,return 0 ; } -#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0) -#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1) - -/* -* Reads a file that was presumably written by a shell script, i.e. with an -* end-of-line marker that needs to be stripped. -* -* Note that only the last end-of-line marker is stripped, consistent with the -* behavior of "$(cat path)" in a shell script. -* -* Returns 1 if the file was read, 0 if it could not be read or does not exist. -*/ -static int read_oneliner (struct strbuf *buf, +int read_oneliner (struct strbuf *buf,const char *path,unsigned flags) {int ret =0 ;
sequencer.h +14 -0
@@ -198,6 +198,20 @@ void print_commit_summary(struct repository *repo,const struct object_id *oid, unsigned int flags); +#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0) +#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1) + +/* +* Reads a file that was presumably written by a shell script, i.e. with an +* end-of-line marker that needs to be stripped. +* +* Note that only the last end-of-line marker is stripped, consistent with the +* behavior of "$(cat path)" in a shell script. +* +* Returns 1 if the file was read, 0 if it could not be read. +*/ +int read_oneliner (struct strbuf *buf, +const char *path,unsigned flags);int read_author_script (const char *path,char **name,char **email,char **date,int allow_missing);void parse_strategy_opts (struct replay_opts *opts,char *raw_opts);
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 7/19 rebase: use read_oneliner()
In reply to Denton LiuSince in sequencer.c, read_one() basically duplicates the functionality of read_oneliner(), reduce code duplication by replacing read_one() with read_oneliner().
This was done with the following Coccinelle script
@@ expression a, b; @@ - read_one(a, b) + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)
and long lines were manually broken up.
--- builtin/rebase.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-)
builtin/rebase.c +17 -20
@@ -586,15 +586,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *oreturn path.buf; } -/* Read one file, then strip line endings */ -static int read_one (const char *path,struct strbuf *buf) -{ -if (strbuf_read_file (buf, path,0 ) <0 ) -return error_errno (_ ("could not read '%s'" ), path); -strbuf_trim_trailing_newline (buf); -return 0 ; -} -/* Initialize the rebase options from the state directory. */ static int read_basic_state (struct rebase_options *opts) {
@@ -602,8 +593,10 @@ static int read_basic_state(struct rebase_options *opts)struct strbuf buf =STRBUF_INIT ;struct object_id oid; -if (read_one (state_dir_path ("head-name" , opts), &head_name) || -read_one (state_dir_path ("onto" , opts), &buf)) +if (!read_oneliner (&head_name,state_dir_path ("head-name" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ) || + !read_oneliner (&buf,state_dir_path ("onto" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ; opts->head_name =starts_with (head_name.buf,"refs/" ) ?xstrdup (head_name.buf) :NULL ;
@@ -619,9 +612,11 @@ static int read_basic_state(struct rebase_options *opts)*/ strbuf_reset (&buf);if (file_exists (state_dir_path ("orig-head" , opts))) { -if (read_one (state_dir_path ("orig-head" , opts), &buf)) +if (!read_oneliner (&buf,state_dir_path ("orig-head" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ; - }else if (read_one (state_dir_path ("head" , opts), &buf)) + }else if (!read_oneliner (&buf,state_dir_path ("head" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ;if (get_oid (buf.buf, &opts->orig_head))return error (_ ("invalid orig-head: '%s'" ), buf.buf);
@@ -641,8 +636,8 @@ static int read_basic_state(struct rebase_options *opts)if (file_exists (state_dir_path ("allow_rerere_autoupdate" , opts))) {strbuf_reset (&buf); -if (read_one (state_dir_path ("allow_rerere_autoupdate" , opts), - &buf)) +if (!read_oneliner (&buf,state_dir_path ("allow_rerere_autoupdate" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ;if (!strcmp (buf.buf,"--rerere-autoupdate" )) opts->allow_rerere_autoupdate =RERERE_AUTOUPDATE ;
@@ -655,8 +650,8 @@ static int read_basic_state(struct rebase_options *opts)if (file_exists (state_dir_path ("gpg_sign_opt" , opts))) {strbuf_reset (&buf); -if (read_one (state_dir_path ("gpg_sign_opt" , opts), - &buf)) +if (!read_oneliner (&buf,state_dir_path ("gpg_sign_opt" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ;free (opts->gpg_sign_opt); opts->gpg_sign_opt =xstrdup (buf.buf);
@@ -664,7 +659,8 @@ static int read_basic_state(struct rebase_options *opts)if (file_exists (state_dir_path ("strategy" , opts))) {strbuf_reset (&buf); -if (read_one (state_dir_path ("strategy" , opts), &buf)) +if (!read_oneliner (&buf,state_dir_path ("strategy" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ;free (opts->strategy); opts->strategy =xstrdup (buf.buf);
@@ -672,7 +668,8 @@ static int read_basic_state(struct rebase_options *opts)if (file_exists (state_dir_path ("strategy_opts" , opts))) {strbuf_reset (&buf); -if (read_one (state_dir_path ("strategy_opts" , opts), &buf)) +if (!read_oneliner (&buf,state_dir_path ("strategy_opts" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ;free (opts->strategy_opts); opts->strategy_opts =xstrdup (buf.buf);
@@ -724,7 +721,7 @@ static int apply_autostash(struct rebase_options *opts)if (!file_exists (path))return 0 ; -if (read_one (path, &autostash)) +if (!read_oneliner (&autostash, path,READ_ONELINER_WARN_NON_EXISTENCE ))return error (_ ("Could not read '%s'" ), path);/* Ensure that the hash is not mistaken for a number */ strbuf_addstr (&autostash,"^0" );
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 8/19 sequencer: make apply_rebase() accept a path
In reply to Denton LiuIn order to make apply_rebase() more generic for future extraction, make it accept a path argument so that the location from where to read the reference to the autostash commit can be customized. Remove the opts argument since it was unused before anyway.
--- sequencer.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
sequencer.c +7 -7
@@ -3655,13 +3655,13 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)return -1 ; } -static int apply_autostash (struct replay_opts *opts) +static int apply_autostash (const char *path) {struct strbuf stash_sha1 =STRBUF_INIT ;struct child_process child =CHILD_PROCESS_INIT ;int ret =0 ; -if (!read_oneliner (&stash_sha1,rebase_path_autostash (), +if (!read_oneliner (&stash_sha1, path,READ_ONELINER_SKIP_IF_EMPTY )) {strbuf_release (&stash_sha1);return 0 ;
@@ -3754,7 +3754,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,return error (_ ("%s: not a valid OID" ), orig_head);if (run_git_checkout (r, opts,oid_to_hex (onto), action)) { -apply_autostash (opts); +apply_autostash (rebase_path_autostash ());sequencer_remove_state (opts);return error (_ ("could not detach HEAD" )); }
@@ -4068,7 +4068,7 @@ static int pick_commits(struct repository *r,run_command (&hook); } } -apply_autostash (opts); +apply_autostash (rebase_path_autostash ());if (!opts->quiet) {if (!opts->verbose)
@@ -5077,7 +5077,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned flatodo_list_add_exec_commands (todo_list, commands);if (count_commands (todo_list) ==0 ) { -apply_autostash (opts); +apply_autostash (rebase_path_autostash ());sequencer_remove_state (opts);return error (_ ("nothing to do" ));
@@ -5088,12 +5088,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned flaif (res ==-1 )return -1 ;else if (res ==-2 ) { -apply_autostash (opts); +apply_autostash (rebase_path_autostash ());sequencer_remove_state (opts);return -1 ; }else if (res ==-3 ) { -apply_autostash (opts); +apply_autostash (rebase_path_autostash ());sequencer_remove_state (opts);todo_list_release (&new_todo);
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 9/19 rebase: use apply_autostash() from sequencer.c
In reply to Denton LiuThe apply_autostash() function in builtin/rebase.c is similar enough to the apply_autostash() function in sequencer.c that they are almost interchangeable. Make the sequencer.c version extern and use it in rebase.
The rebase version was introduced in 6defce2b02 (builtin rebase: support --autostash option, 2018-09-04) as part of the shell to C conversion. It opted to duplicate the function because, at the time, there was another in-progress project converting interactive rebase from shell to C as well and they did not want to clash with them by refactoring sequencer.c version of apply_autostash(). Since both efforts are long done, we can freely combine them together now.
--- builtin/rebase.c | 49 ++---------------------------------------------- sequencer.c | 2 +- sequencer.h | 2 ++ 3 files changed, 5 insertions(+), 48 deletions(-)
builtin/rebase.c +2 -47
@@ -712,51 +712,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)return 0 ; } -static int apply_autostash (struct rebase_options *opts) -{ -const char *path =state_dir_path ("autostash" , opts); -struct strbuf autostash =STRBUF_INIT ; -struct child_process stash_apply =CHILD_PROCESS_INIT ; - -if (!file_exists (path)) -return 0 ; - -if (!read_oneliner (&autostash, path,READ_ONELINER_WARN_NON_EXISTENCE )) -return error (_ ("Could not read '%s'" ), path); -/* Ensure that the hash is not mistaken for a number */ -strbuf_addstr (&autostash,"^0" ); -argv_array_pushl (&stash_apply.args, -"stash" ,"apply" , autostash.buf,NULL ); - stash_apply.git_cmd =1 ; - stash_apply.no_stderr = stash_apply.no_stdout = - stash_apply.no_stdin =1 ; -if (!run_command (&stash_apply)) -printf (_ ("Applied autostash.\n" )); -else { -struct argv_array args =ARGV_ARRAY_INIT ; -int res =0 ; - -argv_array_pushl (&args, -"stash" ,"store" ,"-m" ,"autostash" ,"-q" , - autostash.buf,NULL ); -if (run_command_v_opt (args.argv,RUN_GIT_CMD )) - res =error (_ ("Cannot store %s" ), autostash.buf); -argv_array_clear (&args); -strbuf_release (&autostash); -if (res) -return res; - -fprintf (stderr, -_ ("Applying autostash resulted in conflicts.\n" -"Your changes are safe in the stash.\n" -"You can run \"git stash pop\" or \"git stash drop\" " -"at any time.\n" )); - } - -strbuf_release (&autostash); -return 0 ; -} -static int finish_rebase (struct rebase_options *opts) {struct strbuf dir =STRBUF_INIT ;
@@ -764,7 +719,7 @@ static int finish_rebase(struct rebase_options *opts)int ret =0 ;delete_ref (NULL ,"REBASE_HEAD" ,NULL ,REF_NO_DEREF ); -apply_autostash (opts); +apply_autostash (state_dir_path ("autostash" , opts));close_object_store (the_repository->objects);/* * We ignore errors in 'gc --auto', since the
@@ -1209,7 +1164,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) }else if (status ==2 ) {struct strbuf dir =STRBUF_INIT ; -apply_autostash (opts); +apply_autostash (state_dir_path ("autostash" , opts));strbuf_addstr (&dir, opts->state_dir);remove_dir_recursively (&dir,0 );strbuf_release (&dir);
sequencer.c +1 -1
@@ -3655,7 +3655,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)return -1 ; } -static int apply_autostash (const char *path) +int apply_autostash (const char *path) {struct strbuf stash_sha1 =STRBUF_INIT ;struct child_process child =CHILD_PROCESS_INIT ;
sequencer.h +2 -0
@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,const struct commit *current_head, const struct object_id *new_head); +int apply_autostash (const char *path); +#define SUMMARY_INITIAL_COMMIT (1 << 0)#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1) void print_commit_summary(struct repository *repo,
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 10/19 rebase: generify reset_head()
In reply to Denton LiuIn the future, we plan on lib-ifying reset_head() so we need it to be more generic. Make it more generic by making it accept a struct repository argument instead of implicitly using the non-repo functions. Also, make it accept a const char *default_reflog_action argument so that the default action of "rebase" isn't hardcoded in.
--- builtin/rebase.c | 64 ++++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 27 deletions(-)
builtin/rebase.c +37 -27
@@ -28,6 +28,8 @@#include "sequencer.h" #include "rebase-interactive.h" +#define DEFAULT_REFLOG_ACTION "rebase" +static char const *const builtin_rebase_usage[] = {N_ ("git rebase [-i] [options] [--exec <cmd>] " "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]" ),
@@ -772,9 +774,10 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)#define RESET_HEAD_REFS_ONLY (1<<3)#define RESET_ORIG_HEAD (1<<4) -static int reset_head (struct object_id *oid,const char *action, +static int reset_head (struct repository *r,struct object_id *oid,const char *action,const char *switch_to_branch,unsigned flags, -const char *reflog_orig_head,const char *reflog_head) +const char *reflog_orig_head,const char *reflog_head, +const char *default_reflog_action) {unsigned detach_head = flags &RESET_HEAD_DETACH ;unsigned reset_hard = flags &RESET_HEAD_HARD ;
@@ -796,7 +799,7 @@ static int reset_head(struct object_id *oid, const char *action,if (switch_to_branch && !starts_with (switch_to_branch,"refs/" ))BUG ("Not a fully qualified branch: '%s'" , switch_to_branch); -if (!refs_only &&hold_locked_index (&lock,LOCK_REPORT_ON_ERROR ) <0 ) { +if (!refs_only &&repo_hold_locked_index (r, &lock,LOCK_REPORT_ON_ERROR ) <0 ) { ret =-1 ; goto leave_reset_head; }
@@ -815,26 +818,26 @@ static int reset_head(struct object_id *oid, const char *action,memset (&unpack_tree_opts,0 ,sizeof (unpack_tree_opts));setup_unpack_trees_porcelain (&unpack_tree_opts, action); unpack_tree_opts.head_idx =1 ; - unpack_tree_opts.src_index = the_repository->index; - unpack_tree_opts.dst_index = the_repository->index; + unpack_tree_opts.src_index = r->index; + unpack_tree_opts.dst_index = r->index; unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; unpack_tree_opts.update =1 ; unpack_tree_opts.merge =1 ;if (!detach_head) unpack_tree_opts.reset =1 ; -if (repo_read_index_unmerged (the_repository) <0 ) { +if (repo_read_index_unmerged (r) <0 ) { ret =error (_ ("could not read index" )); goto leave_reset_head; } -if (!reset_hard && !fill_tree_descriptor (the_repository, &desc[nr++], &head_oid)) { +if (!reset_hard && !fill_tree_descriptor (r, &desc[nr++], &head_oid)) { ret =error (_ ("failed to find tree of %s" ),oid_to_hex (&head_oid)); goto leave_reset_head; } -if (!fill_tree_descriptor (the_repository, &desc[nr++], oid)) { +if (!fill_tree_descriptor (r, &desc[nr++], oid)) { ret =error (_ ("failed to find tree of %s" ),oid_to_hex (oid)); goto leave_reset_head; }
@@ -845,16 +848,16 @@ static int reset_head(struct object_id *oid, const char *action, } tree =parse_tree_indirect (oid); -prime_cache_tree (the_repository, the_repository->index, tree); +prime_cache_tree (r, r->index, tree); -if (write_locked_index (the_repository->index, &lock,COMMIT_LOCK ) <0 ) { +if (write_locked_index (r->index, &lock,COMMIT_LOCK ) <0 ) { ret =error (_ ("could not write index" )); goto leave_reset_head; } reset_head_refs: reflog_action =getenv (GIT_REFLOG_ACTION_ENVIRONMENT ); -strbuf_addf (&msg,"%s: " , reflog_action ? reflog_action :"rebase" ); +strbuf_addf (&msg,"%s: " , reflog_action ? reflog_action : default_reflog_action); prefix_len = msg.len;if (update_orig_head) {
@@ -916,8 +919,10 @@ static int move_to_original_branch(struct rebase_options *opts) opts->head_name, oid_to_hex(&opts->onto->object.oid));strbuf_addf (&head_reflog,"rebase finished: returning to %s" , opts->head_name); - ret =reset_head (NULL ,"" , opts->head_name,RESET_HEAD_REFS_ONLY , - orig_head_reflog.buf, head_reflog.buf); + ret =reset_head (the_repository,NULL ,"" , opts->head_name, +RESET_HEAD_REFS_ONLY , + orig_head_reflog.buf, head_reflog.buf, +DEFAULT_REFLOG_ACTION );strbuf_release (&orig_head_reflog);strbuf_release (&head_reflog);
@@ -1005,8 +1010,9 @@ static int run_am(struct rebase_options *opts)free (rebased_patches);argv_array_clear (&am.args); -reset_head (&opts->orig_head,"checkout" , opts->head_name,0 , -"HEAD" ,NULL ); +reset_head (the_repository, &opts->orig_head,"checkout" , + opts->head_name,0 , +"HEAD" ,NULL ,DEFAULT_REFLOG_ACTION ); error(_("\ngit encountered an error while preparing the " "patches to replay\n" "these revisions:\n"
@@ -1661,8 +1667,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)rerere_clear (the_repository, &merge_rr);string_list_clear (&merge_rr,1 ); -if (reset_head (NULL ,"reset" ,NULL ,RESET_HEAD_HARD , -NULL ,NULL ) <0 ) +if (reset_head (the_repository,NULL ,"reset" ,NULL ,RESET_HEAD_HARD , +NULL ,NULL ,DEFAULT_REFLOG_ACTION ) <0 )die (_ ("could not discard worktree changes" ));remove_branch_state (the_repository,0 );if (read_basic_state (&options))
@@ -1679,9 +1685,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)if (read_basic_state (&options))exit (1 ); -if (reset_head (&options.orig_head,"reset" , +if (reset_head (the_repository, &options.orig_head,"reset" , options.head_name,RESET_HEAD_HARD , -NULL ,NULL ) <0 ) +NULL ,NULL ,DEFAULT_REFLOG_ACTION ) <0 )die (_ ("could not move back to %s" ),oid_to_hex (&options.orig_head));remove_branch_state (the_repository,0 );
@@ -2073,8 +2079,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.state_dir);write_file (autostash,"%s" ,oid_to_hex (&oid));printf (_ ("Created autostash: %s\n" ), buf.buf); -if (reset_head (NULL ,"reset --hard" , -NULL ,RESET_HEAD_HARD ,NULL ,NULL ) <0 ) +if (reset_head (the_repository,NULL ,"reset --hard" , +NULL ,RESET_HEAD_HARD ,NULL ,NULL , +DEFAULT_REFLOG_ACTION ) <0 )die (_ ("could not reset --hard" )); if (discard_index(the_repository->index) < 0 ||
@@ -2114,10 +2121,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)strbuf_addf (&buf,"%s: checkout %s" ,getenv (GIT_REFLOG_ACTION_ENVIRONMENT ), options.switch_to); -if (reset_head (&options.orig_head,"checkout" , +if (reset_head (the_repository, + &options.orig_head,"checkout" , options.head_name,RESET_HEAD_RUN_POST_CHECKOUT_HOOK , -NULL , buf.buf) <0 ) { +NULL , buf.buf, +DEFAULT_REFLOG_ACTION ) <0 ) { ret = !!error (_ ("could not switch to " "%s" ), options.switch_to);
@@ -2189,10 +2198,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)strbuf_addf (&msg,"%s: checkout %s" ,getenv (GIT_REFLOG_ACTION_ENVIRONMENT ), options.onto_name); -if (reset_head (&options.onto->object.oid,"checkout" ,NULL , +if (reset_head (the_repository, &options.onto->object.oid,"checkout" ,NULL ,RESET_HEAD_DETACH |RESET_ORIG_HEAD |RESET_HEAD_RUN_POST_CHECKOUT_HOOK , -NULL , msg.buf)) +NULL , msg.buf,DEFAULT_REFLOG_ACTION ))die (_ ("Could not detach HEAD" ));strbuf_release (&msg);
@@ -2207,8 +2216,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)strbuf_addf (&msg,"rebase finished: %s onto %s" , options.head_name ? options.head_name :"detached HEAD" ,oid_to_hex (&options.onto->object.oid)); -reset_head (NULL ,"Fast-forwarded" , options.head_name, -RESET_HEAD_REFS_ONLY ,"HEAD" , msg.buf); +reset_head (the_repository,NULL ,"Fast-forwarded" , options.head_name, +RESET_HEAD_REFS_ONLY ,"HEAD" , msg.buf, +DEFAULT_REFLOG_ACTION );strbuf_release (&msg); ret = !!finish_rebase (&options); goto cleanup;
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 11/19 reset: extract reset_head() from rebase
In reply to Denton LiuContinue the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.
This patch is best viewed with --color-moved.
--- Makefile | 1 + builtin/rebase.c | 139 +--------------------------------------------- reset.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++ reset.h | 20 +++++++ 4 files changed, 162 insertions(+), 138 deletions(-) create mode 100644 reset.c create mode 100644 reset.h
Makefile +1 -0
@@ -970,6 +970,7 @@ LIB_OBJS += replace-object.o LIB_OBJS += repo-settings.o LIB_OBJS += repository.o LIB_OBJS += rerere.o +LIB_OBJS += reset.o LIB_OBJS += resolve-undo.o LIB_OBJS += revision.o LIB_OBJS += run-command.o
builtin/rebase.c +1 -138
151 lines not on this page · raw message
reset.c new file +140 -0
140 lines not on this page · raw message
reset.h new file +20 -0
@@ -0,0 +1,20 @@ +#ifndef RESET_H +#define RESET_H + +#include "hash.h" +#include "repository.h" + +#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" + +#define RESET_HEAD_DETACH (1<<0) +#define RESET_HEAD_HARD (1<<1) +#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) +#define RESET_HEAD_REFS_ONLY (1<<3) +#define RESET_ORIG_HEAD (1<<4) + +int reset_head (struct repository *r,struct object_id *oid,const char *action, +const char *switch_to_branch,unsigned flags, +const char *reflog_orig_head,const char *reflog_head, +const char *default_reflog_action); + +#endif
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 12/19 rebase: extract create_autostash()
In reply to Denton LiuIn a future commit, we will lib-ify this code. In preparation for this, extract the code into the create_autostash() function so that it can be cleaned up before it is finally lib-ified.
This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.
--- builtin/rebase.c | 94 +++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 44 deletions(-)
builtin/rebase.c +50 -44
@@ -1274,6 +1274,55 @@ static int check_exec_cmd(const char *cmd)return 0 ; } +static void create_autostash (struct rebase_options *options) +{ +struct strbuf buf =STRBUF_INIT ; +struct lock_file lock_file =LOCK_INIT ; +int fd; + + fd =hold_locked_index (&lock_file,0 ); +refresh_cache (REFRESH_QUIET ); +if (0 <= fd) +repo_update_index_if_able (the_repository, &lock_file); +rollback_lock_file (&lock_file); + +if (has_unstaged_changes (the_repository,1 ) || +has_uncommitted_changes (the_repository,1 )) { +const char *autostash = +state_dir_path ("autostash" , options); +struct child_process stash =CHILD_PROCESS_INIT ; +struct object_id oid; + +argv_array_pushl (&stash.args, +"stash" ,"create" ,"autostash" ,NULL ); + stash.git_cmd =1 ; + stash.no_stdin =1 ; +strbuf_reset (&buf); +if (capture_command (&stash, &buf,GIT_MAX_HEXSZ )) +die (_ ("Cannot autostash" )); +strbuf_trim_trailing_newline (&buf); +if (get_oid (buf.buf, &oid)) +die (_ ("Unexpected stash response: '%s'" ), + buf.buf); +strbuf_reset (&buf); +strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); + +if (safe_create_leading_directories_const (autostash)) +die (_ ("Could not create directory for '%s'" ), + options->state_dir); +write_file (autostash,"%s" ,oid_to_hex (&oid)); +printf (_ ("Created autostash: %s\n" ), buf.buf); +if (reset_head (the_repository,NULL ,"reset --hard" , +NULL ,RESET_HEAD_HARD ,NULL ,NULL , +DEFAULT_REFLOG_ACTION ) <0 ) +die (_ ("could not reset --hard" )); + +if (discard_index (the_repository->index) <0 || +repo_read_index (the_repository) <0 ) +die (_ ("could not read index" )); + } +strbuf_release (&buf); +}int cmd_rebase (int argc,const char **argv,const char *prefix) {
@@ -1907,50 +1956,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)die (_ ("could not read index" ));if (options.autostash) { -struct lock_file lock_file =LOCK_INIT ; -int fd; - - fd =hold_locked_index (&lock_file,0 ); -refresh_cache (REFRESH_QUIET ); -if (0 <= fd) -repo_update_index_if_able (the_repository, &lock_file); -rollback_lock_file (&lock_file); - -if (has_unstaged_changes (the_repository,1 ) || -has_uncommitted_changes (the_repository,1 )) { -const char *autostash = -state_dir_path ("autostash" , &options); -struct child_process stash =CHILD_PROCESS_INIT ; -struct object_id oid; - -argv_array_pushl (&stash.args, -"stash" ,"create" ,"autostash" ,NULL ); - stash.git_cmd =1 ; - stash.no_stdin =1 ; -strbuf_reset (&buf); -if (capture_command (&stash, &buf,GIT_MAX_HEXSZ )) -die (_ ("Cannot autostash" )); -strbuf_trim_trailing_newline (&buf); -if (get_oid (buf.buf, &oid)) -die (_ ("Unexpected stash response: '%s'" ), - buf.buf); -strbuf_reset (&buf); -strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); - -if (safe_create_leading_directories_const (autostash)) -die (_ ("Could not create directory for '%s'" ), - options.state_dir); -write_file (autostash,"%s" ,oid_to_hex (&oid)); -printf (_ ("Created autostash: %s\n" ), buf.buf); -if (reset_head (the_repository,NULL ,"reset --hard" , -NULL ,RESET_HEAD_HARD ,NULL ,NULL , -DEFAULT_REFLOG_ACTION ) <0 ) -die (_ ("could not reset --hard" )); - -if (discard_index (the_repository->index) <0 || -repo_read_index (the_repository) <0 ) -die (_ ("could not read index" )); - } +create_autostash (&options); } if (require_clean_work_tree(the_repository, "rebase",
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 13/19 rebase: generify create_autostash()
In reply to Denton LiuIn the future, we plan on lib-ifying create_autostash() so we need it to be more generic. Make it more generic by making it accept a struct repository argument instead of implicitly using the non-repo functions and the_repository. Also, make it accept a path argument so that we no longer rely have to rely on struct rebase_options. Finally, make it accept a default_reflog_action argument so we no longer have to rely on DEFAULT_REFLOG_ACTION.
--- builtin/rebase.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
builtin/rebase.c +16 -16
@@ -1274,22 +1274,21 @@ static int check_exec_cmd(const char *cmd)return 0 ; } -static void create_autostash (struct rebase_options *options) +static void create_autostash (struct repository *r,const char *path, +const char *default_reflog_action) {struct strbuf buf =STRBUF_INIT ;struct lock_file lock_file =LOCK_INIT ;int fd; - fd =hold_locked_index (&lock_file,0 ); -refresh_cache (REFRESH_QUIET ); + fd =repo_hold_locked_index (r, &lock_file,0 ); +refresh_index (r->index,REFRESH_QUIET ,NULL ,NULL ,NULL );if (0 <= fd) -repo_update_index_if_able (the_repository, &lock_file); +repo_update_index_if_able (r, &lock_file);rollback_lock_file (&lock_file); -if (has_unstaged_changes (the_repository,1 ) || -has_uncommitted_changes (the_repository,1 )) { -const char *autostash = -state_dir_path ("autostash" , options); +if (has_unstaged_changes (r,1 ) || +has_uncommitted_changes (r,1 )) {struct child_process stash =CHILD_PROCESS_INIT ;struct object_id oid;
@@ -1307,18 +1306,18 @@ static void create_autostash(struct rebase_options *options)strbuf_reset (&buf);strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); -if (safe_create_leading_directories_const (autostash)) +if (safe_create_leading_directories_const (path))die (_ ("Could not create directory for '%s'" ), - options->state_dir); -write_file (autostash,"%s" ,oid_to_hex (&oid)); + path); +write_file (path,"%s" ,oid_to_hex (&oid));printf (_ ("Created autostash: %s\n" ), buf.buf); -if (reset_head (the_repository,NULL ,"reset --hard" , +if (reset_head (r,NULL ,"reset --hard" ,NULL ,RESET_HEAD_HARD ,NULL ,NULL , -DEFAULT_REFLOG_ACTION ) <0 ) + default_reflog_action) <0 )die (_ ("could not reset --hard" )); -if (discard_index (the_repository->index) <0 || -repo_read_index (the_repository) <0 ) +if (discard_index (r->index) <0 || +repo_read_index (r) <0 )die (_ ("could not read index" )); }strbuf_release (&buf);
@@ -1956,7 +1955,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)die (_ ("could not read index" ));if (options.autostash) { -create_autostash (&options); +create_autostash (the_repository,state_dir_path ("autostash" , &options), +DEFAULT_REFLOG_ACTION ); } if (require_clean_work_tree(the_repository, "rebase",
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 14/19 sequencer: extract perform_autostash() from rebase
In reply to Denton LiuLib-ify the autostash code by extracting perform_autostash() from rebase into sequencer. In a future commit, this will be used to implement --autostash in other builtins.
This patch is best viewed with --color-moved.
--- builtin/rebase.c | 49 ----------------------------------------------- sequencer.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ sequencer.h | 2 ++ 3 files changed, 52 insertions(+), 49 deletions(-)
builtin/rebase.c +0 -49
@@ -1274,55 +1274,6 @@ static int check_exec_cmd(const char *cmd)return 0 ; } -static void create_autostash (struct repository *r,const char *path, -const char *default_reflog_action) -{ -struct strbuf buf =STRBUF_INIT ; -struct lock_file lock_file =LOCK_INIT ; -int fd; - - fd =repo_hold_locked_index (r, &lock_file,0 ); -refresh_index (r->index,REFRESH_QUIET ,NULL ,NULL ,NULL ); -if (0 <= fd) -repo_update_index_if_able (r, &lock_file); -rollback_lock_file (&lock_file); - -if (has_unstaged_changes (r,1 ) || -has_uncommitted_changes (r,1 )) { -struct child_process stash =CHILD_PROCESS_INIT ; -struct object_id oid; - -argv_array_pushl (&stash.args, -"stash" ,"create" ,"autostash" ,NULL ); - stash.git_cmd =1 ; - stash.no_stdin =1 ; -strbuf_reset (&buf); -if (capture_command (&stash, &buf,GIT_MAX_HEXSZ )) -die (_ ("Cannot autostash" )); -strbuf_trim_trailing_newline (&buf); -if (get_oid (buf.buf, &oid)) -die (_ ("Unexpected stash response: '%s'" ), - buf.buf); -strbuf_reset (&buf); -strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); - -if (safe_create_leading_directories_const (path)) -die (_ ("Could not create directory for '%s'" ), - path); -write_file (path,"%s" ,oid_to_hex (&oid)); -printf (_ ("Created autostash: %s\n" ), buf.buf); -if (reset_head (r,NULL ,"reset --hard" , -NULL ,RESET_HEAD_HARD ,NULL ,NULL , - default_reflog_action) <0 ) -die (_ ("could not reset --hard" )); - -if (discard_index (r->index) <0 || -repo_read_index (r) <0 ) -die (_ ("could not read index" )); - } -strbuf_release (&buf); -} -int cmd_rebase (int argc,const char **argv,const char *prefix) {struct rebase_options options =REBASE_OPTIONS_INIT ;
sequencer.c +50 -0
62 lines not on this page · raw message
sequencer.h +2 -0
@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,const struct commit *current_head, const struct object_id *new_head); +void create_autostash (struct repository *r,const char *path, +const char *default_reflog_action);int apply_autostash (const char *path);#define SUMMARY_INITIAL_COMMIT (1 << 0)
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 15/19 sequencer: unlink autostash in apply_autostash()
In reply to Denton LiuExplicitly remove autostash file in apply_autostash() once it has been applied successfully.
This is currently a no-op because the only users of this function will unlink the state (including the autostash file) after this function runs. However, in the future, we will introduce a user of the function that does not explicitly remove the state so we do it here.
--- sequencer.c | 1 + 1 file changed, 1 insertion(+)
sequencer.c +1 -0
@@ -3746,6 +3746,7 @@ int apply_autostash(const char *path) " \"git stash drop\" at any time.\n")); } +unlink (path);strbuf_release (&stash_sha1);return ret; }
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 16/19 sequencer: implement save_autostash()
In reply to Denton LiuExtract common functionality of apply_autostash() into apply_save_autostash() and use it to implement save_autostash(). This function will be used in a future commit.
--- sequencer.c | 37 +++++++++++++++++++++++++++---------- sequencer.h | 1 + 2 files changed, 28 insertions(+), 10 deletions(-)
sequencer.c +27 -10
@@ -3705,7 +3705,7 @@ void create_autostash(struct repository *r, const char *path,strbuf_release (&buf); } -int apply_autostash (const char *path) +static int apply_save_autostash (const char *path,int attempt_apply) {struct strbuf stash_sha1 =STRBUF_INIT ;struct child_process child =CHILD_PROCESS_INIT ;
@@ -3718,13 +3718,17 @@ int apply_autostash(const char *path) }strbuf_trim (&stash_sha1); - child.git_cmd =1 ; - child.no_stdout =1 ; - child.no_stderr =1 ; -argv_array_push (&child.args,"stash" ); -argv_array_push (&child.args,"apply" ); -argv_array_push (&child.args, stash_sha1.buf); -if (!run_command (&child)) +if (attempt_apply) { + child.git_cmd =1 ; + child.no_stdout =1 ; + child.no_stderr =1 ; +argv_array_push (&child.args,"stash" ); +argv_array_push (&child.args,"apply" ); +argv_array_push (&child.args, stash_sha1.buf); + ret =run_command (&child); + } + +if (attempt_apply && !ret)fprintf (stderr,_ ("Applied autostash.\n" ));else {struct child_process store =CHILD_PROCESS_INIT ;
@@ -3740,10 +3744,13 @@ int apply_autostash(const char *path) ret =error (_ ("cannot store %s" ), stash_sha1.buf); else fprintf(stderr, - _("Applying autostash resulted in conflicts.\n" + _("%s\n""Your changes are safe in the stash.\n" "You can run \"git stash pop\" or" - " \"git stash drop\" at any time.\n")); + " \"git stash drop\" at any time.\n"), + attempt_apply ? +_ ("Applying autostash resulted in conflicts." ) : + _("Autostash exists; creating a new stash entry.")); }unlink (path);
@@ -3751,6 +3758,16 @@ int apply_autostash(const char *path)return ret; } +int save_autostash (const char *path) +{ +return apply_save_autostash (path,0 ); +} + +int apply_autostash (const char *path) +{ +return apply_save_autostash (path,1 ); +} +static const char *reflog_message (struct replay_opts *opts,const char *sub_action,const char *fmt, ...) {
sequencer.h +1 -0
@@ -193,6 +193,7 @@ void commit_post_rewrite(struct repository *r,void create_autostash (struct repository *r,const char *path,const char *default_reflog_action); +int save_autostash (const char *path);int apply_autostash (const char *path);#define SUMMARY_INITIAL_COMMIT (1 << 0)
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 18/19 t5520: make test_pull_autostash() accept expect_parent_num
In reply to Denton LiuBefore, test_pull_autostash() was hardcoded to run test_cmp_rev HEAD^ copy to test that a rebase happened. However, in a future patch, we plan on testing merging as well. Make test_pull_autostash() accept a parent number as an argument so that, in the future, we can test if a merge happened in addition to a rebase.
--- t/t5520-pull.sh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
t/t5520-pull.sh +8 -6
@@ -10,11 +10,13 @@ modify () { } test_pull_autostash () { + expect_parent_num="$1" && + shift && git reset --hard before-rebase && echo dirty >new_file && git add new_file && git pull "$@" . copy && - test_cmp_rev HEAD^ copy && + test_cmp_rev HEAD^"$expect_parent_num" copy && echo dirty >expect && test_cmp expect new_file && echo "modified again" >expect &&
@@ -369,22 +371,22 @@ test_expect_success '--rebase fails with multiple branches' ' test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' ' test_config rebase.autostash true && - test_pull_autostash --rebase + test_pull_autostash 1 --rebase ' test_expect_success 'pull --rebase --autostash & rebase.autostash=true' ' test_config rebase.autostash true && - test_pull_autostash --rebase --autostash + test_pull_autostash 1 --rebase --autostash ' test_expect_success 'pull --rebase --autostash & rebase.autostash=false' ' test_config rebase.autostash false && - test_pull_autostash --rebase --autostash + test_pull_autostash 1 --rebase --autostash ' test_expect_success 'pull --rebase --autostash & rebase.autostash unset' ' test_unconfig rebase.autostash && - test_pull_autostash --rebase --autostash + test_pull_autostash 1 --rebase --autostash ' test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '
@@ -422,7 +424,7 @@ test_expect_success 'pull.rebase' ' test_expect_success 'pull --autostash & pull.rebase=true' ' test_config pull.rebase true && - test_pull_autostash --autostash + test_pull_autostash 1 --autostash ' test_expect_success 'pull --no-autostash & pull.rebase=true' '
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 17/19 merge: teach --autostash option
In reply to Denton LiuIn rebase, one can pass the --autostash option to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.
Implement the --autostash option and corresponding merge.autoStash option in merge which stashes before merging and then pops after.
This option is useful when a developer has some local changes on a topic branch but they realize that their work depends on another branch. Previously, they had to run something like
git fetch ...
git stash push
git merge FETCH_HEAD
git stash pop
but now, that is reduced to
git fetch ... git merge --autostash FETCH_HEAD
When git reset --hard is run to abort a merge, the working tree will be left in a clean state, as expected, with the autostash pushed onto the stash stack.
--- Documentation/config/merge.txt | 10 +++ Documentation/merge-options.txt | 8 +++ branch.c | 1 + builtin/commit.c | 2 + builtin/merge.c | 17 ++++++ builtin/reset.c | 7 ++- path.c | 1 + path.h | 4 +- t/t3033-merge-toplevel.sh | 22 +++++++ t/t7600-merge.sh | 104 ++++++++++++++++++++++++++++++++ 10 files changed, 174 insertions(+), 2 deletions(-)
Documentation/config/merge.txt +10 -0
@@ -70,6 +70,16 @@ merge.stat:: Whether to print the diffstat between ORIG_HEAD and the merge result at the end of the merge. True by default. +merge.autoStash:: + When set to true, automatically create a temporary stash entry + before the operation begins, and apply it after the operation + ends. This means that you can run rebase on a dirty worktree. + However, use with care: the final stash application after a + successful rebase might result in non-trivial conflicts. + This option can be overridden by the `--no-autostash` and + `--autostash` options of linkgit:git-merge[1]. + Defaults to false. + merge.tool:: Controls which merge tool is used by linkgit:git-mergetool[1]. The list below shows the valid built-in values.
Documentation/merge-options.txt +8 -0
@@ -155,6 +155,14 @@ ifndef::git-pull[] Note that not all merge strategies may support progress reporting. +--autostash:: +--no-autostash:: + Automatically create a temporary stash entry before the operation + begins, and apply it after the operation ends. This means + that you can run rebase on a dirty worktree. However, use + with care: the final stash application after a successful + rebase might result in non-trivial conflicts. + endif::git-pull[] --allow-unrelated-histories::
branch.c +1 -0
@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)unlink (git_path_merge_rr (r));unlink (git_path_merge_msg (r));unlink (git_path_merge_mode (r)); +apply_autostash (git_path_merge_autostash (r)); }void remove_branch_state (struct repository *r,int verbose)
builtin/commit.c +2 -0
@@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)unlink (git_path_merge_mode (the_repository));unlink (git_path_squash_msg (the_repository)); +apply_autostash (git_path_merge_autostash (the_repository)); +if (commit_index_files ()) die(_("repository has been updated, but unable to write\n" "new_index file. Check that disk is not full and quota is\n"
builtin/merge.c +17 -0
@@ -82,6 +82,7 @@ static int show_progress = -1;static int default_to_upstream =1 ;static int signoff;static const char *sign_commit; +static int autostash;static int no_verify;static struct strategy all_strategy[] = {
@@ -286,6 +287,8 @@ static struct option builtin_merge_options[] = {OPT_SET_INT (0 ,"progress" , &show_progress,N_ ("force progress reporting" ),1 ), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, +OPT_BOOL (0 ,"autostash" , &autostash, +N_ ("automatically stash/stash pop before and after" )),OPT_BOOL (0 ,"overwrite-ignore" , &overwrite_ignore,N_ ("update ignored files (default)" )),OPT_BOOL (0 ,"signoff" , &signoff,N_ ("add Signed-off-by:" )), OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ -441,6 +444,7 @@ static void finish(struct commit *head_commit,strbuf_addf (&reflog_message,"%s: %s" ,getenv ("GIT_REFLOG_ACTION" ), msg); } +apply_autostash (git_path_merge_autostash (the_repository));if (squash) {squash_message (head_commit, remoteheads); }else {
@@ -634,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)return 0 ; }else if (!strcmp (k,"gpg.mintrustlevel" )) { check_trust_level =0 ; + }else if (!strcmp (k,"merge.autostash" )) { + autostash =git_config_bool (k, v); +return 0 ; } status =fmt_merge_msg_config (k, v, cb);
@@ -1291,6 +1298,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)/* Invoke 'git reset --merge' */ ret =cmd_reset (nargc, nargv, prefix); +apply_autostash (git_path_merge_autostash (the_repository)); goto done; }
@@ -1513,6 +1521,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix) goto done; } +if (autostash) +create_autostash (the_repository, +git_path_merge_autostash (the_repository), +"merge" ); if (checkout_fast_forward(the_repository, &head_commit->object.oid, &commit->object.oid,
@@ -1579,6 +1591,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)if (fast_forward ==FF_ONLY )die (_ ("Not possible to fast-forward, aborting." )); +if (autostash) +create_autostash (the_repository, +git_path_merge_autostash (the_repository), +"merge" ); +/* We are going to make a new commit. */ git_committer_info (IDENT_STRICT );
builtin/reset.c +6 -1
@@ -25,6 +25,7 @@#include "cache-tree.h" #include "submodule.h" #include "submodule-config.h" +#include "sequencer.h" #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (reset_type ==HARD && !update_ref_status && !quiet)print_new_head_line (lookup_commit_reference (the_repository, &oid)); } -if (!pathspec.nr) +if (!pathspec.nr) { +if (reset_type ==HARD ) +save_autostash (git_path_merge_autostash (the_repository)); +remove_branch_state (the_repository,0 ); + }return update_ref_status; }
path.c +1 -0
@@ -1535,5 +1535,6 @@ REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG") REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR") REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE") REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD") +REPO_GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH") REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")REPO_GIT_PATH_FUNC (shallow,"shallow" )
path.h +3 -1
@@ -177,11 +177,12 @@ struct path_cache {const char *merge_rr;const char *merge_mode;const char *merge_head; +const char *merge_autostash;const char *fetch_head;const char *shallow; }; -#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }const char *git_path_cherry_pick_head (struct repository *r);const char *git_path_revert_head (struct repository *r);
@@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);const char *git_path_merge_rr (struct repository *r);const char *git_path_merge_mode (struct repository *r);const char *git_path_merge_head (struct repository *r); +const char *git_path_merge_autostash (struct repository *r);const char *git_path_fetch_head (struct repository *r);const char *git_path_shallow (struct repository *r);
t/t3033-merge-toplevel.sh +22 -0
@@ -142,6 +142,17 @@ test_expect_success 'refuse two-project merge by default' ' test_must_fail git merge five ' +test_expect_success 'refuse two-project merge by default, quit before --autostash happens' ' + t3033_reset && + git reset --hard four && + echo change >>one.t && + git diff >expect && + test_must_fail git merge --autostash five 2>err && + test_i18ngrep ! "stash" err && + git diff >actual && + test_cmp expect actual +' + test_expect_success 'two-project merge with --allow-unrelated-histories' ' t3033_reset && git reset --hard four &&
@@ -149,4 +160,15 @@ test_expect_success 'two-project merge with --allow-unrelated-histories' ' git diff --exit-code five ' +test_expect_success 'two-project merge with --allow-unrelated-histories with --autostash' ' + t3033_reset && + git reset --hard four && + echo change >>one.t && + git diff one.t >expect && + git merge --allow-unrelated-histories --autostash five 2>err && + test_i18ngrep "Applied autostash." err && + git diff one.t >actual && + test_cmp expect actual +' + test_done
t/t7600-merge.sh +104 -0
123 lines not on this page · raw message
Signature · 1 line
2.25.0.114.g5b0ca878e0
PATCH v3 19/19 pull: pass --autostash to merge
In reply to Denton LiuBefore, --autostash only worked with git pull --rebase. However, in the last patch, merge learned --autostash as well so there's no reason why we should have this restriction anymore. Teach pull to pass --autostash to merge, just like it did for rebase.
--- Documentation/git-pull.txt | 9 ------- Documentation/merge-options.txt | 4 +-- builtin/pull.c | 9 ++++--- t/t5520-pull.sh | 43 +++++++++++++++++++++++++++------ 4 files changed, 42 insertions(+), 23 deletions(-)
Documentation/git-pull.txt +0 -9
@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully. --no-rebase:: Override earlier --rebase. ---autostash:: ---no-autostash:: - Before starting rebase, stash local modifications away (see - linkgit:git-stash[1]) if needed, and apply the stash entry when - done. `--no-autostash` is useful to override the `rebase.autoStash` - configuration variable (see linkgit:git-config[1]). -+ -This option is only valid when "--rebase" is used. - Options related to fetching ~~~~~~~~~~~~~~~~~~~~~~~~~~~
Documentation/merge-options.txt +2 -2
@@ -155,6 +155,8 @@ ifndef::git-pull[] Note that not all merge strategies may support progress reporting. +endif::git-pull[] + --autostash:: --no-autostash:: Automatically create a temporary stash entry before the operation
@@ -163,8 +165,6 @@ ifndef::git-pull[] with care: the final stash application after a successful rebase might result in non-trivial conflicts. -endif::git-pull[] - --allow-unrelated-histories:: By default, `git merge` command refuses to merge histories that do not share a common ancestor. This option can be
builtin/pull.c +5 -4
@@ -163,7 +163,7 @@ static struct option pull_options[] = { N_("verify that the named commit has a valid GPG signature"), PARSE_OPT_NOARG),OPT_BOOL (0 ,"autostash" , &opt_autostash, -N_ ("automatically stash/stash pop before and after rebase" )), +N_ ("automatically stash/stash pop before and after" )),OPT_PASSTHRU_ARGV ('s' ,"strategy" , &opt_strategies,N_ ("strategy" ),N_ ("merge strategy to use" ), 0),
@@ -661,6 +661,10 @@ static int run_merge(void)argv_array_pushv (&args, opt_strategy_opts.argv);if (opt_gpg_sign)argv_array_push (&args, opt_gpg_sign); +if (opt_autostash ==0 ) +argv_array_push (&args,"--no-autostash" ); +else if (opt_autostash ==1 ) +argv_array_push (&args,"--autostash" );if (opt_allow_unrelated_histories >0 )argv_array_push (&args,"--allow-unrelated-histories" );
@@ -908,9 +912,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)if (get_oid ("HEAD" , &orig_head))oidclr (&orig_head); -if (!opt_rebase && opt_autostash !=-1 ) -die (_ ("--[no-]autostash option is only valid with --rebase." )); - autostash = config_autostash;if (opt_rebase) {if (opt_autostash !=-1 )
t/t5520-pull.sh +35 -8
55 lines not on this page · raw message
Signature · 1 line
2.25.0.114.g5b0ca878e0
Re: [PATCH v3 03/19] sequencer: use file strbuf for read_oneliner()
In reply to Denton LiuHi Denton
Quoted 10 lines · Denton Liu
In the original
read_onelinerlogic, we duplicated the logic forstrbuf_trim_trailing_newline()with one exception: instead of preventing buffer accesses below index 0, it would prevent buffer accesses below indexorig_len. Although this is correct, it isn't worth having the duplicated logic around.Add a second strbuf to which files are read and run
strbuf_trim_trailing_newline()directly on this strbuf then concatenate this strbuf with the argument strbuf at the end of the function. The function's external behaviour is unchanged.
read_oneliner() is typically called with an strbuf that is used multiple times with the caller resetting it in-between calls. This saves us from having to allocate a new buffer for each call. The change here negates that as we end up allocating a new buffer each time and then copying the result. I'd be surprised if any of the callers actually wanted to append to the buffer, I suspect they all call strbuf_reset() before passing the buffer to read_oneliner(). If that is the case maybe we should think about adding the call to strbuf_reset() inside read_oneliner() and documenting it as resetting the buffer before reading. Then we can just use strbuf_trim() on the 'real' buffer.
Best Wishes
Phillip
Quoted 51 lines
Signed-off-by Denton Liu <[email protected]>--- sequencer.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-)sequencer.c +13
-11@@ -431,26 +431,28 @@ static int write_message(const void *buf, size_t len, const char *filename,static int read_oneliner (struct strbuf *buf,const char *path,int skip_if_empty) { -int orig_len = buf->len; +int ret =0 ; +struct strbuf file_buf =STRBUF_INIT ;if (!file_exists (path))return 0 ; -if (strbuf_read_file (buf, path,0 ) <0 ) { +if (strbuf_read_file (&file_buf, path,0 ) <0 ) {warning_errno (_ ("could not read '%s'" ), path); -return 0 ; + goto done; } -if (buf->len > orig_len && buf->buf[buf->len -1 ] =='\n' ) { -if (--buf->len > orig_len && buf->buf[buf->len -1 ] =='\r' ) - --buf->len; - buf->buf[buf->len] ='\0' ; - } +strbuf_trim_trailing_newline (&file_buf); -if (skip_if_empty && buf->len == orig_len) -return 0 ; +if (skip_if_empty && !file_buf.len) + goto done; -return 1 ; +strbuf_addbuf (buf, &file_buf); + ret =1 ; + +done: +strbuf_release (&file_buf); +return ret; }static struct tree *empty_tree (struct repository *r)
Re: [PATCH v3 05/19] sequencer: configurably warn on non-existent files
In reply to Denton LiuHi Denton
Quoted 4 lines · Denton Liu
In the future, we plan on externing
read_oneliner(). Future users ofread_oneliner()will want the ability to output warnings in the event that thepathdoesn't exist. Introduce theREAD_ONELINER_WARN_NON_EXISTENCE
This is a rather cumbersome name, I think Junio's original suggestion of READ_ONELINER_WARN_MISSING is nicer
Quoted 22 lines
flag which, if active, would issue a
warning when a file doesn't exist by skipping the!file_exists()check and lettingstrbuf_read_file()handle that case.Signed-off-by Denton Liu <[email protected]>--- sequencer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)sequencer.c +1
-0@@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename, }#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0) +#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)/* * Reads a file that was presumably written by a shell script, i.e. with an
sequencer.c static int read_oneliner(struct strbuf *buf, · line 441
Quoted 6 lines
sequencer.c +1
-1@@ -436,7 +437,7 @@ static int read_oneliner(struct strbuf *buf,int ret =0 ;struct strbuf file_buf =STRBUF_INIT ; -if (!file_exists (path)) +if (!(flags &READ_ONELINER_WARN_NON_EXISTENCE ) && !file_exists (path))return 0 ;
This isn't the fault of you patch but it would be more efficient to do the read and then check for ENOENT/ENOTDIR rather than calling file_exists() first
Best Wishes
Phillip
Quoted 2 lines
sequencer.c +0
-0if (strbuf_read_file (&file_buf, path,0 ) <0 ) {
Re: [PATCH v3 07/19] rebase: use read_oneliner()
In reply to Denton LiuHi Denton
Quoted 6 lines · Denton Liu
Since in
sequencer.c,read_one()basically duplicates the functionality ofread_oneliner(), reduce code duplication by replacingread_one()withread_oneliner().This was done with the following Coccinelle script
Quoted 7 lines
@@ expression a, b; @@ - read_one(a, b) + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)and long lines were manually broken up.
It's great to see read_one() going away
Thanks
Phillip
Quoted 104 lines
Signed-off-by Denton Liu <[email protected]>--- builtin/rebase.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-)builtin/rebase.c +17
-20@@ -586,15 +586,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *oreturn path.buf; } -/* Read one file, then strip line endings */ -static int read_one (const char *path,struct strbuf *buf) -{ -if (strbuf_read_file (buf, path,0 ) <0 ) -return error_errno (_ ("could not read '%s'" ), path); -strbuf_trim_trailing_newline (buf); -return 0 ; -} -/* Initialize the rebase options from the state directory. */ static int read_basic_state (struct rebase_options *opts) {@@ -602,8 +593,10 @@ static int read_basic_state(struct rebase_options *opts)struct strbuf buf =STRBUF_INIT ;struct object_id oid; -if (read_one (state_dir_path ("head-name" , opts), &head_name) || -read_one (state_dir_path ("onto" , opts), &buf)) +if (!read_oneliner (&head_name,state_dir_path ("head-name" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ) || + !read_oneliner (&buf,state_dir_path ("onto" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ; opts->head_name =starts_with (head_name.buf,"refs/" ) ?xstrdup (head_name.buf) :NULL ;@@ -619,9 +612,11 @@ static int read_basic_state(struct rebase_options *opts)*/ strbuf_reset (&buf);if (file_exists (state_dir_path ("orig-head" , opts))) { -if (read_one (state_dir_path ("orig-head" , opts), &buf)) +if (!read_oneliner (&buf,state_dir_path ("orig-head" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ; - }else if (read_one (state_dir_path ("head" , opts), &buf)) + }else if (!read_oneliner (&buf,state_dir_path ("head" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ;if (get_oid (buf.buf, &opts->orig_head))return error (_ ("invalid orig-head: '%s'" ), buf.buf);@@ -641,8 +636,8 @@ static int read_basic_state(struct rebase_options *opts)if (file_exists (state_dir_path ("allow_rerere_autoupdate" , opts))) {strbuf_reset (&buf); -if (read_one (state_dir_path ("allow_rerere_autoupdate" , opts), - &buf)) +if (!read_oneliner (&buf,state_dir_path ("allow_rerere_autoupdate" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ;if (!strcmp (buf.buf,"--rerere-autoupdate" )) opts->allow_rerere_autoupdate =RERERE_AUTOUPDATE ;@@ -655,8 +650,8 @@ static int read_basic_state(struct rebase_options *opts)if (file_exists (state_dir_path ("gpg_sign_opt" , opts))) {strbuf_reset (&buf); -if (read_one (state_dir_path ("gpg_sign_opt" , opts), - &buf)) +if (!read_oneliner (&buf,state_dir_path ("gpg_sign_opt" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ;free (opts->gpg_sign_opt); opts->gpg_sign_opt =xstrdup (buf.buf);@@ -664,7 +659,8 @@ static int read_basic_state(struct rebase_options *opts)if (file_exists (state_dir_path ("strategy" , opts))) {strbuf_reset (&buf); -if (read_one (state_dir_path ("strategy" , opts), &buf)) +if (!read_oneliner (&buf,state_dir_path ("strategy" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ;free (opts->strategy); opts->strategy =xstrdup (buf.buf);@@ -672,7 +668,8 @@ static int read_basic_state(struct rebase_options *opts)if (file_exists (state_dir_path ("strategy_opts" , opts))) {strbuf_reset (&buf); -if (read_one (state_dir_path ("strategy_opts" , opts), &buf)) +if (!read_oneliner (&buf,state_dir_path ("strategy_opts" , opts), +READ_ONELINER_WARN_NON_EXISTENCE ))return -1 ;free (opts->strategy_opts); opts->strategy_opts =xstrdup (buf.buf);@@ -724,7 +721,7 @@ static int apply_autostash(struct rebase_options *opts)if (!file_exists (path))return 0 ; -if (read_one (path, &autostash)) +if (!read_oneliner (&autostash, path,READ_ONELINER_WARN_NON_EXISTENCE ))return error (_ ("Could not read '%s'" ), path);/* Ensure that the hash is not mistaken for a number */ strbuf_addstr (&autostash,"^0" );
Re: [PATCH v3 09/19] rebase: use apply_autostash() from sequencer.c
In reply to Denton LiuHi Denton
Quoted 3 lines · Denton Liu
The
apply_autostash()function inbuiltin/rebase.cis similar enough to theapply_autostash()function insequencer.cthat they are almost interchangeable.
You say they are almost interchangeable and then use them as if they are completely interchangeable. I think they are interchangeable, the only difference I could see is that the code in rebase.c redirects stdin when forking 'git stash apply' but the sequencer does not, I don't think this matters.
Best Wishes
Phillip
Make the sequencer.c version extern and use it in
Quoted 118 lines
rebase.
The rebase version was introduced in
6defce2b02(builtin rebase: support--autostashoption, 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 refactoringsequencer.cversion ofapply_autostash(). Since both efforts are long done, we can freely combine them together now.Signed-off-by Denton Liu <[email protected]>--- builtin/rebase.c | 49 ++---------------------------------------------- sequencer.c | 2 +- sequencer.h | 2 ++ 3 files changed, 5 insertions(+), 48 deletions(-)builtin/rebase.c +2
-47@@ -712,51 +712,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)return 0 ; } -static int apply_autostash (struct rebase_options *opts) -{ -const char *path =state_dir_path ("autostash" , opts); -struct strbuf autostash =STRBUF_INIT ; -struct child_process stash_apply =CHILD_PROCESS_INIT ; - -if (!file_exists (path)) -return 0 ; - -if (!read_oneliner (&autostash, path,READ_ONELINER_WARN_NON_EXISTENCE )) -return error (_ ("Could not read '%s'" ), path); -/* Ensure that the hash is not mistaken for a number */ -strbuf_addstr (&autostash,"^0" ); -argv_array_pushl (&stash_apply.args, -"stash" ,"apply" , autostash.buf,NULL ); - stash_apply.git_cmd =1 ; - stash_apply.no_stderr = stash_apply.no_stdout = - stash_apply.no_stdin =1 ; -if (!run_command (&stash_apply)) -printf (_ ("Applied autostash.\n" )); -else { -struct argv_array args =ARGV_ARRAY_INIT ; -int res =0 ; - -argv_array_pushl (&args, -"stash" ,"store" ,"-m" ,"autostash" ,"-q" , - autostash.buf,NULL ); -if (run_command_v_opt (args.argv,RUN_GIT_CMD )) - res =error (_ ("Cannot store %s" ), autostash.buf); -argv_array_clear (&args); -strbuf_release (&autostash); -if (res) -return res; - -fprintf (stderr, -_ ("Applying autostash resulted in conflicts.\n" -"Your changes are safe in the stash.\n" -"You can run \"git stash pop\" or \"git stash drop\" " -"at any time.\n" )); - } - -strbuf_release (&autostash); -return 0 ; -} -static int finish_rebase (struct rebase_options *opts) {struct strbuf dir =STRBUF_INIT ;@@ -764,7 +719,7 @@ static int finish_rebase(struct rebase_options *opts)int ret =0 ;delete_ref (NULL ,"REBASE_HEAD" ,NULL ,REF_NO_DEREF ); -apply_autostash (opts); +apply_autostash (state_dir_path ("autostash" , opts));close_object_store (the_repository->objects);/* * We ignore errors in 'gc --auto', since the @@ -1209,7 +1164,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) }else if (status ==2 ) {struct strbuf dir =STRBUF_INIT ; -apply_autostash (opts); +apply_autostash (state_dir_path ("autostash" , opts));strbuf_addstr (&dir, opts->state_dir);remove_dir_recursively (&dir,0 );strbuf_release (&dir);sequencer.c +1
-1@@ -3655,7 +3655,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)return -1 ; } -static int apply_autostash (const char *path) +int apply_autostash (const char *path) {struct strbuf stash_sha1 =STRBUF_INIT ;struct child_process child =CHILD_PROCESS_INIT ;sequencer.h +2
-0@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,const struct commit *current_head, const struct object_id *new_head); +int apply_autostash (const char *path); +#define SUMMARY_INITIAL_COMMIT (1 << 0)#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1) void print_commit_summary(struct repository *repo,
Re: [PATCH v3 11/19] reset: extract reset_head() from rebase
In reply to Denton LiuHi Denton
Quoted 4 lines · Denton Liu
Continue the process of lib-ifying the autostash code. In a future commit, this will be used to implement
--autostashin other builtins.This patch is best viewed with
--color-moved.
Indeed! It looks good. Looking ahead I hope we can use this to avoid forking 'git checkout' in sequencer.c
Best Wishes
Phillip
Quoted 353 lines
Signed-off-by Denton Liu <[email protected]>--- 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 100644reset.hMakefile +1
-0@@ -970,6 +970,7 @@ LIB_OBJS += replace-object.o LIB_OBJS += repo-settings.o LIB_OBJS += repository.o LIB_OBJS += rerere.o +LIB_OBJS += reset.o LIB_OBJS += resolve-undo.o LIB_OBJS += revision.o LIB_OBJS += run-command.obuiltin/rebase.c +1
-138@@ -27,6 +27,7 @@#include "branch.h" #include "sequencer.h" #include "rebase-interactive.h" +#include "reset.h" #define DEFAULT_REFLOG_ACTION "rebase"@@ -766,144 +767,6 @@ static void add_var(struct strbuf *buf, const char *name, const char *value) } } -#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" - -#define RESET_HEAD_DETACH (1<<0) -#define RESET_HEAD_HARD (1<<1) -#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) -#define RESET_HEAD_REFS_ONLY (1<<3) -#define RESET_ORIG_HEAD (1<<4) - -static int reset_head (struct repository *r,struct object_id *oid,const char *action, -const char *switch_to_branch,unsigned flags, -const char *reflog_orig_head,const char *reflog_head, -const char *default_reflog_action) -{ -unsigned detach_head = flags &RESET_HEAD_DETACH ; -unsigned reset_hard = flags &RESET_HEAD_HARD ; -unsigned run_hook = flags &RESET_HEAD_RUN_POST_CHECKOUT_HOOK ; -unsigned refs_only = flags &RESET_HEAD_REFS_ONLY ; -unsigned update_orig_head = flags &RESET_ORIG_HEAD ; -struct object_id head_oid; -struct tree_desc desc[2 ] = { {NULL }, {NULL } }; -struct lock_file lock =LOCK_INIT ; -struct unpack_trees_options unpack_tree_opts; -struct tree *tree; -const char *reflog_action; -struct strbuf msg =STRBUF_INIT ; -size_t prefix_len; -struct object_id *orig =NULL , oid_orig, - *old_orig =NULL , oid_old_orig; -int ret =0 , nr =0 ; - -if (switch_to_branch && !starts_with (switch_to_branch,"refs/" )) -BUG ("Not a fully qualified branch: '%s'" , switch_to_branch); - -if (!refs_only &&repo_hold_locked_index (r, &lock,LOCK_REPORT_ON_ERROR ) <0 ) { - ret =-1 ; - goto leave_reset_head; - } - -if ((!oid || !reset_hard) &&get_oid ("HEAD" , &head_oid)) { - ret =error (_ ("could not determine HEAD revision" )); - goto leave_reset_head; - } - -if (!oid) - oid = &head_oid; - -if (refs_only) - goto reset_head_refs; - -memset (&unpack_tree_opts,0 ,sizeof (unpack_tree_opts)); -setup_unpack_trees_porcelain (&unpack_tree_opts, action); - unpack_tree_opts.head_idx =1 ; - unpack_tree_opts.src_index = r->index; - unpack_tree_opts.dst_index = r->index; - unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; - unpack_tree_opts.update =1 ; - unpack_tree_opts.merge =1 ; -if (!detach_head) - unpack_tree_opts.reset =1 ; - -if (repo_read_index_unmerged (r) <0 ) { - ret =error (_ ("could not read index" )); - goto leave_reset_head; - } - -if (!reset_hard && !fill_tree_descriptor (r, &desc[nr++], &head_oid)) { - ret =error (_ ("failed to find tree of %s" ), -oid_to_hex (&head_oid)); - goto leave_reset_head; - } - -if (!fill_tree_descriptor (r, &desc[nr++], oid)) { - ret =error (_ ("failed to find tree of %s" ),oid_to_hex (oid)); - goto leave_reset_head; - } - -if (unpack_trees (nr, desc, &unpack_tree_opts)) { - ret =-1 ; - goto leave_reset_head; - } - - tree =parse_tree_indirect (oid); -prime_cache_tree (r, r->index, tree); - -if (write_locked_index (r->index, &lock,COMMIT_LOCK ) <0 ) { - ret =error (_ ("could not write index" )); - goto leave_reset_head; - } - -reset_head_refs: - reflog_action =getenv (GIT_REFLOG_ACTION_ENVIRONMENT ); -strbuf_addf (&msg,"%s: " , reflog_action ? reflog_action : default_reflog_action); - prefix_len = msg.len; - -if (update_orig_head) { -if (!get_oid ("ORIG_HEAD" , &oid_old_orig)) - old_orig = &oid_old_orig; -if (!get_oid ("HEAD" , &oid_orig)) { - orig = &oid_orig; -if (!reflog_orig_head) { -strbuf_addstr (&msg,"updating ORIG_HEAD" ); - reflog_orig_head = msg.buf; - } -update_ref (reflog_orig_head,"ORIG_HEAD" , orig, - old_orig,0 ,UPDATE_REFS_MSG_ON_ERR ); - }else if (old_orig) -delete_ref (NULL ,"ORIG_HEAD" , old_orig,0 ); - } - -if (!reflog_head) { -strbuf_setlen (&msg, prefix_len); -strbuf_addstr (&msg,"updating HEAD" ); - reflog_head = msg.buf; - } -if (!switch_to_branch) - ret =update_ref (reflog_head,"HEAD" , oid, orig, - detach_head ?REF_NO_DEREF :0 , -UPDATE_REFS_MSG_ON_ERR ); -else { - ret =update_ref (reflog_head, switch_to_branch, oid, -NULL ,0 ,UPDATE_REFS_MSG_ON_ERR ); -if (!ret) - ret =create_symref ("HEAD" , switch_to_branch, - reflog_head); - } -if (run_hook) -run_hook_le (NULL ,"post-checkout" , -oid_to_hex (orig ? orig : &null_oid), -oid_to_hex (oid),"1" ,NULL ); - -leave_reset_head: -strbuf_release (&msg); -rollback_lock_file (&lock); -while (nr) -free ((void *)desc[--nr].buffer); -return ret; -} -static int move_to_original_branch (struct rebase_options *opts) {struct strbuf orig_head_reflog =STRBUF_INIT , head_reflog =STRBUF_INIT ;reset.c new file +140
-0@@ -0,0 +1,140 @@ +#include "git-compat-util.h" +#include "cache-tree.h" +#include "lockfile.h" +#include "refs.h" +#include "reset.h" +#include "run-command.h" +#include "tree-walk.h" +#include "tree.h" +#include "unpack-trees.h" + +int reset_head (struct repository *r,struct object_id *oid,const char *action, +const char *switch_to_branch,unsigned flags, +const char *reflog_orig_head,const char *reflog_head, +const char *default_reflog_action) +{ +unsigned detach_head = flags &RESET_HEAD_DETACH ; +unsigned reset_hard = flags &RESET_HEAD_HARD ; +unsigned run_hook = flags &RESET_HEAD_RUN_POST_CHECKOUT_HOOK ; +unsigned refs_only = flags &RESET_HEAD_REFS_ONLY ; +unsigned update_orig_head = flags &RESET_ORIG_HEAD ; +struct object_id head_oid; +struct tree_desc desc[2 ] = { {NULL }, {NULL } }; +struct lock_file lock =LOCK_INIT ; +struct unpack_trees_options unpack_tree_opts; +struct tree *tree; +const char *reflog_action; +struct strbuf msg =STRBUF_INIT ; +size_t prefix_len; +struct object_id *orig =NULL , oid_orig, + *old_orig =NULL , oid_old_orig; +int ret =0 , nr =0 ; + +if (switch_to_branch && !starts_with (switch_to_branch,"refs/" )) +BUG ("Not a fully qualified branch: '%s'" , switch_to_branch); + +if (!refs_only &&repo_hold_locked_index (r, &lock,LOCK_REPORT_ON_ERROR ) <0 ) { + ret =-1 ; + goto leave_reset_head; + } + +if ((!oid || !reset_hard) &&get_oid ("HEAD" , &head_oid)) { + ret =error (_ ("could not determine HEAD revision" )); + goto leave_reset_head; + } + +if (!oid) + oid = &head_oid; + +if (refs_only) + goto reset_head_refs; + +memset (&unpack_tree_opts,0 ,sizeof (unpack_tree_opts)); +setup_unpack_trees_porcelain (&unpack_tree_opts, action); + unpack_tree_opts.head_idx =1 ; + unpack_tree_opts.src_index = r->index; + unpack_tree_opts.dst_index = r->index; + unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; + unpack_tree_opts.update =1 ; + unpack_tree_opts.merge =1 ; +if (!detach_head) + unpack_tree_opts.reset =1 ; + +if (repo_read_index_unmerged (r) <0 ) { + ret =error (_ ("could not read index" )); + goto leave_reset_head; + } + +if (!reset_hard && !fill_tree_descriptor (r, &desc[nr++], &head_oid)) { + ret =error (_ ("failed to find tree of %s" ), +oid_to_hex (&head_oid)); + goto leave_reset_head; + } + +if (!fill_tree_descriptor (r, &desc[nr++], oid)) { + ret =error (_ ("failed to find tree of %s" ),oid_to_hex (oid)); + goto leave_reset_head; + } + +if (unpack_trees (nr, desc, &unpack_tree_opts)) { + ret =-1 ; + goto leave_reset_head; + } + + tree =parse_tree_indirect (oid); +prime_cache_tree (r, r->index, tree); + +if (write_locked_index (r->index, &lock,COMMIT_LOCK ) <0 ) { + ret =error (_ ("could not write index" )); + goto leave_reset_head; + } + +reset_head_refs: + reflog_action =getenv (GIT_REFLOG_ACTION_ENVIRONMENT ); +strbuf_addf (&msg,"%s: " , reflog_action ? reflog_action : default_reflog_action); + prefix_len = msg.len; + +if (update_orig_head) { +if (!get_oid ("ORIG_HEAD" , &oid_old_orig)) + old_orig = &oid_old_orig; +if (!get_oid ("HEAD" , &oid_orig)) { + orig = &oid_orig; +if (!reflog_orig_head) { +strbuf_addstr (&msg,"updating ORIG_HEAD" ); + reflog_orig_head = msg.buf; + } +update_ref (reflog_orig_head,"ORIG_HEAD" , orig, + old_orig,0 ,UPDATE_REFS_MSG_ON_ERR ); + }else if (old_orig) +delete_ref (NULL ,"ORIG_HEAD" , old_orig,0 ); + } + +if (!reflog_head) { +strbuf_setlen (&msg, prefix_len); +strbuf_addstr (&msg,"updating HEAD" ); + reflog_head = msg.buf; + } +if (!switch_to_branch) + ret =update_ref (reflog_head,"HEAD" , oid, orig, + detach_head ?REF_NO_DEREF :0 , +UPDATE_REFS_MSG_ON_ERR ); +else { + ret =update_ref (reflog_head, switch_to_branch, oid, +NULL ,0 ,UPDATE_REFS_MSG_ON_ERR ); +if (!ret) + ret =create_symref ("HEAD" , switch_to_branch, + reflog_head); + } +if (run_hook) +run_hook_le (NULL ,"post-checkout" , +oid_to_hex (orig ? orig : &null_oid), +oid_to_hex (oid),"1" ,NULL ); + +leave_reset_head: +strbuf_release (&msg); +rollback_lock_file (&lock); +while (nr) +free ((void *)desc[--nr].buffer); +return ret; + +}reset.h new file +20
-0@@ -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
Re: [PATCH v3 16/19] sequencer: implement save_autostash()
In reply to Denton LiuHi Denton
Quoted 35 lines · Denton Liu
Extract common functionality of
apply_autostash()intoapply_save_autostash()and use it to implementsave_autostash(). This function will be used in a future commit.Signed-off-by Denton Liu <[email protected]>--- sequencer.c | 37 +++++++++++++++++++++++++++---------- sequencer.h | 1 + 2 files changed, 28 insertions(+), 10 deletions(-)sequencer.c +2
-8@@ -3705,7 +3705,7 @@ void create_autostash(struct repository *r, const char *path,strbuf_release (&buf); } -int apply_autostash (const char *path) +static int apply_save_autostash (const char *path,int attempt_apply) {struct strbuf stash_sha1 =STRBUF_INIT ;struct child_process child =CHILD_PROCESS_INIT ;@@ -3718,13 +3718,17 @@ int apply_autostash(const char *path) }strbuf_trim (&stash_sha1); - child.git_cmd =1 ; - child.no_stdout =1 ; - child.no_stderr =1 ; -argv_array_push (&child.args,"stash" ); -argv_array_push (&child.args,"apply" ); -argv_array_push (&child.args, stash_sha1.buf); -if (!run_command (&child)) +if (attempt_apply) {
sequencer.c int apply_autostash(const char *path) · line 3731
Quoted 10 lines
sequencer.c +10
-0+ child.git_cmd =1 ; + child.no_stdout =1 ; + child.no_stderr =1 ; +argv_array_push (&child.args,"stash" ); +argv_array_push (&child.args,"apply" ); +argv_array_push (&child.args, stash_sha1.buf); + ret =run_command (&child); + } + +if (attempt_apply && !ret)
If attempt_only is not set then we wont get any message - is this intended? Previously we always showed the message when applying was successful
Quoted 6 lines
sequencer.c +0
-0fprintf (stderr,_ ("Applied autostash.\n" )); else {struct child_process store =CHILD_PROCESS_INIT ;@@ -3740,10 +3744,13 @@ int apply_autostash(const char *path) ret =error (_ ("cannot store %s" ), stash_sha1.buf); else
sequencer.c int apply_autostash(const char *path) · line 3753
Quoted 10 lines
sequencer.c +5
-2fprintf (stderr, -_ ("Applying autostash resulted in conflicts.\n" +_ ("%s\n" "Your changes are safe in the stash.\n" "You can run \"git stash pop\" or" -" \"git stash drop\" at any time.\n" )); +" \"git stash drop\" at any time.\n" ), + attempt_apply ? +_ ("Applying autostash resulted in conflicts." ) : +_ ("Autostash exists; creating a new stash entry." ));
I'm a bit confused by the new message, perhaps the commit message could explain what attempt_only is used for and then I suspect the reason for this change will become clear
Best Wishes
Phillip
Quoted 33 lines
sequencer.c +10
-0}unlink (path);@@ -3751,6 +3758,16 @@ int apply_autostash(const char *path)return ret; } +int save_autostash (const char *path) +{ +return apply_save_autostash (path,0 ); +} + +int apply_autostash (const char *path) +{ +return apply_save_autostash (path,1 ); +} +static const char *reflog_message (struct replay_opts *opts,const char *sub_action,const char *fmt, ...) {sequencer.h +1
-0@@ -193,6 +193,7 @@ void commit_post_rewrite(struct repository *r,void create_autostash (struct repository *r,const char *path,const char *default_reflog_action); +int save_autostash (const char *path);int apply_autostash (const char *path);#define SUMMARY_INITIAL_COMMIT (1 << 0)
Re: [PATCH v3 17/19] merge: teach --autostash option
In reply to Denton LiuHi Denton
Quoted 45 lines · Denton Liu
In rebase, one can pass the
--autostashoption to cause the worktree to be automatically stashed before continuing with the rebase. This option is missing in merge, however.Implement the
--autostashoption and correspondingmerge.autoStashoption 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 mergeFETCH_HEAD
git stash popbut now, that is reduced to
git fetch ... git merge --autostash FETCH_HEADWhen
git reset --hardis run to abort a merge, the working tree will be left in a clean state, as expected, with the autostash pushed onto the stash stack.Suggested-by Alban Gruin <[email protected]>Signed-off-by Denton Liu <[email protected]>--- Documentation/config/merge.txt | 10 +++ Documentation/merge-options.txt | 8 +++ branch.c | 1 + builtin/commit.c | 2 + builtin/merge.c | 17 ++++++ builtin/reset.c | 7 ++- path.c | 1 + path.h | 4 +- t/t3033-merge-toplevel.sh | 22 +++++++ t/t7600-merge.sh | 104 ++++++++++++++++++++++++++++++++ 10 files changed, 174 insertions(+), 2 deletions(-)Documentation/config/merge.txt +0
-0
Documentation/config/merge.txt merge.stat:: · line 76
Quoted 7 lines
Documentation/config/merge.txt +4
-0@@ -70,6 +70,16 @@ merge.stat:: Whether to print the diffstat between ORIG_HEAD and the merge result at the end of the merge. True by default. +merge.autoStash:: + When set to true, automatically create a temporary stash entry + before the operation begins, and apply it after the operation + ends. This means that you can run rebase
copy and paste error - s/rebase/merge/
Quoted 15 lines
on a dirty worktree.
Documentation/config/merge.txt +6
-0+ However, use with care: the final stash application after a + successful rebase might result in non-trivial conflicts. + This option can be overridden by the `--no-autostash` and + `--autostash` options of linkgit:git-merge[1]. + Defaults to false. + merge.tool:: Controls which merge tool is used by linkgit:git-mergetool[1]. The list below shows the valid built-in values.Documentation/merge-options.txt +0
-0
Documentation/merge-options.txt ifndef::git-pull[] · line 163
Quoted 9 lines
Documentation/merge-options.txt +6
-0@@ -155,6 +155,14 @@ ifndef::git-pull[] Note that not all merge strategies may support progress reporting. +--autostash:: +--no-autostash:: + Automatically create a temporary stash entry before the operation + begins, and apply it after the operation ends. This means + that you can run rebase on a dirty worktree. However, use > + with care: the final stash application after a successful + rebase might result in non-trivial conflicts.
s/rebase/merge/
Quoted 21 lines
Documentation/merge-options.txt +1
-0+ endif::git-pull[] --allow-unrelated-histories::branch.c +1
-0@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)unlink (git_path_merge_rr (r));unlink (git_path_merge_msg (r));unlink (git_path_merge_mode (r)); +apply_autostash (git_path_merge_autostash (r)); }void remove_branch_state (struct repository *r,int verbose)builtin/commit.c +0
-0
builtin/commit.c int cmd_commit(int argc, const char **argv, const char *prefix) · line 1691
Quoted 5 lines
builtin/commit.c +2
-0@@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)unlink (git_path_merge_mode (the_repository));unlink (git_path_squash_msg (the_repository)); +apply_autostash (git_path_merge_autostash (the_repository)); +
It's hard to tell from the limited context but do we want to run commit_index_files() before applying the autostash? I wonder if this should be using remove_merge_branch_state() instead of duplicating it as well.
Quoted 16 lines
builtin/commit.c +0
-0if (commit_index_files ()) die(_("repository has been updated, but unable to write\n" "new_index file. Check that disk is not full and quota is\n"builtin/merge.c +1
-0@@ -82,6 +82,7 @@ static int show_progress = -1;static int default_to_upstream =1 ;static int signoff;static const char *sign_commit; +static int autostash;static int no_verify;static struct strategy all_strategy[] = {
builtin/merge.c static struct option builtin_merge_options[] = { · line 291
Quoted 5 lines
builtin/merge.c +2
-0@@ -286,6 +287,8 @@ static struct option builtin_merge_options[] = {OPT_SET_INT (0 ,"progress" , &show_progress,N_ ("force progress reporting" ),1 ), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"), N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" }, +OPT_BOOL (0 ,"autostash" , &autostash, + N_("automatically stash/stash pop before and after")),
If we're adding this option to several commands (rebase, merge, pull) then it might be worth defining it in parse-options.h
Quoted 65 lines
builtin/merge.c +14
-0OPT_BOOL (0 ,"overwrite-ignore" , &overwrite_ignore,N_ ("update ignored files (default)" )),OPT_BOOL (0 ,"signoff" , &signoff,N_ ("add Signed-off-by:" )), OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),@@ -441,6 +444,7 @@ static void finish(struct commit *head_commit,strbuf_addf (&reflog_message,"%s: %s" ,getenv ("GIT_REFLOG_ACTION" ), msg); } +apply_autostash (git_path_merge_autostash (the_repository));if (squash) {squash_message (head_commit, remoteheads); }else {@@ -634,6 +638,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)return 0 ; }else if (!strcmp (k,"gpg.mintrustlevel" )) { check_trust_level =0 ; + }else if (!strcmp (k,"merge.autostash" )) { + autostash =git_config_bool (k, v); +return 0 ; } status =fmt_merge_msg_config (k, v, cb);@@ -1291,6 +1298,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)/* Invoke 'git reset --merge' */ ret =cmd_reset (nargc, nargv, prefix); +apply_autostash (git_path_merge_autostash (the_repository)); goto done; }@@ -1513,6 +1521,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix) goto done; } +if (autostash) +create_autostash (the_repository, +git_path_merge_autostash (the_repository), +"merge" ); if (checkout_fast_forward(the_repository, &head_commit->object.oid, &commit->object.oid,@@ -1579,6 +1591,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)if (fast_forward ==FF_ONLY )die (_ ("Not possible to fast-forward, aborting." )); +if (autostash) +create_autostash (the_repository, +git_path_merge_autostash (the_repository), +"merge" ); +/* We are going to make a new commit. */ git_committer_info (IDENT_STRICT );builtin/reset.c +1
-0@@ -25,6 +25,7 @@#include "cache-tree.h" #include "submodule.h" #include "submodule-config.h" +#include "sequencer.h" #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 445
Quoted 9 lines
builtin/reset.c +4
-1@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (reset_type ==HARD && !update_ref_status && !quiet)print_new_head_line (lookup_commit_reference (the_repository, &oid)); } -if (!pathspec.nr) +if (!pathspec.nr) { +if (reset_type ==HARD ) +save_autostash (git_path_merge_autostash (the_repository)); +remove_branch_state (the_repository,0 );
This removes the autostash file for all reset types but we only keep the stash in the case of 'reset --hard' which is confusing.
Best Wishes
Phillip
Quoted 209 lines
builtin/reset.c +1
-0+ }return update_ref_status; }path.c +1
-0@@ -1535,5 +1535,6 @@ REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG") REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR") REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE") REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD") +REPO_GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH") REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")REPO_GIT_PATH_FUNC (shallow,"shallow" )path.h +3
-1@@ -177,11 +177,12 @@ struct path_cache {const char *merge_rr;const char *merge_mode;const char *merge_head; +const char *merge_autostash;const char *fetch_head;const char *shallow; }; -#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }const char *git_path_cherry_pick_head (struct repository *r);const char *git_path_revert_head (struct repository *r);@@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);const char *git_path_merge_rr (struct repository *r);const char *git_path_merge_mode (struct repository *r);const char *git_path_merge_head (struct repository *r); +const char *git_path_merge_autostash (struct repository *r);const char *git_path_fetch_head (struct repository *r);const char *git_path_shallow (struct repository *r);t/t3033-merge-toplevel.sh +22
-0@@ -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_donet/t7600-merge.sh +104
-0@@ -30,13 +30,17 @@ Testing basic merge operations/option parsing. . "$TEST_DIRECTORY"/lib-gpg.sh test_write_lines 1 2 3 4 5 6 7 8 9 >file +cp file file.orig test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1 +test_write_lines 1 2 '3 X' 4 5 6 7 8 9 >file.3 test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5 test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9 test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1 test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5 +test_write_lines '1 X' 2 3 4 5 6 7 8 '9 X' >result.1-9 test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9 +test_write_lines '1 X' 2 '3 X' 4 '5 X' 6 7 8 '9 X' >result.1-3-5-9 test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z create_merge_msgs () {@@ -675,6 +679,106 @@ test_expect_success 'refresh the index before merging' ' git merge c3 ' +test_expect_success 'merge with --autostash' ' + git reset --hard c1 && + git merge-file file file.orig file.9 && + git merge --autostash c2 2>err && + test_i18ngrep "Applied autostash." err && + git show HEAD:file >merge-result && + test_cmp result.1-5 merge-result && + test_cmp result.1-5-9 file +' + +test_expect_success 'merge with merge.autoStash' ' + test_config merge.autoStash true && + git reset --hard c1 && + git merge-file file file.orig file.9 && + git merge c2 2>err && + test_i18ngrep "Applied autostash." err && + git show HEAD:file >merge-result && + test_cmp result.1-5 merge-result && + test_cmp result.1-5-9 file +' + +test_expect_success 'fast-forward merge with --autostash' ' + git reset --hard c0 && + git merge-file file file.orig file.5 && + git merge --autostash c1 2>err && + test_i18ngrep "Applied autostash." err && + test_cmp result.1-5 file +' + +test_expect_success 'octopus merge with --autostash' ' + git reset --hard c1 && + git merge-file file file.orig file.3 && + git merge --autostash c2 c3 2>err && + test_i18ngrep "Applied autostash." err && + git show HEAD:file >merge-result && + test_cmp result.1-5-9 merge-result && + test_cmp result.1-3-5-9 file +' + +test_expect_success 'conflicted merge with --autostash, --abort restores stash' ' + git reset --hard c3 && + cp file.1 file && + test_must_fail git merge --autostash c7 && + git merge --abort 2>err && + test_i18ngrep "Applied autostash." err && + test_cmp file.1 file +' + +test_expect_success 'completed merge with --no-commit and --autostash' ' + git reset --hard c1 && + git merge-file file file.orig file.9 && + git diff >expect && + git merge --no-commit --autostash c2 && + git stash show -p MERGE_AUTOSTASH >actual && + test_cmp expect actual && + git commit 2>err && + test_i18ngrep "Applied autostash." err && + git show HEAD:file >merge-result && + test_cmp result.1-5 merge-result && + test_cmp result.1-5-9 file +' + +test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' ' + git reset --hard c1 && + git merge-file file file.orig file.9 && + git diff >expect && + git merge --no-commit --autostash c2 && + git stash show -p MERGE_AUTOSTASH >actual && + test_cmp expect actual && + git merge --abort 2>err && + test_i18ngrep "Applied autostash." err && + git diff >actual && + test_cmp expect actual +' + +test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' ' + git reset --hard c1 && + git merge-file file file.orig file.9 && + git diff >expect && + git merge --no-commit --autostash c2 && + git stash show -p MERGE_AUTOSTASH >actual && + test_cmp expect actual && + git reset --hard 2>err && + test_i18ngrep "Autostash exists; creating a new stash entry." err && + git diff --exit-code +' + +test_expect_success 'merge with conflicted --autostash changes' ' + git reset --hard c1 && + git merge-file file file.orig file.9y && + git diff >expect && + test_when_finished "test_might_fail git stash drop" && + git merge --autostash c3 2>err && + test_i18ngrep "Applying autostash resulted in conflicts." err && + git show HEAD:file >merge-result && + test_cmp result.1-9 merge-result && + git stash show -p >actual && + test_cmp expect actual +' + cat >expected.branch <<\EOF Merge branch 'c5-branch' (early part) EOF
Re: [PATCH v3 19/19] pull: pass --autostash to merge
In reply to Denton LiuHi Denton
Quoted 47 lines · Denton Liu
Before,
--autostashonly worked withgit pull --rebase. However, in the last patch, merge learned--autostashas well so there's no reason why we should have this restriction anymore. Teach pull to pass--autostashto merge, just like it did for rebase.Signed-off-by Denton Liu <[email protected]>--- Documentation/git-pull.txt | 9 ------- Documentation/merge-options.txt | 4 +-- builtin/pull.c | 9 ++++--- t/t5520-pull.sh | 43 +++++++++++++++++++++++++++------ 4 files changed, 42 insertions(+), 23 deletions(-)Documentation/git-pull.txt +0
-9@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully. --no-rebase:: Override earlier --rebase. ---autostash:: ---no-autostash:: - Before starting rebase, stash local modifications away (see - linkgit:git-stash[1]) if needed, and apply the stash entry when - done. `--no-autostash` is useful to override the `rebase.autoStash` - configuration variable (see linkgit:git-config[1]). -+ -This option is only valid when "--rebase" is used. - Options related to fetching ~~~~~~~~~~~~~~~~~~~~~~~~~~~Documentation/merge-options.txt +2
-0@@ -155,6 +155,8 @@ ifndef::git-pull[] Note that not all merge strategies may support progress reporting. +endif::git-pull[] + --autostash:: --no-autostash:: Automatically create a temporary stash entry before the operation
Documentation/merge-options.txt ifndef::git-pull[] · line 167
Quoted 5 lines
Documentation/merge-options.txt +0
-2@@ -163,8 +165,6 @@ ifndef::git-pull[] with care: the final stash application after a successful rebase might result in non-trivial conflicts. -endif::git-pull[] -
This change means we need a neutral wording for the --autostash documentation that works with rebase/merge/pull rather than mentioning a specific command
Best Wishes
Phillip
Quoted 99 lines
Documentation/merge-options.txt +0
-0--allow-unrelated-histories:: By default, `git merge` command refuses to merge histories that do not share a common ancestor. This option can bebuiltin/pull.c +5
-4@@ -163,7 +163,7 @@ static struct option pull_options[] = { N_("verify that the named commit has a valid GPG signature"), PARSE_OPT_NOARG),OPT_BOOL (0 ,"autostash" , &opt_autostash, -N_ ("automatically stash/stash pop before and after rebase" )), +N_ ("automatically stash/stash pop before and after" )),OPT_PASSTHRU_ARGV ('s' ,"strategy" , &opt_strategies,N_ ("strategy" ),N_ ("merge strategy to use" ), 0),@@ -661,6 +661,10 @@ static int run_merge(void)argv_array_pushv (&args, opt_strategy_opts.argv);if (opt_gpg_sign)argv_array_push (&args, opt_gpg_sign); +if (opt_autostash ==0 ) +argv_array_push (&args,"--no-autostash" ); +else if (opt_autostash ==1 ) +argv_array_push (&args,"--autostash" );if (opt_allow_unrelated_histories >0 )argv_array_push (&args,"--allow-unrelated-histories" );@@ -908,9 +912,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)if (get_oid ("HEAD" , &orig_head))oidclr (&orig_head); -if (!opt_rebase && opt_autostash !=-1 ) -die (_ ("--[no-]autostash option is only valid with --rebase." )); - autostash = config_autostash;if (opt_rebase) {if (opt_autostash !=-1 )t/t5520-pull.sh +35
-8@@ -28,7 +28,7 @@ test_pull_autostash_fail () { echo dirty >new_file && git add new_file && test_must_fail git pull "$@" . copy 2>err && - test_i18ngrep "uncommitted changes." err + test_i18ngrep "\(uncommitted changes.\)\|\(overwritten by merge:\)" err } test_expect_success setup '@@ -404,13 +404,40 @@ test_expect_success 'pull --rebase --no-autostash & rebase.autostash unset' ' test_pull_autostash_fail --rebase --no-autostash ' -for i in --autostash --no-autostash -do - test_expect_success "pull $i (without --rebase) is illegal" ' - test_must_fail git pull $i . copy 2>err && - test_i18ngrep "only valid with --rebase" err - ' -done +test_expect_success 'pull succeeds with dirty working directory and merge.autostash set' ' + test_config merge.autostash true && + test_pull_autostash 2 +' + +test_expect_success 'pull --autostash & merge.autostash=true' ' + test_config merge.autostash true && + test_pull_autostash 2 --autostash +' + +test_expect_success 'pull --autostash & merge.autostash=false' ' + test_config merge.autostash false && + test_pull_autostash 2 --autostash +' + +test_expect_success 'pull --autostash & merge.autostash unset' ' + test_unconfig merge.autostash && + test_pull_autostash 2 --autostash +' + +test_expect_success 'pull --no-autostash & merge.autostash=true' ' + test_config merge.autostash true && + test_pull_autostash_fail --no-autostash +' + +test_expect_success 'pull --no-autostash & merge.autostash=false' ' + test_config merge.autostash false && + test_pull_autostash_fail --no-autostash +' + +test_expect_success 'pull --no-autostash & merge.autostash unset' ' + test_unconfig merge.autostash && + test_pull_autostash_fail --no-autostash +' test_expect_success 'pull.rebase' ' git reset --hard before-rebase &&
Re: [PATCH v3 17/19] merge: teach --autostash option
In reply to Phillip WoodHi Phillip,
Thanks for your detailed review on the series. I've updated everything according to your comments except for the parts below:
Quoted 15 lines · Phillip Wood
Quoted 15 lines
branch.c +1
-0@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)unlink (git_path_merge_rr (r));unlink (git_path_merge_msg (r));unlink (git_path_merge_mode (r)); +apply_autostash (git_path_merge_autostash (r)); }void remove_branch_state (struct repository *r,int verbose)builtin/commit.c +0
-0
Quoted 8 lines
builtin/commit.c int cmd_commit(int argc, const char **argv, const char *prefix) · line 1690
Quoted 5 lines
builtin/commit.c +2
-0@@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)unlink (git_path_merge_mode (the_repository));unlink (git_path_squash_msg (the_repository)); +apply_autostash (git_path_merge_autostash (the_repository)); +It's hard to tell from the limited context but do we want to run
commit_index_files()before applying the autostash?
I don't think it really matters which order we run it in. When we run apply_autostash(), we only ever touch the working tree, not the index so it doesn't matter if it's run before or after. I'd prefer to keep it here because if we ever refactor this to use remove_merge_branch_state(), the person working on this will be able to perform the refactor more easily without having to worry about implicit ordering dependencies.
Quoted 2 lines
I wonder if this should
be usingremove_merge_branch_state()instead of duplicating it as well.
We can _almost_ use remove_branch_state() even! Unfortunately, remove_merge_branch_state() calls unlink(git_path_merge_rr(r)) which we cannot do since repo_rerere() relies on it later. Perhaps we can can move repo_rerere() earlier?
Quoted 15 lines
Quoted 15 lines
builtin/reset.c +1
-1@@ -25,6 +25,7 @@#include "cache-tree.h" #include "submodule.h" #include "submodule-config.h" +#include "sequencer.h" #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (reset_type ==HARD && !update_ref_status && !quiet)print_new_head_line (lookup_commit_reference (the_repository, &oid)); } -if (!pathspec.nr)
Quoted 8 lines
builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 445
Quoted 5 lines
builtin/reset.c +4
-0+if (!pathspec.nr) { +if (reset_type ==HARD ) +save_autostash (git_path_merge_autostash (the_repository)); +remove_branch_state (the_repository,0 );This removes the autostash file for all reset types but we only keep the stash in the case of 'reset --hard' which is confusing.
I was worried that this change would be controversial... The rationale behind this change was that with reset --hard, we want to leave a clean working tree behind so we save it into the stash reflog. In all other cases, remove_branch_state() will apply the saved stash entry which should be fine since users don't expect a clean worktree with the other reset types.
I considered saving the autostash in all cases of reset but git merge --abort invokes git reset --merge behind the scenes so we'd have to consider that. Perhaps we can make all resets save the stash entry and, in the case of merge --abort, we can add some extra logic to subvert this so that the stash entry is applied?
I'm not sure about what the most intuitive behaviour is. I thought that this implementation would be the best but I'm not entirely sure. I'd appreciate some more discussion on this.
Thanks,
Denton
Quoted 7 lines
Best Wishes
Phillip
Quoted 3 lines
builtin/reset.c +1
-0+ }return update_ref_status; }
Re: [PATCH v3 17/19] merge: teach --autostash option
Quoted 31 lines · Denton Liu
Quoted 15 lines
Quoted 15 lines
builtin/reset.c +1
-1@@ -25,6 +25,7 @@#include "cache-tree.h" #include "submodule.h" #include "submodule-config.h" +#include "sequencer.h" #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (reset_type ==HARD && !update_ref_status && !quiet)print_new_head_line (lookup_commit_reference (the_repository, &oid)); } -if (!pathspec.nr)Quoted 8 lines
builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 445
Quoted 5 lines
builtin/reset.c +4
-0+if (!pathspec.nr) { +if (reset_type ==HARD ) +save_autostash (git_path_merge_autostash (the_repository)); +remove_branch_state (the_repository,0 );This removes the autostash file for all reset types but we only keep the stash in the case of 'reset --hard' which is confusing.
I was worried that this change would be controversial... The rationale behind this change was that with
reset --hard, we want to leave a clean working tree behind so we save it into the stash reflog. In all other cases,remove_branch_state()will apply the saved stash entry which should be fine since users don't expect a clean worktree with the other reset types.
Quoted 5 lines
I considered saving the autostash in all cases of reset but
git merge --abortinvokesgit reset --mergebehind the scenes so we'd have to consider that. Perhaps we can make all resets save the stash entry and, in the case ofmerge --abort, we can add some extra logic to subvert this so that the stash entry is applied?
Perhaps something like this?
-- >8 --
commit 14d0b569cb7675f00d32d3d7fad7564fcaeca458
Author: Denton Liu <[email protected]> Date: Fri Apr 3 06:50:34 2020 -0400
squash! merge: teach --autostash option
Stash is saved when any reset is run, when git merge --abort is run, stash is applied.
builtin/merge.c +10 -1
@@ -1242,6 +1242,8 @@ static int merging_a_throwaway_tag(struct commit *commit)return is_throwaway_tag; } +static GIT_PATH_FUNC(git_path_merge_autostash_saved, "MERGE_AUTOSTASH_SAVED") + int cmd_merge(int argc, const char **argv, const char *prefix) {struct object_id result_tree, stash, head_oid;
@@ -1295,9 +1297,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)if (!file_exists (git_path_merge_head (the_repository)))die (_ ("There is no merge to abort (MERGE_HEAD missing)." )); +if (file_exists (git_path_merge_autostash (the_repository))) { +if (rename (git_path_merge_autostash (the_repository), +git_path_merge_autostash_saved ())) +die_errno (_ ("failed to rename autostash" )); + } +/* Invoke 'git reset --merge' */ ret =cmd_reset (nargc, nargv, prefix); -apply_autostash (git_path_merge_autostash (the_repository)); + +apply_autostash (git_path_merge_autostash_saved ()); goto done; }
builtin/reset.c +1 -3
@@ -439,9 +439,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)print_new_head_line (lookup_commit_reference (the_repository, &oid)); }if (!pathspec.nr) { -if (reset_type ==HARD ) -save_autostash (git_path_merge_autostash (the_repository)); - +save_autostash (git_path_merge_autostash (the_repository));remove_branch_state (the_repository,0 ); }
Quoted 16 lines
I'm not sure about what the most intuitive behaviour is. I thought that this implementation would be the best but I'm not entirely sure. I'd appreciate some more discussion on this.
Thanks,
Denton
Quoted 7 lines
Best Wishes
Phillip
Quoted 3 lines
builtin/reset.c +1
-0+ }return update_ref_status; }
Re: [PATCH v3 17/19] merge: teach --autostash option
Hi Denton
Quoted 42 lines · Denton Liu
Quoted 31 lines · Denton Liu
Quoted 15 lines
Quoted 15 lines
builtin/reset.c +1
-1@@ -25,6 +25,7 @@#include "cache-tree.h" #include "submodule.h" #include "submodule-config.h" +#include "sequencer.h" #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (reset_type ==HARD && !update_ref_status && !quiet)print_new_head_line (lookup_commit_reference (the_repository, &oid)); } -if (!pathspec.nr)Quoted 8 lines
builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 445
Quoted 5 lines
builtin/reset.c +4
-0+if (!pathspec.nr) { +if (reset_type ==HARD ) +save_autostash (git_path_merge_autostash (the_repository)); +remove_branch_state (the_repository,0 );This removes the autostash file for all reset types but we only keep the stash in the case of 'reset --hard' which is confusing.
I was worried that this change would be controversial... The rationale behind this change was that with
reset --hard, we want to leave a clean working tree behind so we save it into the stash reflog. In all other cases,remove_branch_state()will apply the saved stash entry which should be fine since users don't expect a clean worktree with the other reset types.Quoted 5 lines
I considered saving the autostash in all cases of reset but
git merge --abortinvokesgit reset --mergebehind the scenes so we'd have to consider that. Perhaps we can make all resets save the stash entry and, in the case ofmerge --abort, we can add some extra logic to subvert this so that the stash entry is applied?Perhaps something like this?
-- >8 --
commit14d0b569cb7675f00d32d3d7fad7564fcaeca458
Quoted 7 lines
Author: Denton Liu <[email protected]> Date: Fri Apr 3 06:50:34 2020 -0400squash! merge: teach --autostash option
Stash is saved when any reset is run, when git merge --abort is run, stash is applied.
I think this is the easiest behavior to understand, it avoids changing the behavior of reset, in particular it stops 'reset --mixed/--soft' from suddenly starting to touch the working tree.
Quoted 14 lines
builtin/merge.c +2
-0@@ -1242,6 +1242,8 @@ static int merging_a_throwaway_tag(struct commit *commit)return is_throwaway_tag; } +static GIT_PATH_FUNC(git_path_merge_autostash_saved, "MERGE_AUTOSTASH_SAVED") + int cmd_merge(int argc, const char **argv, const char *prefix) {struct object_id result_tree, stash, head_oid;
builtin/merge.c int cmd_merge(int argc, const char **argv, const char *prefix) · line 1303
Quoted 7 lines
builtin/merge.c +4
-0@@ -1295,9 +1297,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)if (!file_exists (git_path_merge_head (the_repository)))die (_ ("There is no merge to abort (MERGE_HEAD missing)." )); +if (file_exists (git_path_merge_autostash (the_repository))) { +if (rename (git_path_merge_autostash (the_repository), +git_path_merge_autostash_saved ())) +die_errno (_ ("failed to rename autostash" ));
This is a bit of a performance, can't we just remember the stash oid in a variable and tweak the apply code?
builtin/merge.c int cmd_merge(int argc, const char **argv, const char *prefix) · line 1309
Quoted 7 lines
builtin/merge.c +4
-1+ } +/* Invoke 'git reset --merge' */ ret =cmd_reset (nargc, nargv, prefix); -apply_autostash (git_path_merge_autostash (the_repository)); + +apply_autostash (git_path_merge_autostash_saved ());
Calling cmd_reset() was already a bit dodgy by our normal rules, now we're calling other functions after it though I guess given the current autostash implementation it's mostly in a separate process.
I think this is a good direction to go in
BTW what message gets printed when the stash is saved?
Best Wishes
Phillip
Quoted 34 lines
builtin/merge.c +0
-0goto done; }builtin/reset.c +1
-3@@ -439,9 +439,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)print_new_head_line (lookup_commit_reference (the_repository, &oid)); }if (!pathspec.nr) { -if (reset_type ==HARD ) -save_autostash (git_path_merge_autostash (the_repository)); - +save_autostash (git_path_merge_autostash (the_repository));remove_branch_state (the_repository,0 ); }Quoted 16 lines
I'm not sure about what the most intuitive behaviour is. I thought that this implementation would be the best but I'm not entirely sure. I'd appreciate some more discussion on this.
Thanks,
Denton
Quoted 7 lines
Best Wishes
Phillip
Quoted 3 lines
builtin/reset.c +1
-0+ }return update_ref_status; }
Re: [PATCH v3 17/19] merge: teach --autostash option
In reply to Denton LiuHi Denton
Quoted 4 lines · Denton Liu
Hi Phillip,
Thanks for your detailed review on the series. I've updated everything according to your comments except for the parts below:
That's great, I've added a few more comments below
Quoted 25 lines · Phillip Wood
Quoted 15 lines
Quoted 15 lines
branch.c +1
-0@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)unlink (git_path_merge_rr (r));unlink (git_path_merge_msg (r));unlink (git_path_merge_mode (r)); +apply_autostash (git_path_merge_autostash (r)); }void remove_branch_state (struct repository *r,int verbose)builtin/commit.c +0
-0Quoted 8 lines
builtin/commit.c int cmd_commit(int argc, const char **argv, const char *prefix) · line 1690
Quoted 5 lines
builtin/commit.c +2
-0@@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)unlink (git_path_merge_mode (the_repository));unlink (git_path_squash_msg (the_repository)); +apply_autostash (git_path_merge_autostash (the_repository)); +It's hard to tell from the limited context but do we want to run
commit_index_files()before applying the autostash?
Quoted 7 lines
I don't think it really matters which order we run it in. When we run
apply_autostash(), we only ever touch the working tree, not the index so it doesn't matter if it's run before or after. I'd prefer to keep it here because if we ever refactor this to useremove_merge_branch_state(), the person working on this will be able to perform the refactor more easily without having to worry about implicit ordering dependencies.
Thinking a bit more about this we definitely want to commit the index files before applying the stash as that is done in a separate process so we want our index written to disk first.
'git stash apply' does touch the index while it's merging and then tries to reset it to the pre-merge state if the merge has no conflicts. If the user runs
git add new-file
git merge --autostash branch
and new-file is not in branch then 'git stash apply' will add new-file to the index
Quoted 7 lines
Quoted 2 lines
I wonder if this should
be usingremove_merge_branch_state()instead of duplicating it as well.We can _almost_ use
remove_branch_state()even! Unfortunately,remove_merge_branch_state()callsunlink(git_path_merge_rr(r))which we cannot do sincerepo_rerere()relies on it later. Perhaps we can can moverepo_rerere()earlier?
I think we should move apply_autostash() down so that repo_rerere() is called with the index that we've committed before we apply the stash which might add conflicts or new files. We should probably run the post-commit and post-rewrite hooks before we apply the autostash as well to avoid changing the existing behaviour.
If we aim for something like
commit_index_files()
repo_rerere()
stash_oid = read_autostash_oid()
cleanup_branch_state()
run_post_commit_hook()
run_post_rewrite_hook()
print_commit_summary()
apply_autostash(stash_oid)
Then we keep the existing behaviour for rerere and the hooks, and the commit summary is printed before all the status junk that apply_autostash() spews out
We probably need to check the where 'git merge' applies the autostash as well to make sure it is writing the index to disk, calling the post-merge hook and printing any messages before applying the stash
Best Wishes
Phillip
Quoted 52 lines
Quoted 15 lines
Quoted 15 lines
builtin/reset.c +1
-1@@ -25,6 +25,7 @@#include "cache-tree.h" #include "submodule.h" #include "submodule-config.h" +#include "sequencer.h" #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (reset_type ==HARD && !update_ref_status && !quiet)print_new_head_line (lookup_commit_reference (the_repository, &oid)); } -if (!pathspec.nr)Quoted 8 lines
builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 445
Quoted 5 lines
builtin/reset.c +4
-0+if (!pathspec.nr) { +if (reset_type ==HARD ) +save_autostash (git_path_merge_autostash (the_repository)); +remove_branch_state (the_repository,0 );This removes the autostash file for all reset types but we only keep the stash in the case of 'reset --hard' which is confusing.
I was worried that this change would be controversial... The rationale behind this change was that with
reset --hard, we want to leave a clean working tree behind so we save it into the stash reflog. In all other cases,remove_branch_state()will apply the saved stash entry which should be fine since users don't expect a clean worktree with the other reset types.I considered saving the autostash in all cases of reset but
git merge --abortinvokesgit reset --mergebehind the scenes so we'd have to consider that. Perhaps we can make all resets save the stash entry and, in the case ofmerge --abort, we can add some extra logic to subvert this so that the stash entry is applied?I'm not sure about what the most intuitive behaviour is. I thought that this implementation would be the best but I'm not entirely sure. I'd appreciate some more discussion on this.
Thanks,
Denton
Quoted 7 lines
Best Wishes
Phillip
Quoted 3 lines
builtin/reset.c +1
-0+ }return update_ref_status; }
Re: [PATCH v3 17/19] merge: teach --autostash option
In reply to Phillip WoodHi Phillip,
Quoted 48 lines · Phillip Wood
Hi Denton
Quoted 45 lines · Denton Liu
Quoted 31 lines · Denton Liu
Quoted 15 lines
Quoted 15 lines
builtin/reset.c +1
-1@@ -25,6 +25,7 @@#include "cache-tree.h" #include "submodule.h" #include "submodule-config.h" +#include "sequencer.h" #define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)@@ -437,8 +438,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)if (reset_type ==HARD && !update_ref_status && !quiet)print_new_head_line (lookup_commit_reference (the_repository, &oid)); } -if (!pathspec.nr)Quoted 8 lines
builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 445
Quoted 5 lines
builtin/reset.c +4
-0+if (!pathspec.nr) { +if (reset_type ==HARD ) +save_autostash (git_path_merge_autostash (the_repository)); +remove_branch_state (the_repository,0 );This removes the autostash file for all reset types but we only keep the stash in the case of 'reset --hard' which is confusing.
I was worried that this change would be controversial... The rationale behind this change was that with
reset --hard, we want to leave a clean working tree behind so we save it into the stash reflog. In all other cases,remove_branch_state()will apply the saved stash entry which should be fine since users don't expect a clean worktree with the other reset types.Quoted 5 lines
I considered saving the autostash in all cases of reset but
git merge --abortinvokesgit reset --mergebehind the scenes so we'd have to consider that. Perhaps we can make all resets save the stash entry and, in the case ofmerge --abort, we can add some extra logic to subvert this so that the stash entry is applied?Perhaps something like this?
-- >8 --
commit14d0b569cb7675f00d32d3d7fad7564fcaeca458Author: Denton Liu <[email protected]> Date: Fri Apr 3 06:50:34 2020 -0400
Quoted 7 lines
Quoted 3 lines
squash! merge: teach --autostash option
Stash is saved when any reset is run, when git merge --abort is run, stash is applied.I think this is the easiest behavior to understand, it avoids changing the behavior of reset, in particular it stops 'reset --mixed/--soft' from suddenly starting to touch the working tree.
Great, I'll use this approach then.
Quoted 14 lines
Quoted 14 lines
builtin/merge.c +2
-0@@ -1242,6 +1242,8 @@ static int merging_a_throwaway_tag(struct commit *commit)return is_throwaway_tag; } +static GIT_PATH_FUNC(git_path_merge_autostash_saved, "MERGE_AUTOSTASH_SAVED") + int cmd_merge(int argc, const char **argv, const char *prefix) {struct object_id result_tree, stash, head_oid;@@ -1295,9 +1297,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)if (!file_exists (git_path_merge_head (the_repository)))
Quoted 8 lines
builtin/merge.c int cmd_merge(int argc, const char **argv, const char *prefix) · line 1303
Quoted 5 lines
builtin/merge.c +4
-0die (_ ("There is no merge to abort (MERGE_HEAD missing)." )); +if (file_exists (git_path_merge_autostash (the_repository))) { +if (rename (git_path_merge_autostash (the_repository), +git_path_merge_autostash_saved ())) +die_errno (_ ("failed to rename autostash" ));This is a bit of a performance, can't we just remember the stash oid in a variable and tweak the apply code?
This makes me a little bit sad because we basically have to duplicate the file-handling portion of apply_save_autostash() but I agree that doing your way should be better.
Quoted 12 lines
builtin/merge.c int cmd_merge(int argc, const char **argv, const char *prefix) · line 1309
Quoted 7 lines
builtin/merge.c +4
-1+ } +/* Invoke 'git reset --merge' */ ret =cmd_reset (nargc, nargv, prefix); -apply_autostash (git_path_merge_autostash (the_repository)); + +apply_autostash (git_path_merge_autostash_saved ());Calling
cmd_reset()was already a bit dodgy by our normal rules, now we're calling other functions after it though I guess given the current autostash implementation it's mostly in a separate process.
Quoted 2 lines
I think this is a good direction to go in
BTW what message gets printed when the stash is saved?
This is the message that is printed:
$ git reset --hard HEAD is now at c4c4222 commit 1 Autostash exists; creating a new stash entry. Your changes are safe in the stash. You can run "git stash pop" or "git stash drop" at any time.
Thanks,
Denton
Quoted 36 lines
Best Wishes
Phillip
Quoted 32 lines
builtin/merge.c +0
-0goto done; }builtin/reset.c +1
-3@@ -439,9 +439,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)print_new_head_line (lookup_commit_reference (the_repository, &oid)); }if (!pathspec.nr) { -if (reset_type ==HARD ) -save_autostash (git_path_merge_autostash (the_repository)); - +save_autostash (git_path_merge_autostash (the_repository));remove_branch_state (the_repository,0 ); }Quoted 16 lines
I'm not sure about what the most intuitive behaviour is. I thought that this implementation would be the best but I'm not entirely sure. I'd appreciate some more discussion on this.
Thanks,
Denton
Quoted 7 lines
Best Wishes
Phillip
Quoted 3 lines
builtin/reset.c +1
-0+ }return update_ref_status; }
Re: [PATCH v3 17/19] merge: teach --autostash option
In reply to Phillip WoodHi Phillip,
Quoted 70 lines · Phillip Wood
Quoted 25 lines · Phillip Wood
Quoted 15 lines
Quoted 15 lines
branch.c +1
-0@@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)unlink (git_path_merge_rr (r));unlink (git_path_merge_msg (r));unlink (git_path_merge_mode (r)); +apply_autostash (git_path_merge_autostash (r)); }void remove_branch_state (struct repository *r,int verbose)builtin/commit.c +0
-0Quoted 8 lines
builtin/commit.c int cmd_commit(int argc, const char **argv, const char *prefix) · line 1690
Quoted 5 lines
builtin/commit.c +2
-0@@ -1687,6 +1687,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)unlink (git_path_merge_mode (the_repository));unlink (git_path_squash_msg (the_repository)); +apply_autostash (git_path_merge_autostash (the_repository)); +It's hard to tell from the limited context but do we want to run
commit_index_files()before applying the autostash?Quoted 7 lines
I don't think it really matters which order we run it in. When we run
apply_autostash(), we only ever touch the working tree, not the index so it doesn't matter if it's run before or after. I'd prefer to keep it here because if we ever refactor this to useremove_merge_branch_state(), the person working on this will be able to perform the refactor more easily without having to worry about implicit ordering dependencies.Thinking a bit more about this we definitely want to commit the index files before applying the stash as that is done in a separate process so we want our index written to disk first.
'git stash apply' does touch the index while it's merging and then tries to reset it to the pre-merge state if the merge has no conflicts. If the user runs
git add new-file
git merge --autostash branch
and new-file is not in branch then 'git stash apply' will add new-file to the indexQuoted 7 lines
Quoted 2 lines
I wonder if this should
be usingremove_merge_branch_state()instead of duplicating it as well.We can _almost_ use
remove_branch_state()even! Unfortunately,remove_merge_branch_state()callsunlink(git_path_merge_rr(r))which we cannot do sincerepo_rerere()relies on it later. Perhaps we can can moverepo_rerere()earlier?I think we should move
apply_autostash()down so thatrepo_rerere()is called with the index that we've committed before we apply the stash which might add conflicts or new files. We should probably run the post-commit and post-rewrite hooks before we apply the autostash as well to avoid changing the existing behaviour.If we aim for something like
commit_index_files()
repo_rerere()
stash_oid=read_autostash_oid()
cleanup_branch_state()
run_post_commit_hook()
run_post_rewrite_hook()
print_commit_summary()
apply_autostash(stash_oid)
Quoted 3 lines
Then we keep the existing behaviour for rerere and the hooks, and the commit summary is printed before all the status junk that
apply_autostash()spews out
Makes sense, I'll make this change.
Quoted 3 lines
We probably need to check the where 'git merge' applies the autostash as well to make sure it is writing the index to disk, calling the post-merge hook and printing any messages before applying the stash
It currently applies the autostash in finish(). After reviewing it, I've moved the apply_autostash() call to the end of that function.
Actually, going back to review some more callsites of apply_autostash(), the one in remove_merge_branch_state() doesn't really make sense. We should really be calling save_autostash() in there so that we don't accidentally dirty the worktree unexpectedly. The bonus to this is that I've removed the save_autostash() call in git reset so it should just be able to call remove_branch_state() and have everything just work.
As a result of these changes, we now only introduce three callsites of apply_autostash(), which are all explicit:
1. At the end of git commit
2. At the end of finish() in git merge
3. At the end of git merge --abort
I'll send up a new version later today.
Thanks for helping shape this topic up,
Denton
Quoted 3 lines
Best Wishes
Phillip
PATCH v4 2/23 t7600: use test_write_lines()
In reply to Denton LiuIn t7600, we were rewriting printf '%s\n' ... to create files from parameters, one per line. However, we already have a function that wraps this for us: test_write_lines(). Rewrite these instances to use that function instead of open coding it.
--- t/t7600-merge.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-)
t/t7600-merge.sh +11 -11
@@ -29,15 +29,15 @@ Testing basic merge operations/option parsing. . ./test-lib.sh . "$TEST_DIRECTORY"/lib-gpg.sh -printf '%s\n' 1 2 3 4 5 6 7 8 9 >file -printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >file.1 -printf '%s\n' 1 2 3 4 '5 X' 6 7 8 9 >file.5 -printf '%s\n' 1 2 3 4 5 6 7 8 '9 X' >file.9 -printf '%s\n' 1 2 3 4 5 6 7 8 '9 Y' >file.9y -printf '%s\n' '1 X' 2 3 4 5 6 7 8 9 >result.1 -printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5 -printf '%s\n' '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9 -printf '%s\n' 1 2 3 4 5 6 7 8 '9 Z' >result.9z +test_write_lines 1 2 3 4 5 6 7 8 9 >file +test_write_lines '1 X' 2 3 4 5 6 7 8 9 >file.1 +test_write_lines 1 2 3 4 '5 X' 6 7 8 9 >file.5 +test_write_lines 1 2 3 4 5 6 7 8 '9 X' >file.9 +test_write_lines 1 2 3 4 5 6 7 8 '9 Y' >file.9y +test_write_lines '1 X' 2 3 4 5 6 7 8 9 >result.1 +test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 9 >result.1-5 +test_write_lines '1 X' 2 3 4 '5 X' 6 7 8 '9 X' >result.1-5-9 +test_write_lines 1 2 3 4 5 6 7 8 '9 Z' >result.9z create_merge_msgs () { echo "Merge tag 'c2'" >msg.1-5 &&
@@ -81,7 +81,7 @@ verify_head () { } verify_parents () { - printf '%s\n' "$@" >parents.expected && + test_write_lines "$@" >parents.expected && >parents.actual && i=1 && while test $i -le $#
@@ -95,7 +95,7 @@ verify_parents () { } verify_mergeheads () { - printf '%s\n' "$@" >mergehead.expected && + test_write_lines "$@" >mergehead.expected && while read sha1 rest do git rev-parse $sha1
Signature · 1 line
2.26.0.159.g23e2136ad0
v4 cover merge: learn --autostash
In reply to Denton LiuHi Phillip,
I implemented all of your comments. Patches 3-5, 11 need careful review as they're new. 'merge: teach --autostash option' also probably needs some more review as I've changed the callsites of apply_autostash() and save_autostash().
Alban reported[1] that he expected merge to have an --autostash option, just like rebase. Since there's not really any reason why merge can't have it, let's implement it in this patchset.
The majority of this patchset is spent refactoring. This is so that the actual implementation in merge is done as simply as possible.
Changes since v3:
* Replace 'sequencer: use file strbuf for read_oneliner()' with patches 3-5
* Rename flag to READ_ONELINER_WARN_MISSING
* Add 'sequencer: rename stash_sha1 to stash_oid'
* Many changes to 'merge: teach --autostash option'
Changes since v2:
* Change patch 1 to refer to ASCII order[2]
* Make read_oneliner() accept a collective flags arg instead of two separate args[3]
* git reset --hard now leaves the worktree clean[4]
* Rebased against 'master' since the base is quite old and a new release is coming out soon
Changes since v1:
* Completely changed how the refactoring was done
* More tests and documentation
[1]: https://github.com/gitgitgadget/git/issues/394
[2]: https://lore.kernel.org/git/[email protected]/
[3]: https://lore.kernel.org/git/[email protected]/
[4]: https://lore.kernel.org/git/20200123042906.GA29009@generichostname/
Denton Liu (23):
Makefile: ASCII-sort += lists
t7600: use test_write_lines()
sequencer: stop leaking buf
sequencer: reuse strbuf_trim() in read_oneliner()
sequencer: make file exists check more efficient
sequencer: make read_oneliner() accept flags
sequencer: configurably warn on non-existent files
sequencer: make read_oneliner() extern
rebase: use read_oneliner()
sequencer: make apply_rebase() accept a path
sequencer: rename stash_sha1 to stash_oid
rebase: use apply_autostash() from sequencer.c
rebase: generify reset_head()
reset: extract reset_head() from rebase
rebase: extract create_autostash()
rebase: generify create_autostash()
sequencer: extract perform_autostash() from rebase
sequencer: unlink autostash in apply_autostash()
sequencer: implement save_autostash()
sequencer: implement apply_autostash_oid()
merge: teach --autostash option
t5520: make test_pull_autostash() accept expect_parent_num
pull: pass --autostash to merge
Documentation/config/merge.txt | 10 + Documentation/git-merge.txt | 6 +- Documentation/git-pull.txt | 9 - Documentation/merge-options.txt | 8 + Makefile | 78 ++++---- branch.c | 1 + builtin/commit.c | 2 + builtin/merge.c | 27 +++ builtin/pull.c | 9 +- builtin/rebase.c | 312 +++++--------------------------- builtin/reset.c | 4 +- parse-options.h | 1 + path.c | 1 + path.h | 4 +- reset.c | 140 ++++++++++++++ reset.h | 20 ++ sequencer.c | 187 +++++++++++++------ sequencer.h | 20 ++ t/t3033-merge-toplevel.sh | 22 +++ t/t5520-pull.sh | 57 ++++-- t/t7600-merge.sh | 140 ++++++++++++-- 21 files changed, 653 insertions(+), 405 deletions(-) create mode 100644 reset.c create mode 100644 reset.h
Range-diff against v3:
1: 67511a47b2 = 1: c26771e934 Makefile: ASCII-sort += lists 2: 6d76cea5b4 = 2: 059e0e8e43 t7600: use test_write_lines() 3: 7c254b34b0 < -: ---------- sequencer: use file strbuf for read_oneliner() -: ---------- > 3: 76585e5b13 sequencer: stop leaking buf -: ---------- > 4: c7a3cfa200 sequencer: reuse strbuf_trim() in read_oneliner() -: ---------- > 5: 58da8e9555 sequencer: make file exists check more efficient 4: dc88da2ebf ! 6: 6ead240957 sequencer: make read_oneliner() accept flags
@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
+
/*
- * Reads a file that was presumably written by a shell script, i.e. with an
- * end-of-line marker that needs to be stripped.
+ * Resets a strbuf then reads a file that was presumably written by a shell
+ * script, i.e. with an end-of-line marker that needs to be stripped.
@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,
* Returns 1 if the file was read, 0 if it could not be read or does not exist. */
@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
- const char *path, int skip_if_empty)
+ const char *path, unsigned flags)
{
- int ret = 0;
- struct strbuf file_buf = STRBUF_INIT;
+ strbuf_reset(buf);
+ if (strbuf_read_file(buf, path, 0) < 0) {
@@ sequencer.c: static int read_oneliner(struct strbuf *buf,
- strbuf_trim_trailing_newline(&file_buf);
+ strbuf_trim(buf);
-- if (skip_if_empty && !file_buf.len)
-+ if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !file_buf.len)
- goto done;
+- if (skip_if_empty && !buf->len)
++ if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && !buf->len)
+ return 0;
- strbuf_addbuf(buf, &file_buf);
+ return 1;
@@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
- if (is_rebase_i(opts)) {
struct strbuf buf = STRBUF_INIT;
+ int ret = 0;
- if (read_oneliner(&buf, rebase_path_gpg_sign_opt(), 1)) {
+ if (read_oneliner(&buf, rebase_path_gpg_sign_opt(),
@@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
strbuf_reset(&buf);
else {
@@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
- strbuf_reset(&buf);
+ }
}
- if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
@@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
opts->allow_rerere_auto = RERERE_AUTOUPDATE;
else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
@@ sequencer.c: static int read_populate_opts(struct replay_opts *opts)
- strbuf_release(&buf);
+ read_strategy_opts(opts, &buf);
if (read_oneliner(&opts->current_fixups,
- rebase_path_current_fixups(), 1)) {
5: 7702543d91 < -: ---------- sequencer: configurably warn on non-existent files -: ---------- > 7: 2e7922b259 sequencer: configurably warn on non-existent files 6: 361bc2e885 ! 8: 6c3c37994b sequencer: make read_oneliner() extern
@@ Commit message
Instead of hiding it as a static function within sequencer.c, extern it so that it can be reused by others.
+ This patch is best viewed with --color-moved.
+
## sequencer.c ##
@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *filename,
return 0;
}
-#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
--#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
+-#define READ_ONELINER_WARN_MISSING (1 << 1)
-
-/*
-- * Reads a file that was presumably written by a shell script, i.e. with an -- * end-of-line marker that needs to be stripped.
+- * Resets a strbuf then reads a file that was presumably written by a shell +- * script, i.e. with an end-of-line marker that needs to be stripped.
- *
- * Note that only the last end-of-line marker is stripped, consistent with the
- * behavior of "$(cat path)" in a shell script.
@@ sequencer.c: static int write_message(const void *buf, size_t len, const char *f
+int read_oneliner(struct strbuf *buf,
const char *path, unsigned flags)
{
- int ret = 0;
+ strbuf_reset(buf);
## sequencer.h ##
@@ sequencer.h: void print_commit_summary(struct repository *repo,
@@ sequencer.h: void print_commit_summary(struct repository *repo,
unsigned int flags);
+#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0)
-+#define READ_ONELINER_WARN_NON_EXISTENCE (1 << 1)
++#define READ_ONELINER_WARN_MISSING (1 << 1)
+
+/*
-+ * Reads a file that was presumably written by a shell script, i.e. with an -+ * end-of-line marker that needs to be stripped.
++ * Resets a strbuf then reads a file that was presumably written by a shell ++ * script, i.e. with an end-of-line marker that needs to be stripped.
+ *
+ * Note that only the last end-of-line marker is stripped, consistent with the
+ * behavior of "$(cat path)" in a shell script.
+ *
-+ * Returns 1 if the file was read, 0 if it could not be read.
++ * Returns 1 if the file was read, 0 if it could not be read or does not exist.
+ */
+int read_oneliner(struct strbuf *buf,
+ const char *path, unsigned flags);
7: 47816abff1 ! 9: 689f34a2a5 rebase: use read_oneliner()
@@ Commit message
Since in sequencer.c, read_one() basically duplicates the functionality
of read_oneliner(), reduce code duplication by replacing read_one() with
- read_oneliner().
+ read_oneliner(). Also, delete strbuf_reset() calls prior to
+ read_oneliner() as read_oneliner() already resets the strbuf.
- This was done with the following Coccinelle script
+ This was mostly done with the following Coccinelle script
@@
expression a, b;
@@ Commit message
- read_one(a, b)
+ !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)
- and long lines were manually broken up.
+ Long lines were broken up and strbuf_reset()s were deleted manually.
## builtin/rebase.c ##
@@ builtin/rebase.c: static const char *state_dir_path(const char *filename, struct rebase_options *o
@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
- if (read_one(state_dir_path("head-name", opts), &head_name) ||
- read_one(state_dir_path("onto", opts), &buf))
+ if (!read_oneliner(&head_name, state_dir_path("head-name", opts),
-+ READ_ONELINER_WARN_NON_EXISTENCE) ||
++ READ_ONELINER_WARN_MISSING) ||
+ !read_oneliner(&buf, state_dir_path("onto", opts),
-+ READ_ONELINER_WARN_NON_EXISTENCE))
++ READ_ONELINER_WARN_MISSING))
return -1;
opts->head_name = starts_with(head_name.buf, "refs/") ?
xstrdup(head_name.buf) : NULL;
@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
+ * head. Fall back to reading from head to cover for the case that the
+ * user upgraded git with an ongoing interactive rebase.
*/
- strbuf_reset(&buf);
+- strbuf_reset(&buf);
if (file_exists(state_dir_path("orig-head", opts))) {
- if (read_one(state_dir_path("orig-head", opts), &buf))
+ if (!read_oneliner(&buf, state_dir_path("orig-head", opts),
-+ READ_ONELINER_WARN_NON_EXISTENCE))
++ READ_ONELINER_WARN_MISSING))
return -1;
- } else if (read_one(state_dir_path("head", opts), &buf))
+ } else if (!read_oneliner(&buf, state_dir_path("head", opts),
-+ READ_ONELINER_WARN_NON_EXISTENCE))
++ READ_ONELINER_WARN_MISSING))
return -1;
if (get_oid(buf.buf, &opts->orig_head))
return error(_("invalid orig-head: '%s'"), buf.buf);
@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
+ }
if (file_exists(state_dir_path("allow_rerere_autoupdate", opts))) {
- strbuf_reset(&buf);
+- strbuf_reset(&buf);
- if (read_one(state_dir_path("allow_rerere_autoupdate", opts),
- &buf))
+ if (!read_oneliner(&buf, state_dir_path("allow_rerere_autoupdate", opts),
-+ READ_ONELINER_WARN_NON_EXISTENCE))
++ READ_ONELINER_WARN_MISSING))
return -1;
if (!strcmp(buf.buf, "--rerere-autoupdate"))
opts->allow_rerere_autoupdate = RERERE_AUTOUPDATE;
@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
+ }
if (file_exists(state_dir_path("gpg_sign_opt", opts))) {
- strbuf_reset(&buf);
+- strbuf_reset(&buf);
- if (read_one(state_dir_path("gpg_sign_opt", opts),
- &buf))
+ if (!read_oneliner(&buf, state_dir_path("gpg_sign_opt", opts),
-+ READ_ONELINER_WARN_NON_EXISTENCE))
++ READ_ONELINER_WARN_MISSING))
return -1;
free(opts->gpg_sign_opt);
opts->gpg_sign_opt = xstrdup(buf.buf);
-@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
+ }
if (file_exists(state_dir_path("strategy", opts))) {
- strbuf_reset(&buf);
+- strbuf_reset(&buf);
- if (read_one(state_dir_path("strategy", opts), &buf))
+ if (!read_oneliner(&buf, state_dir_path("strategy", opts),
-+ READ_ONELINER_WARN_NON_EXISTENCE))
++ READ_ONELINER_WARN_MISSING))
return -1;
free(opts->strategy);
opts->strategy = xstrdup(buf.buf);
-@@ builtin/rebase.c: static int read_basic_state(struct rebase_options *opts)
+ }
if (file_exists(state_dir_path("strategy_opts", opts))) {
- strbuf_reset(&buf);
+- strbuf_reset(&buf);
- if (read_one(state_dir_path("strategy_opts", opts), &buf))
+ if (!read_oneliner(&buf, state_dir_path("strategy_opts", opts),
-+ READ_ONELINER_WARN_NON_EXISTENCE))
++ READ_ONELINER_WARN_MISSING))
return -1;
free(opts->strategy_opts);
opts->strategy_opts = xstrdup(buf.buf);
@@ builtin/rebase.c: static int apply_autostash(struct rebase_options *opts)
return 0;
- if (read_one(path, &autostash))
-+ if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
++ if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))
return error(_("Could not read '%s'"), path);
/* Ensure that the hash is not mistaken for a number */
strbuf_addstr(&autostash, "^0");
8: 7e9a86c337 = 10: e0b8e409af sequencer: make apply_rebase() accept a path
-: ---------- > 11: a52583beff sequencer: rename stash_sha1 to stash_oid
9: 76a486d8b0 ! 12: 389b17df33 rebase: use apply_autostash() from sequencer.c@@ Commit message
The apply_autostash() function in builtin/rebase.c is similar enough to the apply_autostash() function in sequencer.c that they are almost
- interchangeable. Make the sequencer.c version extern and use it in
- rebase.
+ interchangeable, except for the type of arg they accept. Make the
+ sequencer.c version extern and use it in rebase.
The rebase version was introduced in 6defce2b02 (builtin rebase: support --autostash option, 2018-09-04) as part of the shell to C conversion.
@@ builtin/rebase.c: static int rebase_write_basic_state(struct rebase_options *opt
- if (!file_exists(path))
- return 0;
-
-- if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_NON_EXISTENCE))
+- if (!read_oneliner(&autostash, path, READ_ONELINER_WARN_MISSING))
- return error(_("Could not read '%s'"), path);
- /* Ensure that the hash is not mistaken for a number */
- strbuf_addstr(&autostash, "^0");
@@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
-static int apply_autostash(const char *path)
+int apply_autostash(const char *path)
{
- struct strbuf stash_sha1 = STRBUF_INIT;
+ struct strbuf stash_oid = STRBUF_INIT;
struct child_process child = CHILD_PROCESS_INIT;
## sequencer.h ##
10: 6d37e8049b = 13: fe57e21dbc rebase: generify reset_head() 11: 18e1900a16 = 14: 045668620d reset: extract reset_head() from rebase 12: 21e228920e = 15: d5f51cd80e rebase: extract create_autostash() 13: 7e2eab5203 = 16: 9ab10d23d4 rebase: generify create_autostash() 14: fddf506115 ! 17: 26cca49be6 sequencer: extract perform_autostash() from rebase
@@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list,
+
int apply_autostash(const char *path)
{
- struct strbuf stash_sha1 = STRBUF_INIT;
+ struct strbuf stash_oid = STRBUF_INIT;
## sequencer.h ##
@@ sequencer.h: void commit_post_rewrite(struct repository *r,
15: 9394c7ae30 ! 18: e703022fda sequencer: unlink autostash in apply_autostash()
@@ sequencer.c: int apply_autostash(const char *path)
}
+ unlink(path);
- strbuf_release(&stash_sha1);
+ strbuf_release(&stash_oid);
return ret;
}
16: 0035b84388 ! 19: 75dc3f10a1 sequencer: implement save_autostash()
@@ Commit message
apply_save_autostash() and use it to implement save_autostash(). This
function will be used in a future commit.
+ The difference between save_autostash() and apply_autostash() is that
+ the latter does not try to apply the stash. It skips that step and
+ just stores the created entry in the stash reflog.
+
## sequencer.c ##
@@ sequencer.c: void create_autostash(struct repository *r, const char *path,
strbuf_release(&buf);
@@ sequencer.c: void create_autostash(struct repository *r, const char *path,
-int apply_autostash(const char *path)
+static int apply_save_autostash(const char *path, int attempt_apply)
{
- struct strbuf stash_sha1 = STRBUF_INIT;
+ struct strbuf stash_oid = STRBUF_INIT;
struct child_process child = CHILD_PROCESS_INIT;
@@ sequencer.c: int apply_autostash(const char *path)
}
- strbuf_trim(&stash_sha1);
+ strbuf_trim(&stash_oid);
- child.git_cmd = 1;
- child.no_stdout = 1;
- child.no_stderr = 1;
- argv_array_push(&child.args, "stash");
- argv_array_push(&child.args, "apply");
-- argv_array_push(&child.args, stash_sha1.buf);
+- argv_array_push(&child.args, stash_oid.buf);
- if (!run_command(&child))
+ if (attempt_apply) {
+ child.git_cmd = 1;
@@ sequencer.c: int apply_autostash(const char *path)
+ child.no_stderr = 1;
+ argv_array_push(&child.args, "stash");
+ argv_array_push(&child.args, "apply");
-+ argv_array_push(&child.args, stash_sha1.buf);
++ argv_array_push(&child.args, stash_oid.buf);
+ ret = run_command(&child);
+ }
+
@@ sequencer.c: int apply_autostash(const char *path)
else {
struct child_process store = CHILD_PROCESS_INIT;
@@ sequencer.c: int apply_autostash(const char *path)
- ret = error(_("cannot store %s"), stash_sha1.buf);
+ ret = error(_("cannot store %s"), stash_oid.buf);
else
fprintf(stderr,
- _("Applying autostash resulted in conflicts.\n"
-: ---------- > 20: 598ddea6c1 sequencer: implement apply_autostash_oid()
17: 085f8e0dd2 ! 21: 7adf794192 merge: teach --autostash option @@ Commit message
git fetch ...
git merge --autostash FETCH_HEAD
- When git reset --hard is run to abort a merge, the working tree will
- be left in a clean state, as expected, with the autostash pushed onto
- the stash stack.
+ When an autostash is generated, it is automatically reapplied to the
+ worktree only in three explicit situations:
+
+ 1. An incomplete merge is commit using git commit.
+ 2. A merge completes successfully.
+ 3. A merge is aborted using git merge --abort.
+
+ In all other situations where the merge state is removed using
+ remove_merge_branch_state() such as aborting a merge via
+ git reset --hard, the autostash is saved into the stash reflog
+ instead keeping the worktree clean.
@@ Documentation/config/merge.txt: merge.stat::
+merge.autoStash::
+ When set to true, automatically create a temporary stash entry
+ before the operation begins, and apply it after the operation
-+ ends. This means that you can run rebase on a dirty worktree.
++ ends. This means that you can run merge on a dirty worktree.
+ However, use with care: the final stash application after a
-+ successful rebase might result in non-trivial conflicts.
++ successful merge might result in non-trivial conflicts.
+ This option can be overridden by the --no-autostash and
+ --autostash options of linkgit:git-merge[1].
+ Defaults to false.
@@ Documentation/config/merge.txt: merge.stat::
Controls which merge tool is used by linkgit:git-mergetool[1]. The list below shows the valid built-in values.
+ ## Documentation/git-merge.txt ##
+@@ Documentation/git-merge.txt: will be appended to the specified message.
+
+ --abort::
+ Abort the current conflict resolution process, and
+- try to reconstruct the pre-merge state.
++ try to reconstruct the pre-merge state. If an autostash entry is
++ present, apply it back to the worktree.
+ +
+ If there were uncommitted worktree changes present when the merge
+ started, 'git merge --abort' will in some cases be unable to
+@@ Documentation/git-merge.txt: reconstruct these changes. It is therefore recommended to always
+ commit or stash your changes before running 'git merge'.
+ +
+ 'git merge --abort' is equivalent to 'git reset --merge' when
+-MERGE_HEAD is present.
++MERGE_HEAD is present unless MERGE_AUTOSTASH is also present where
++the stash entry is applied to the worktree.
+
+ --quit::
+ Forget about the current merge in progress. Leave the index
+
## Documentation/merge-options.txt ##
@@ Documentation/merge-options.txt: ifndef::git-pull[]
Note that not all merge strategies may support progress
@@ Documentation/merge-options.txt: ifndef::git-pull[]
+--no-autostash::
+ Automatically create a temporary stash entry before the operation
+ begins, and apply it after the operation ends. This means
-+ that you can run rebase on a dirty worktree. However, use
++ that you can run the operation on a dirty worktree. However, use
+ with care: the final stash application after a successful
-+ rebase might result in non-trivial conflicts.
++ merge might result in non-trivial conflicts.
+
endif::git-pull[]
@@ branch.c: void remove_merge_branch_state(struct repository *r)
unlink(git_path_merge_rr(r)); unlink(git_path_merge_msg(r)); unlink(git_path_merge_mode(r));
-+ apply_autostash(git_path_merge_autostash(r));
++ save_autostash(git_path_merge_autostash(r));
}
void remove_branch_state(struct repository *r, int verbose)
## builtin/commit.c ##
@@ builtin/commit.c: int cmd_commit(int argc, const char **argv, const char *prefix)
- unlink(git_path_merge_mode(the_repository));
- unlink(git_path_squash_msg(the_repository));
+ &oid, flags);
+ }
+ apply_autostash(git_path_merge_autostash(the_repository));
+
- if (commit_index_files())
- die(_("repository has been updated, but unable to write\n"
- "new_index file. Check that disk is not full and quota is\n"
+ UNLEAK(err);
+ UNLEAK(sb);
+ return 0;
## builtin/merge.c ##
@@ builtin/merge.c: static int show_progress = -1;
@@ builtin/merge.c: static struct option builtin_merge_options[] = {
OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1), { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-+ OPT_BOOL(0, "autostash", &autostash,
-+ N_("automatically stash/stash pop before and after")),
++ OPT_AUTOSTASH(&autostash),
OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")), OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
@@ builtin/merge.c: static void finish(struct commit *head_commit,
- strbuf_addf(&reflog_message, "%s: %s",
- getenv("GIT_REFLOG_ACTION"), msg);
- }
+ /* Run a post-merge hook */
+ run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
+
+ apply_autostash(git_path_merge_autostash(the_repository));
- if (squash) {
- squash_message(head_commit, remoteheads);
- } else {
+ strbuf_release(&reflog_message);
+ }
+
@@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void *cb)
return 0;
} else if (!strcmp(k, "gpg.mintrustlevel")) {
@@ builtin/merge.c: static int git_merge_config(const char *k, const char *v, void
status = fmt_merge_msg_config(k, v, cb);
@@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
+ if (abort_current_merge) {
+ int nargc = 2;
+ const char *nargv[] = {"reset", "--merge", NULL};
++ struct strbuf stash_oid = STRBUF_INIT;
+ if (orig_argc != 2)
+ usage_msg_opt(_("--abort expects no arguments"),
+@@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
+ if (!file_exists(git_path_merge_head(the_repository)))
+ die(_("There is no merge to abort (MERGE_HEAD missing)."));
+
++ if (read_oneliner(&stash_oid, git_path_merge_autostash(the_repository),
++ READ_ONELINER_SKIP_IF_EMPTY)) {
++ strbuf_trim(&stash_oid);
++ unlink(git_path_merge_autostash(the_repository));
++ }
++
/* Invoke 'git reset --merge' */
ret = cmd_reset(nargc, nargv, prefix);
-+ apply_autostash(git_path_merge_autostash(the_repository));
++
++ if (stash_oid.len)
++ apply_autostash_oid(stash_oid.buf);
++
++ strbuf_release(&stash_oid);
goto done;
}
@@ builtin/merge.c: int cmd_merge(int argc, const char **argv, const char *prefix)
git_committer_info(IDENT_STRICT);
+ ## builtin/rebase.c ##
+@@ builtin/rebase.c: int cmd_rebase(int argc, const char **argv, const char *prefix)
+ { OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
+ N_("GPG-sign commits"),
+ PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+- OPT_BOOL(0, "autostash", &options.autostash,
+- N_("automatically stash/stash pop before and after")),
++ OPT_AUTOSTASH(&options.autostash),
+ OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
+ N_("add exec lines after each commit of the "
+ "editable list")),
+
## builtin/reset.c ##
@@
#include "cache-tree.h"
@@ builtin/reset.c: int cmd_reset(int argc, const char **argv, const char *prefix)
}
- if (!pathspec.nr)
+ if (!pathspec.nr) {
-+ if (reset_type == HARD)
-+ save_autostash(git_path_merge_autostash(the_repository));
-+
remove_branch_state(the_repository, 0);
+ }
return update_ref_status;
}
+ ## parse-options.h ##
+@@ parse-options.h: int parse_opt_passthru_argv(const struct option *, const char *, int);
+ #define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
+ #define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file"))
+ #define OPT_PATHSPEC_FILE_NUL(v) OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character")) ++#define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after"))
+
+ #endif
+
## path.c ##
@@ path.c: REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
@@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '
+ test_cmp file.1 file
+'
+
-+test_expect_success 'completed merge with --no-commit and --autostash' '
++test_expect_success 'completed merge (git commit) with --no-commit and --autostash' '
+ git reset --hard c1 &&
+ git merge-file file file.orig file.9 &&
+ git diff >expect &&
@@ t/t7600-merge.sh: test_expect_success 'refresh the index before merging' '
+ test_cmp result.1-5-9 file
+'
+
++test_expect_success 'completed merge (git merge --continue) with --no-commit and --autostash' '
++ git reset --hard c1 &&
++ git merge-file file file.orig file.9 &&
++ git diff >expect &&
++ git merge --no-commit --autostash c2 &&
++ git stash show -p MERGE_AUTOSTASH >actual &&
++ test_cmp expect actual &&
++ git merge --continue 2>err &&
++ test_i18ngrep "Applied autostash." err &&
++ git show HEAD:file >merge-result &&
++ test_cmp result.1-5 merge-result &&
++ test_cmp result.1-5-9 file
++'
++
+test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '
+ git reset --hard c1 &&
+ git merge-file file file.orig file.9 &&
18: 4dabe214f2 = 22: f1e54622fb t5520: make test_pull_autostash() accept expect_parent_num 19: 9b6f16f1bf ! 23: 177c7e537b pull: pass --autostash to merge
@@ Documentation/merge-options.txt: ifndef::git-pull[]
Automatically create a temporary stash entry before the operation
@@ Documentation/merge-options.txt: ifndef::git-pull[]
with care: the final stash application after a successful
- rebase might result in non-trivial conflicts.
+ merge might result in non-trivial conflicts.
-endif::git-pull[]
-
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 3/23 sequencer: stop leaking buf
In read_populate_opts(), we call read_oneliner() _after_ calling strbuf_release(). This means that buf is leaked at the end of the function.
Always clean up the strbuf by going to done_rebase_i whether or not we return an error.
--- sequencer.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
sequencer.c +9 -4
@@ -2485,6 +2485,7 @@ static int read_populate_opts(struct replay_opts *opts) {if (is_rebase_i (opts)) {struct strbuf buf =STRBUF_INIT ; +int ret =0 ;if (read_oneliner (&buf,rebase_path_gpg_sign_opt (),1 )) {if (!starts_with (buf.buf,"-S" ))
@@ -2525,7 +2526,7 @@ static int read_populate_opts(struct replay_opts *opts) opts->keep_redundant_commits =1 ;read_strategy_opts (opts, &buf); -strbuf_release (&buf); +strbuf_reset (&buf);if (read_oneliner (&opts->current_fixups,rebase_path_current_fixups (),1 )) {
@@ -2538,12 +2539,16 @@ static int read_populate_opts(struct replay_opts *opts) }if (read_oneliner (&buf,rebase_path_squash_onto (),0 )) { -if (get_oid_hex (buf.buf, &opts->squash_onto) <0 ) -return error (_ ("unusable squash-onto" )); +if (get_oid_hex (buf.buf, &opts->squash_onto) <0 ) { + ret =error (_ ("unusable squash-onto" )); + goto done_rebase_i; + } opts->have_squash_onto =1 ; } -return 0 ; +done_rebase_i: +strbuf_release (&buf); +return ret; }if (!file_exists (git_path_opts_file ()))
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 5/23 sequencer: make file exists check more efficient
In reply to Denton LiuWe currently check whether a file exists and return early before reading the file. Instead of accessing the file twice, always read the file and check errno to see if the file doesn't exist.
--- sequencer.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)
sequencer.c +2 -5
@@ -431,13 +431,10 @@ static int write_message(const void *buf, size_t len, const char *filename,static int read_oneliner (struct strbuf *buf,const char *path,int skip_if_empty) { - -if (!file_exists (path)) -return 0 ; -strbuf_reset (buf);if (strbuf_read_file (buf, path,0 ) <0 ) { -warning_errno (_ ("could not read '%s'" ), path); +if (errno !=ENOENT && errno !=ENOTDIR ) +warning_errno (_ ("could not read '%s'" ), path);return 0 ; }
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 4/23 sequencer: reuse strbuf_trim() in read_oneliner()
In reply to Denton LiuIn the original read_oneliner() logic, we duplicated the logic for strbuf_trim_trailing_newline() with one exception: instead of preventing buffer accesses below index 0, it would prevent buffer accesses below index orig_len. Although this is correct, it isn't worth having the duplicated logic around.
Reset buf before using it then replace the trimming logic with strbuf_trim().
As a cleanup, remove all reset_strbuf()s that happen before read_oneliner() is called.
--- sequencer.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-)
sequencer.c +5 -13
@@ -420,8 +420,8 @@ static int write_message(const void *buf, size_t len, const char *filename, }/* -* Reads a file that was presumably written by a shell script, i.e. with an -* end-of-line marker that needs to be stripped. +* Resets a strbuf then reads a file that was presumably written by a shell +* script, i.e. with an end-of-line marker that needs to be stripped. * * Note that only the last end-of-line marker is stripped, consistent with the * behavior of "$(cat path)" in a shell script.
@@ -431,23 +431,19 @@ static int write_message(const void *buf, size_t len, const char *filename,static int read_oneliner (struct strbuf *buf,const char *path,int skip_if_empty) { -int orig_len = buf->len;if (!file_exists (path))return 0 ; +strbuf_reset (buf);if (strbuf_read_file (buf, path,0 ) <0 ) {warning_errno (_ ("could not read '%s'" ), path);return 0 ; } -if (buf->len > orig_len && buf->buf[buf->len -1 ] =='\n' ) { -if (--buf->len > orig_len && buf->buf[buf->len -1 ] =='\r' ) - --buf->len; - buf->buf[buf->len] ='\0' ; - } +strbuf_trim (buf); -if (skip_if_empty && buf->len == orig_len) +if (skip_if_empty && !buf->len)return 0 ;return 1 ;
@@ -2471,7 +2467,6 @@ void parse_strategy_opts(struct replay_opts *opts, char *raw_opts)static void read_strategy_opts (struct replay_opts *opts,struct strbuf *buf) { -strbuf_reset (buf);if (!read_oneliner (buf,rebase_path_strategy (),0 ))return ; opts->strategy =strbuf_detach (buf,NULL );
@@ -2494,7 +2489,6 @@ static int read_populate_opts(struct replay_opts *opts)free (opts->gpg_sign); opts->gpg_sign =xstrdup (buf.buf +2 ); } -strbuf_reset (&buf); }if (read_oneliner (&buf,rebase_path_allow_rerere_autoupdate (),1 )) {
@@ -2526,7 +2520,6 @@ static int read_populate_opts(struct replay_opts *opts) opts->keep_redundant_commits =1 ;read_strategy_opts (opts, &buf); -strbuf_reset (&buf);if (read_oneliner (&opts->current_fixups,rebase_path_current_fixups (),1 )) {
@@ -4006,7 +3999,6 @@ static int pick_commits(struct repository *r, res =error (_ ("could not read orig-head" )); goto cleanup_head_ref; } -strbuf_reset (&buf);if (!read_oneliner (&buf,rebase_path_onto (),0 )) { res =error (_ ("could not read 'onto'" )); goto cleanup_head_ref;
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 7/23 sequencer: configurably warn on non-existent files
In reply to Denton LiuIn the future, we plan on externing read_oneliner(). Future users of read_oneliner() will want the ability to output warnings in the event that the path doesn't exist. Introduce the READ_ONELINER_WARN_MISSING flag which, if active, would issue a warning when a file doesn't exist by skipping the !file_exists() check and letting strbuf_read_file() handle that case.
--- sequencer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
sequencer.c +3 -1
@@ -420,6 +420,7 @@ static int write_message(const void *buf, size_t len, const char *filename, }#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0) +#define READ_ONELINER_WARN_MISSING (1 << 1)/* * Resets a strbuf then reads a file that was presumably written by a shell
@@ -435,7 +436,8 @@ static int read_oneliner(struct strbuf *buf, {strbuf_reset (buf);if (strbuf_read_file (buf, path,0 ) <0 ) { -if (errno !=ENOENT && errno !=ENOTDIR ) +if ((flags &READ_ONELINER_WARN_MISSING ) || + (errno !=ENOENT && errno !=ENOTDIR ))warning_errno (_ ("could not read '%s'" ), path);return 0 ; }
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 1/23 Makefile: ASCII-sort += lists
In reply to Denton LiuThere are many += lists in the Makefile and, over time, they have gotten slightly out of ASCII order. Sort all += lists to bring them back in order.
ASCII sorting was chosen over strict alphabetical order even though, if we omit file prefixes, the lists aren't sorted in strictly alphabetical order (e.g. archive.o comes after archive-zip.o instead of before archive-tar.o). This is intentional because the purpose of maintaining the sorted list is to ensure line insertions are deterministic. By using ASCII ordering, it is more easily mechanically reproducible in the future, such as by using :sort in Vim.
This patch is best viewed with --color-moved.
--- Makefile | 77 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 38 deletions(-)
Makefile +39 -38
@@ -604,12 +604,12 @@ unexport CDPATH SCRIPT_SH += git-bisect.sh SCRIPT_SH += git-difftool--helper.sh SCRIPT_SH += git-filter-branch.sh +SCRIPT_SH += git-legacy-stash.sh SCRIPT_SH += git-merge-octopus.sh SCRIPT_SH += git-merge-one-file.sh SCRIPT_SH += git-merge-resolve.sh SCRIPT_SH += git-mergetool.sh SCRIPT_SH += git-quiltimport.sh -SCRIPT_SH += git-legacy-stash.sh SCRIPT_SH += git-request-pull.sh SCRIPT_SH += git-submodule.sh SCRIPT_SH += git-web--browse.sh
@@ -617,8 +617,8 @@ SCRIPT_SH += git-web--browse.sh SCRIPT_LIB += git-mergetool--lib SCRIPT_LIB += git-parse-remote SCRIPT_LIB += git-rebase--preserve-merges -SCRIPT_LIB += git-sh-setup SCRIPT_LIB += git-sh-i18n +SCRIPT_LIB += git-sh-setup SCRIPT_PERL += git-add--interactive.perl SCRIPT_PERL += git-archimport.perl
@@ -686,9 +686,9 @@ PROGRAM_OBJS += daemon.o PROGRAM_OBJS += fast-import.o PROGRAM_OBJS += http-backend.o PROGRAM_OBJS += imap-send.o +PROGRAM_OBJS += remote-testsvn.o PROGRAM_OBJS += sh-i18n--envsubst.o PROGRAM_OBJS += shell.o -PROGRAM_OBJS += remote-testsvn.o # Binary suffix, set to .exe for Windows builds X =
@@ -709,9 +709,9 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o TEST_BUILTINS_OBJS += test-example-decorate.o TEST_BUILTINS_OBJS += test-genrandom.o TEST_BUILTINS_OBJS += test-genzeros.o +TEST_BUILTINS_OBJS += test-hash-speed.o TEST_BUILTINS_OBJS += test-hash.o TEST_BUILTINS_OBJS += test-hashmap.o -TEST_BUILTINS_OBJS += test-hash-speed.o TEST_BUILTINS_OBJS += test-index-version.o TEST_BUILTINS_OBJS += test-json-writer.o TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@ -737,8 +737,8 @@ TEST_BUILTINS_OBJS += test-revision-walking.o TEST_BUILTINS_OBJS += test-run-command.o TEST_BUILTINS_OBJS += test-scrap-cache-tree.o TEST_BUILTINS_OBJS += test-serve-v2.o -TEST_BUILTINS_OBJS += test-sha1.o TEST_BUILTINS_OBJS += test-sha1-array.o +TEST_BUILTINS_OBJS += test-sha1.o TEST_BUILTINS_OBJS += test-sha256.o TEST_BUILTINS_OBJS += test-sigchain.o TEST_BUILTINS_OBJS += test-strcmp-offset.o
@@ -748,10 +748,10 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o TEST_BUILTINS_OBJS += test-subprocess.o TEST_BUILTINS_OBJS += test-trace2.o TEST_BUILTINS_OBJS += test-urlmatch-normalization.o -TEST_BUILTINS_OBJS += test-xml-encode.o TEST_BUILTINS_OBJS += test-wildmatch.o TEST_BUILTINS_OBJS += test-windows-named-pipe.o TEST_BUILTINS_OBJS += test-write-cache.o +TEST_BUILTINS_OBJS += test-xml-encode.o # Do not add more tests here unless they have extra dependencies. Add # them in TEST_BUILTINS_OBJS above.
@@ -788,10 +788,10 @@ OTHER_PROGRAMS = git$X # what test wrappers are needed and 'install' will install, in bindir BINDIR_PROGRAMS_NEED_X += git -BINDIR_PROGRAMS_NEED_X += git-upload-pack BINDIR_PROGRAMS_NEED_X += git-receive-pack -BINDIR_PROGRAMS_NEED_X += git-upload-archive BINDIR_PROGRAMS_NEED_X += git-shell +BINDIR_PROGRAMS_NEED_X += git-upload-archive +BINDIR_PROGRAMS_NEED_X += git-upload-pack BINDIR_PROGRAMS_NO_X += git-cvsserver
@@ -831,9 +831,9 @@ LIB_OBJS += advice.o LIB_OBJS += alias.o LIB_OBJS += alloc.o LIB_OBJS += apply.o -LIB_OBJS += archive.o LIB_OBJS += archive-tar.o LIB_OBJS += archive-zip.o +LIB_OBJS += archive.o LIB_OBJS += argv-array.o LIB_OBJS += attr.o LIB_OBJS += base85.o
@@ -849,9 +849,9 @@ LIB_OBJS += checkout.o LIB_OBJS += color.o LIB_OBJS += column.o LIB_OBJS += combine-diff.o -LIB_OBJS += commit.o LIB_OBJS += commit-graph.o LIB_OBJS += commit-reach.o +LIB_OBJS += commit.o LIB_OBJS += compat/obstack.o LIB_OBJS += compat/terminal.o LIB_OBJS += config.o
@@ -865,17 +865,17 @@ LIB_OBJS += ctype.o LIB_OBJS += date.o LIB_OBJS += decorate.o LIB_OBJS += delta-islands.o +LIB_OBJS += diff-delta.o +LIB_OBJS += diff-lib.o +LIB_OBJS += diff-no-index.o +LIB_OBJS += diff.o LIB_OBJS += diffcore-break.o LIB_OBJS += diffcore-delta.o LIB_OBJS += diffcore-order.o LIB_OBJS += diffcore-pickaxe.o LIB_OBJS += diffcore-rename.o -LIB_OBJS += diff-delta.o -LIB_OBJS += diff-lib.o -LIB_OBJS += diff-no-index.o -LIB_OBJS += diff.o -LIB_OBJS += dir.o LIB_OBJS += dir-iterator.o +LIB_OBJS += dir.o LIB_OBJS += editor.o LIB_OBJS += entry.o LIB_OBJS += environment.o
@@ -893,7 +893,6 @@ LIB_OBJS += gpg-interface.o LIB_OBJS += graph.o LIB_OBJS += grep.o LIB_OBJS += hashmap.o -LIB_OBJS += linear-assignment.o LIB_OBJS += help.o LIB_OBJS += hex.o LIB_OBJS += ident.o
@@ -903,9 +902,10 @@ LIB_OBJS += kwset.o LIB_OBJS += levenshtein.o LIB_OBJS += line-log.o LIB_OBJS += line-range.o -LIB_OBJS += list-objects.o -LIB_OBJS += list-objects-filter.o +LIB_OBJS += linear-assignment.o LIB_OBJS += list-objects-filter-options.o +LIB_OBJS += list-objects-filter.o +LIB_OBJS += list-objects.o LIB_OBJS += ll-merge.o LIB_OBJS += lockfile.o LIB_OBJS += log-tree.o
@@ -914,31 +914,31 @@ LIB_OBJS += mailinfo.o LIB_OBJS += mailmap.o LIB_OBJS += match-trees.o LIB_OBJS += mem-pool.o -LIB_OBJS += merge.o LIB_OBJS += merge-blobs.o LIB_OBJS += merge-recursive.o +LIB_OBJS += merge.o LIB_OBJS += mergesort.o LIB_OBJS += midx.o LIB_OBJS += name-hash.o LIB_OBJS += negotiator/default.o LIB_OBJS += negotiator/skipping.o -LIB_OBJS += notes.o LIB_OBJS += notes-cache.o LIB_OBJS += notes-merge.o LIB_OBJS += notes-utils.o +LIB_OBJS += notes.o LIB_OBJS += object.o LIB_OBJS += oidmap.o LIB_OBJS += oidset.o -LIB_OBJS += packfile.o -LIB_OBJS += pack-bitmap.o LIB_OBJS += pack-bitmap-write.o +LIB_OBJS += pack-bitmap.o LIB_OBJS += pack-check.o LIB_OBJS += pack-objects.o LIB_OBJS += pack-revindex.o LIB_OBJS += pack-write.o +LIB_OBJS += packfile.o LIB_OBJS += pager.o -LIB_OBJS += parse-options.o LIB_OBJS += parse-options-cb.o +LIB_OBJS += parse-options.o LIB_OBJS += patch-delta.o LIB_OBJS += patch-ids.o LIB_OBJS += path.o
@@ -955,8 +955,9 @@ LIB_OBJS += quote.o LIB_OBJS += range-diff.o LIB_OBJS += reachable.o LIB_OBJS += read-cache.o -LIB_OBJS += rebase.o LIB_OBJS += rebase-interactive.o +LIB_OBJS += rebase.o +LIB_OBJS += ref-filter.o LIB_OBJS += reflog-walk.o LIB_OBJS += refs.o LIB_OBJS += refs/files-backend.o
@@ -964,7 +965,6 @@ LIB_OBJS += refs/iterator.o LIB_OBJS += refs/packed-backend.o LIB_OBJS += refs/ref-cache.o LIB_OBJS += refspec.o -LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace-object.o LIB_OBJS += repo-settings.o
@@ -979,8 +979,8 @@ LIB_OBJS += serve.o LIB_OBJS += server-info.o LIB_OBJS += setup.o LIB_OBJS += sha1-array.o -LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1-file.o +LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1-name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o
@@ -990,9 +990,9 @@ LIB_OBJS += stable-qsort.o LIB_OBJS += strbuf.o LIB_OBJS += streaming.o LIB_OBJS += string-list.o -LIB_OBJS += submodule.o -LIB_OBJS += submodule-config.o LIB_OBJS += sub-process.o +LIB_OBJS += submodule-config.o +LIB_OBJS += submodule.o LIB_OBJS += symlinks.o LIB_OBJS += tag.o LIB_OBJS += tempfile.o
@@ -1011,11 +1011,11 @@ LIB_OBJS += trace2/tr2_tgt_normal.o LIB_OBJS += trace2/tr2_tgt_perf.o LIB_OBJS += trace2/tr2_tls.o LIB_OBJS += trailer.o -LIB_OBJS += transport.o LIB_OBJS += transport-helper.o +LIB_OBJS += transport.o LIB_OBJS += tree-diff.o -LIB_OBJS += tree.o LIB_OBJS += tree-walk.o +LIB_OBJS += tree.o LIB_OBJS += unpack-trees.o LIB_OBJS += upload-pack.o LIB_OBJS += url.o
@@ -1055,9 +1055,9 @@ BUILTIN_OBJS += builtin/checkout.o BUILTIN_OBJS += builtin/clean.o BUILTIN_OBJS += builtin/clone.o BUILTIN_OBJS += builtin/column.o +BUILTIN_OBJS += builtin/commit-graph.o BUILTIN_OBJS += builtin/commit-tree.o BUILTIN_OBJS += builtin/commit.o -BUILTIN_OBJS += builtin/commit-graph.o BUILTIN_OBJS += builtin/config.o BUILTIN_OBJS += builtin/count-objects.o BUILTIN_OBJS += builtin/credential.o
@@ -1088,13 +1088,13 @@ BUILTIN_OBJS += builtin/ls-remote.o BUILTIN_OBJS += builtin/ls-tree.o BUILTIN_OBJS += builtin/mailinfo.o BUILTIN_OBJS += builtin/mailsplit.o -BUILTIN_OBJS += builtin/merge.o BUILTIN_OBJS += builtin/merge-base.o BUILTIN_OBJS += builtin/merge-file.o BUILTIN_OBJS += builtin/merge-index.o BUILTIN_OBJS += builtin/merge-ours.o BUILTIN_OBJS += builtin/merge-recursive.o BUILTIN_OBJS += builtin/merge-tree.o +BUILTIN_OBJS += builtin/merge.o BUILTIN_OBJS += builtin/mktag.o BUILTIN_OBJS += builtin/mktree.o BUILTIN_OBJS += builtin/multi-pack-index.o
@@ -1114,9 +1114,9 @@ BUILTIN_OBJS += builtin/read-tree.o BUILTIN_OBJS += builtin/rebase.o BUILTIN_OBJS += builtin/receive-pack.o BUILTIN_OBJS += builtin/reflog.o -BUILTIN_OBJS += builtin/remote.o BUILTIN_OBJS += builtin/remote-ext.o BUILTIN_OBJS += builtin/remote-fd.o +BUILTIN_OBJS += builtin/remote.o BUILTIN_OBJS += builtin/repack.o BUILTIN_OBJS += builtin/replace.o BUILTIN_OBJS += builtin/rerere.o
@@ -2335,16 +2335,16 @@ reconfigure config.mak.autogen: config.status endif XDIFF_OBJS += xdiff/xdiffi.o -XDIFF_OBJS += xdiff/xprepare.o -XDIFF_OBJS += xdiff/xutils.o XDIFF_OBJS += xdiff/xemit.o +XDIFF_OBJS += xdiff/xhistogram.o XDIFF_OBJS += xdiff/xmerge.o XDIFF_OBJS += xdiff/xpatience.o -XDIFF_OBJS += xdiff/xhistogram.o +XDIFF_OBJS += xdiff/xprepare.o +XDIFF_OBJS += xdiff/xutils.o +VCSSVN_OBJS += vcs-svn/fast_export.o VCSSVN_OBJS += vcs-svn/line_buffer.o VCSSVN_OBJS += vcs-svn/sliding_window.o -VCSSVN_OBJS += vcs-svn/fast_export.o VCSSVN_OBJS += vcs-svn/svndiff.o VCSSVN_OBJS += vcs-svn/svndump.o
@@ -3152,9 +3152,10 @@ endif # ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) ALL_COMMANDS += git +ALL_COMMANDS += git-citool +ALL_COMMANDS += git-gui ALL_COMMANDS += gitk ALL_COMMANDS += gitweb -ALL_COMMANDS += git-gui git-citool .PHONY: check-docs check-docs::
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 9/23 rebase: use read_oneliner()
In reply to Denton LiuSince in sequencer.c, read_one() basically duplicates the functionality of read_oneliner(), reduce code duplication by replacing read_one() with read_oneliner(). Also, delete strbuf_reset() calls prior to read_oneliner() as read_oneliner() already resets the strbuf.
This was mostly done with the following Coccinelle script
@@ expression a, b; @@ - read_one(a, b) + !read_oneliner(b, a, READ_ONELINER_WARN_NON_EXISTENCE)
Long lines were broken up and strbuf_reset()s were deleted manually.
--- builtin/rebase.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-)
builtin/rebase.c +17 -25
@@ -586,15 +586,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *oreturn path.buf; } -/* Read one file, then strip line endings */ -static int read_one (const char *path,struct strbuf *buf) -{ -if (strbuf_read_file (buf, path,0 ) <0 ) -return error_errno (_ ("could not read '%s'" ), path); -strbuf_trim_trailing_newline (buf); -return 0 ; -} -/* Initialize the rebase options from the state directory. */ static int read_basic_state (struct rebase_options *opts) {
@@ -602,8 +593,10 @@ static int read_basic_state(struct rebase_options *opts)struct strbuf buf =STRBUF_INIT ;struct object_id oid; -if (read_one (state_dir_path ("head-name" , opts), &head_name) || -read_one (state_dir_path ("onto" , opts), &buf)) +if (!read_oneliner (&head_name,state_dir_path ("head-name" , opts), +READ_ONELINER_WARN_MISSING ) || + !read_oneliner (&buf,state_dir_path ("onto" , opts), +READ_ONELINER_WARN_MISSING ))return -1 ; opts->head_name =starts_with (head_name.buf,"refs/" ) ?xstrdup (head_name.buf) :NULL ;
@@ -617,11 +610,12 @@ static int read_basic_state(struct rebase_options *opts)* head. Fall back to reading from head to cover for the case that the * user upgraded git with an ongoing interactive rebase. */ -strbuf_reset (&buf);if (file_exists (state_dir_path ("orig-head" , opts))) { -if (read_one (state_dir_path ("orig-head" , opts), &buf)) +if (!read_oneliner (&buf,state_dir_path ("orig-head" , opts), +READ_ONELINER_WARN_MISSING ))return -1 ; - }else if (read_one (state_dir_path ("head" , opts), &buf)) + }else if (!read_oneliner (&buf,state_dir_path ("head" , opts), +READ_ONELINER_WARN_MISSING ))return -1 ;if (get_oid (buf.buf, &opts->orig_head))return error (_ ("invalid orig-head: '%s'" ), buf.buf);
@@ -640,9 +634,8 @@ static int read_basic_state(struct rebase_options *opts) }if (file_exists (state_dir_path ("allow_rerere_autoupdate" , opts))) { -strbuf_reset (&buf); -if (read_one (state_dir_path ("allow_rerere_autoupdate" , opts), - &buf)) +if (!read_oneliner (&buf,state_dir_path ("allow_rerere_autoupdate" , opts), +READ_ONELINER_WARN_MISSING ))return -1 ;if (!strcmp (buf.buf,"--rerere-autoupdate" )) opts->allow_rerere_autoupdate =RERERE_AUTOUPDATE ;
@@ -654,25 +647,24 @@ static int read_basic_state(struct rebase_options *opts) }if (file_exists (state_dir_path ("gpg_sign_opt" , opts))) { -strbuf_reset (&buf); -if (read_one (state_dir_path ("gpg_sign_opt" , opts), - &buf)) +if (!read_oneliner (&buf,state_dir_path ("gpg_sign_opt" , opts), +READ_ONELINER_WARN_MISSING ))return -1 ;free (opts->gpg_sign_opt); opts->gpg_sign_opt =xstrdup (buf.buf); }if (file_exists (state_dir_path ("strategy" , opts))) { -strbuf_reset (&buf); -if (read_one (state_dir_path ("strategy" , opts), &buf)) +if (!read_oneliner (&buf,state_dir_path ("strategy" , opts), +READ_ONELINER_WARN_MISSING ))return -1 ;free (opts->strategy); opts->strategy =xstrdup (buf.buf); }if (file_exists (state_dir_path ("strategy_opts" , opts))) { -strbuf_reset (&buf); -if (read_one (state_dir_path ("strategy_opts" , opts), &buf)) +if (!read_oneliner (&buf,state_dir_path ("strategy_opts" , opts), +READ_ONELINER_WARN_MISSING ))return -1 ;free (opts->strategy_opts); opts->strategy_opts =xstrdup (buf.buf);
@@ -724,7 +716,7 @@ static int apply_autostash(struct rebase_options *opts)if (!file_exists (path))return 0 ; -if (read_one (path, &autostash)) +if (!read_oneliner (&autostash, path,READ_ONELINER_WARN_MISSING ))return error (_ ("Could not read '%s'" ), path);/* Ensure that the hash is not mistaken for a number */ strbuf_addstr (&autostash,"^0" );
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 8/23 sequencer: make read_oneliner() extern
In reply to Denton LiuThe function read_oneliner() is a generally useful util function. Instead of hiding it as a static function within sequencer.c, extern it so that it can be reused by others.
This patch is best viewed with --color-moved.
--- sequencer.c | 14 +------------- sequencer.h | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 13 deletions(-)
sequencer.c +1 -13
@@ -419,19 +419,7 @@ static int write_message(const void *buf, size_t len, const char *filename,return 0 ; } -#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0) -#define READ_ONELINER_WARN_MISSING (1 << 1) - -/* -* Resets a strbuf then reads a file that was presumably written by a shell -* script, i.e. with an end-of-line marker that needs to be stripped. -* -* Note that only the last end-of-line marker is stripped, consistent with the -* behavior of "$(cat path)" in a shell script. -* -* Returns 1 if the file was read, 0 if it could not be read or does not exist. -*/ -static int read_oneliner (struct strbuf *buf, +int read_oneliner (struct strbuf *buf,const char *path,unsigned flags) {strbuf_reset (buf);
sequencer.h +14 -0
@@ -198,6 +198,20 @@ void print_commit_summary(struct repository *repo,const struct object_id *oid, unsigned int flags); +#define READ_ONELINER_SKIP_IF_EMPTY (1 << 0) +#define READ_ONELINER_WARN_MISSING (1 << 1) + +/* +* Resets a strbuf then reads a file that was presumably written by a shell +* script, i.e. with an end-of-line marker that needs to be stripped. +* +* Note that only the last end-of-line marker is stripped, consistent with the +* behavior of "$(cat path)" in a shell script. +* +* Returns 1 if the file was read, 0 if it could not be read or does not exist. +*/ +int read_oneliner (struct strbuf *buf, +const char *path,unsigned flags);int read_author_script (const char *path,char **name,char **email,char **date,int allow_missing);void parse_strategy_opts (struct replay_opts *opts,char *raw_opts);
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 12/23 rebase: use apply_autostash() from sequencer.c
In reply to Denton LiuThe apply_autostash() function in builtin/rebase.c is similar enough to the apply_autostash() function in sequencer.c that they are almost interchangeable, except for the type of arg they accept. Make the sequencer.c version extern and use it in rebase.
The rebase version was introduced in 6defce2b02 (builtin rebase: support --autostash option, 2018-09-04) as part of the shell to C conversion. It opted to duplicate the function because, at the time, there was another in-progress project converting interactive rebase from shell to C as well and they did not want to clash with them by refactoring sequencer.c version of apply_autostash(). Since both efforts are long done, we can freely combine them together now.
--- builtin/rebase.c | 49 ++---------------------------------------------- sequencer.c | 2 +- sequencer.h | 2 ++ 3 files changed, 5 insertions(+), 48 deletions(-)
builtin/rebase.c +2 -47
@@ -707,51 +707,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)return 0 ; } -static int apply_autostash (struct rebase_options *opts) -{ -const char *path =state_dir_path ("autostash" , opts); -struct strbuf autostash =STRBUF_INIT ; -struct child_process stash_apply =CHILD_PROCESS_INIT ; - -if (!file_exists (path)) -return 0 ; - -if (!read_oneliner (&autostash, path,READ_ONELINER_WARN_MISSING )) -return error (_ ("Could not read '%s'" ), path); -/* Ensure that the hash is not mistaken for a number */ -strbuf_addstr (&autostash,"^0" ); -argv_array_pushl (&stash_apply.args, -"stash" ,"apply" , autostash.buf,NULL ); - stash_apply.git_cmd =1 ; - stash_apply.no_stderr = stash_apply.no_stdout = - stash_apply.no_stdin =1 ; -if (!run_command (&stash_apply)) -printf (_ ("Applied autostash.\n" )); -else { -struct argv_array args =ARGV_ARRAY_INIT ; -int res =0 ; - -argv_array_pushl (&args, -"stash" ,"store" ,"-m" ,"autostash" ,"-q" , - autostash.buf,NULL ); -if (run_command_v_opt (args.argv,RUN_GIT_CMD )) - res =error (_ ("Cannot store %s" ), autostash.buf); -argv_array_clear (&args); -strbuf_release (&autostash); -if (res) -return res; - -fprintf (stderr, -_ ("Applying autostash resulted in conflicts.\n" -"Your changes are safe in the stash.\n" -"You can run \"git stash pop\" or \"git stash drop\" " -"at any time.\n" )); - } - -strbuf_release (&autostash); -return 0 ; -} -static int finish_rebase (struct rebase_options *opts) {struct strbuf dir =STRBUF_INIT ;
@@ -759,7 +714,7 @@ static int finish_rebase(struct rebase_options *opts)int ret =0 ;delete_ref (NULL ,"REBASE_HEAD" ,NULL ,REF_NO_DEREF ); -apply_autostash (opts); +apply_autostash (state_dir_path ("autostash" , opts));close_object_store (the_repository->objects);/* * We ignore errors in 'gc --auto', since the
@@ -1204,7 +1159,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action) }else if (status ==2 ) {struct strbuf dir =STRBUF_INIT ; -apply_autostash (opts); +apply_autostash (state_dir_path ("autostash" , opts));strbuf_addstr (&dir, opts->state_dir);remove_dir_recursively (&dir,0 );strbuf_release (&dir);
sequencer.c +1 -1
@@ -3649,7 +3649,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)return -1 ; } -static int apply_autostash (const char *path) +int apply_autostash (const char *path) {struct strbuf stash_oid =STRBUF_INIT ;struct child_process child =CHILD_PROCESS_INIT ;
sequencer.h +2 -0
@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,const struct commit *current_head, const struct object_id *new_head); +int apply_autostash (const char *path); +#define SUMMARY_INITIAL_COMMIT (1 << 0)#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1) void print_commit_summary(struct repository *repo,
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 11/23 sequencer: rename stash_sha1 to stash_oid
In reply to Denton LiuThe preferred terminology is to refer to object identifiers as "OIDs". Rename the stash_sha1 variable to stash_oid in order to conform to this.
--- sequencer.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
sequencer.c +8 -8
@@ -3651,23 +3651,23 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)static int apply_autostash (const char *path) { -struct strbuf stash_sha1 =STRBUF_INIT ; +struct strbuf stash_oid =STRBUF_INIT ;struct child_process child =CHILD_PROCESS_INIT ;int ret =0 ; -if (!read_oneliner (&stash_sha1, path, +if (!read_oneliner (&stash_oid, path,READ_ONELINER_SKIP_IF_EMPTY )) { -strbuf_release (&stash_sha1); +strbuf_release (&stash_oid);return 0 ; } -strbuf_trim (&stash_sha1); +strbuf_trim (&stash_oid); child.git_cmd =1 ; child.no_stdout =1 ; child.no_stderr =1 ;argv_array_push (&child.args,"stash" );argv_array_push (&child.args,"apply" ); -argv_array_push (&child.args, stash_sha1.buf); +argv_array_push (&child.args, stash_oid.buf);if (!run_command (&child))fprintf (stderr,_ ("Applied autostash.\n" ));else {
@@ -3679,9 +3679,9 @@ static int apply_autostash(const char *path)argv_array_push (&store.args,"-m" );argv_array_push (&store.args,"autostash" );argv_array_push (&store.args,"-q" ); -argv_array_push (&store.args, stash_sha1.buf); +argv_array_push (&store.args, stash_oid.buf);if (run_command (&store)) - ret =error (_ ("cannot store %s" ), stash_sha1.buf); + ret =error (_ ("cannot store %s" ), stash_oid.buf);else fprintf(stderr, _("Applying autostash resulted in conflicts.\n"
@@ -3690,7 +3690,7 @@ static int apply_autostash(const char *path) " \"git stash drop\" at any time.\n")); } -strbuf_release (&stash_sha1); +strbuf_release (&stash_oid);return ret; }
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 13/23 rebase: generify reset_head()
In reply to Denton LiuIn the future, we plan on lib-ifying reset_head() so we need it to be more generic. Make it more generic by making it accept a struct repository argument instead of implicitly using the non-repo functions. Also, make it accept a const char *default_reflog_action argument so that the default action of "rebase" isn't hardcoded in.
--- builtin/rebase.c | 64 ++++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 27 deletions(-)
builtin/rebase.c +37 -27
@@ -28,6 +28,8 @@#include "sequencer.h" #include "rebase-interactive.h" +#define DEFAULT_REFLOG_ACTION "rebase" +static char const *const builtin_rebase_usage[] = {N_ ("git rebase [-i] [options] [--exec <cmd>] " "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]" ),
@@ -767,9 +769,10 @@ static void add_var(struct strbuf *buf, const char *name, const char *value)#define RESET_HEAD_REFS_ONLY (1<<3)#define RESET_ORIG_HEAD (1<<4) -static int reset_head (struct object_id *oid,const char *action, +static int reset_head (struct repository *r,struct object_id *oid,const char *action,const char *switch_to_branch,unsigned flags, -const char *reflog_orig_head,const char *reflog_head) +const char *reflog_orig_head,const char *reflog_head, +const char *default_reflog_action) {unsigned detach_head = flags &RESET_HEAD_DETACH ;unsigned reset_hard = flags &RESET_HEAD_HARD ;
@@ -791,7 +794,7 @@ static int reset_head(struct object_id *oid, const char *action,if (switch_to_branch && !starts_with (switch_to_branch,"refs/" ))BUG ("Not a fully qualified branch: '%s'" , switch_to_branch); -if (!refs_only &&hold_locked_index (&lock,LOCK_REPORT_ON_ERROR ) <0 ) { +if (!refs_only &&repo_hold_locked_index (r, &lock,LOCK_REPORT_ON_ERROR ) <0 ) { ret =-1 ; goto leave_reset_head; }
@@ -810,26 +813,26 @@ static int reset_head(struct object_id *oid, const char *action,memset (&unpack_tree_opts,0 ,sizeof (unpack_tree_opts));setup_unpack_trees_porcelain (&unpack_tree_opts, action); unpack_tree_opts.head_idx =1 ; - unpack_tree_opts.src_index = the_repository->index; - unpack_tree_opts.dst_index = the_repository->index; + unpack_tree_opts.src_index = r->index; + unpack_tree_opts.dst_index = r->index; unpack_tree_opts.fn = reset_hard ? oneway_merge : twoway_merge; unpack_tree_opts.update =1 ; unpack_tree_opts.merge =1 ;if (!detach_head) unpack_tree_opts.reset =1 ; -if (repo_read_index_unmerged (the_repository) <0 ) { +if (repo_read_index_unmerged (r) <0 ) { ret =error (_ ("could not read index" )); goto leave_reset_head; } -if (!reset_hard && !fill_tree_descriptor (the_repository, &desc[nr++], &head_oid)) { +if (!reset_hard && !fill_tree_descriptor (r, &desc[nr++], &head_oid)) { ret =error (_ ("failed to find tree of %s" ),oid_to_hex (&head_oid)); goto leave_reset_head; } -if (!fill_tree_descriptor (the_repository, &desc[nr++], oid)) { +if (!fill_tree_descriptor (r, &desc[nr++], oid)) { ret =error (_ ("failed to find tree of %s" ),oid_to_hex (oid)); goto leave_reset_head; }
@@ -840,16 +843,16 @@ static int reset_head(struct object_id *oid, const char *action, } tree =parse_tree_indirect (oid); -prime_cache_tree (the_repository, the_repository->index, tree); +prime_cache_tree (r, r->index, tree); -if (write_locked_index (the_repository->index, &lock,COMMIT_LOCK ) <0 ) { +if (write_locked_index (r->index, &lock,COMMIT_LOCK ) <0 ) { ret =error (_ ("could not write index" )); goto leave_reset_head; } reset_head_refs: reflog_action =getenv (GIT_REFLOG_ACTION_ENVIRONMENT ); -strbuf_addf (&msg,"%s: " , reflog_action ? reflog_action :"rebase" ); +strbuf_addf (&msg,"%s: " , reflog_action ? reflog_action : default_reflog_action); prefix_len = msg.len;if (update_orig_head) {
@@ -911,8 +914,10 @@ static int move_to_original_branch(struct rebase_options *opts) opts->head_name, oid_to_hex(&opts->onto->object.oid));strbuf_addf (&head_reflog,"rebase finished: returning to %s" , opts->head_name); - ret =reset_head (NULL ,"" , opts->head_name,RESET_HEAD_REFS_ONLY , - orig_head_reflog.buf, head_reflog.buf); + ret =reset_head (the_repository,NULL ,"" , opts->head_name, +RESET_HEAD_REFS_ONLY , + orig_head_reflog.buf, head_reflog.buf, +DEFAULT_REFLOG_ACTION );strbuf_release (&orig_head_reflog);strbuf_release (&head_reflog);
@@ -1000,8 +1005,9 @@ static int run_am(struct rebase_options *opts)free (rebased_patches);argv_array_clear (&am.args); -reset_head (&opts->orig_head,"checkout" , opts->head_name,0 , -"HEAD" ,NULL ); +reset_head (the_repository, &opts->orig_head,"checkout" , + opts->head_name,0 , +"HEAD" ,NULL ,DEFAULT_REFLOG_ACTION ); error(_("\ngit encountered an error while preparing the " "patches to replay\n" "these revisions:\n"
@@ -1656,8 +1662,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)rerere_clear (the_repository, &merge_rr);string_list_clear (&merge_rr,1 ); -if (reset_head (NULL ,"reset" ,NULL ,RESET_HEAD_HARD , -NULL ,NULL ) <0 ) +if (reset_head (the_repository,NULL ,"reset" ,NULL ,RESET_HEAD_HARD , +NULL ,NULL ,DEFAULT_REFLOG_ACTION ) <0 )die (_ ("could not discard worktree changes" ));remove_branch_state (the_repository,0 );if (read_basic_state (&options))
@@ -1674,9 +1680,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)if (read_basic_state (&options))exit (1 ); -if (reset_head (&options.orig_head,"reset" , +if (reset_head (the_repository, &options.orig_head,"reset" , options.head_name,RESET_HEAD_HARD , -NULL ,NULL ) <0 ) +NULL ,NULL ,DEFAULT_REFLOG_ACTION ) <0 )die (_ ("could not move back to %s" ),oid_to_hex (&options.orig_head));remove_branch_state (the_repository,0 );
@@ -2068,8 +2074,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.state_dir);write_file (autostash,"%s" ,oid_to_hex (&oid));printf (_ ("Created autostash: %s\n" ), buf.buf); -if (reset_head (NULL ,"reset --hard" , -NULL ,RESET_HEAD_HARD ,NULL ,NULL ) <0 ) +if (reset_head (the_repository,NULL ,"reset --hard" , +NULL ,RESET_HEAD_HARD ,NULL ,NULL , +DEFAULT_REFLOG_ACTION ) <0 )die (_ ("could not reset --hard" )); if (discard_index(the_repository->index) < 0 ||
@@ -2109,10 +2116,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)strbuf_addf (&buf,"%s: checkout %s" ,getenv (GIT_REFLOG_ACTION_ENVIRONMENT ), options.switch_to); -if (reset_head (&options.orig_head,"checkout" , +if (reset_head (the_repository, + &options.orig_head,"checkout" , options.head_name,RESET_HEAD_RUN_POST_CHECKOUT_HOOK , -NULL , buf.buf) <0 ) { +NULL , buf.buf, +DEFAULT_REFLOG_ACTION ) <0 ) { ret = !!error (_ ("could not switch to " "%s" ), options.switch_to);
@@ -2184,10 +2193,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)strbuf_addf (&msg,"%s: checkout %s" ,getenv (GIT_REFLOG_ACTION_ENVIRONMENT ), options.onto_name); -if (reset_head (&options.onto->object.oid,"checkout" ,NULL , +if (reset_head (the_repository, &options.onto->object.oid,"checkout" ,NULL ,RESET_HEAD_DETACH |RESET_ORIG_HEAD |RESET_HEAD_RUN_POST_CHECKOUT_HOOK , -NULL , msg.buf)) +NULL , msg.buf,DEFAULT_REFLOG_ACTION ))die (_ ("Could not detach HEAD" ));strbuf_release (&msg);
@@ -2202,8 +2211,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)strbuf_addf (&msg,"rebase finished: %s onto %s" , options.head_name ? options.head_name :"detached HEAD" ,oid_to_hex (&options.onto->object.oid)); -reset_head (NULL ,"Fast-forwarded" , options.head_name, -RESET_HEAD_REFS_ONLY ,"HEAD" , msg.buf); +reset_head (the_repository,NULL ,"Fast-forwarded" , options.head_name, +RESET_HEAD_REFS_ONLY ,"HEAD" , msg.buf, +DEFAULT_REFLOG_ACTION );strbuf_release (&msg); ret = !!finish_rebase (&options); goto cleanup;
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 15/23 rebase: extract create_autostash()
In reply to Denton LiuIn a future commit, we will lib-ify this code. In preparation for this, extract the code into the create_autostash() function so that it can be cleaned up before it is finally lib-ified.
This patch is best viewed with --color-moved and --color-moved-ws=allow-indentation-change.
--- builtin/rebase.c | 94 +++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 44 deletions(-)
builtin/rebase.c +50 -44
@@ -1269,6 +1269,55 @@ static int check_exec_cmd(const char *cmd)return 0 ; } +static void create_autostash (struct rebase_options *options) +{ +struct strbuf buf =STRBUF_INIT ; +struct lock_file lock_file =LOCK_INIT ; +int fd; + + fd =hold_locked_index (&lock_file,0 ); +refresh_cache (REFRESH_QUIET ); +if (0 <= fd) +repo_update_index_if_able (the_repository, &lock_file); +rollback_lock_file (&lock_file); + +if (has_unstaged_changes (the_repository,1 ) || +has_uncommitted_changes (the_repository,1 )) { +const char *autostash = +state_dir_path ("autostash" , options); +struct child_process stash =CHILD_PROCESS_INIT ; +struct object_id oid; + +argv_array_pushl (&stash.args, +"stash" ,"create" ,"autostash" ,NULL ); + stash.git_cmd =1 ; + stash.no_stdin =1 ; +strbuf_reset (&buf); +if (capture_command (&stash, &buf,GIT_MAX_HEXSZ )) +die (_ ("Cannot autostash" )); +strbuf_trim_trailing_newline (&buf); +if (get_oid (buf.buf, &oid)) +die (_ ("Unexpected stash response: '%s'" ), + buf.buf); +strbuf_reset (&buf); +strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); + +if (safe_create_leading_directories_const (autostash)) +die (_ ("Could not create directory for '%s'" ), + options->state_dir); +write_file (autostash,"%s" ,oid_to_hex (&oid)); +printf (_ ("Created autostash: %s\n" ), buf.buf); +if (reset_head (the_repository,NULL ,"reset --hard" , +NULL ,RESET_HEAD_HARD ,NULL ,NULL , +DEFAULT_REFLOG_ACTION ) <0 ) +die (_ ("could not reset --hard" )); + +if (discard_index (the_repository->index) <0 || +repo_read_index (the_repository) <0 ) +die (_ ("could not read index" )); + } +strbuf_release (&buf); +}int cmd_rebase (int argc,const char **argv,const char *prefix) {
@@ -1902,50 +1951,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)die (_ ("could not read index" ));if (options.autostash) { -struct lock_file lock_file =LOCK_INIT ; -int fd; - - fd =hold_locked_index (&lock_file,0 ); -refresh_cache (REFRESH_QUIET ); -if (0 <= fd) -repo_update_index_if_able (the_repository, &lock_file); -rollback_lock_file (&lock_file); - -if (has_unstaged_changes (the_repository,1 ) || -has_uncommitted_changes (the_repository,1 )) { -const char *autostash = -state_dir_path ("autostash" , &options); -struct child_process stash =CHILD_PROCESS_INIT ; -struct object_id oid; - -argv_array_pushl (&stash.args, -"stash" ,"create" ,"autostash" ,NULL ); - stash.git_cmd =1 ; - stash.no_stdin =1 ; -strbuf_reset (&buf); -if (capture_command (&stash, &buf,GIT_MAX_HEXSZ )) -die (_ ("Cannot autostash" )); -strbuf_trim_trailing_newline (&buf); -if (get_oid (buf.buf, &oid)) -die (_ ("Unexpected stash response: '%s'" ), - buf.buf); -strbuf_reset (&buf); -strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); - -if (safe_create_leading_directories_const (autostash)) -die (_ ("Could not create directory for '%s'" ), - options.state_dir); -write_file (autostash,"%s" ,oid_to_hex (&oid)); -printf (_ ("Created autostash: %s\n" ), buf.buf); -if (reset_head (the_repository,NULL ,"reset --hard" , -NULL ,RESET_HEAD_HARD ,NULL ,NULL , -DEFAULT_REFLOG_ACTION ) <0 ) -die (_ ("could not reset --hard" )); - -if (discard_index (the_repository->index) <0 || -repo_read_index (the_repository) <0 ) -die (_ ("could not read index" )); - } +create_autostash (&options); } if (require_clean_work_tree(the_repository, "rebase",
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 16/23 rebase: generify create_autostash()
In reply to Denton LiuIn the future, we plan on lib-ifying create_autostash() so we need it to be more generic. Make it more generic by making it accept a struct repository argument instead of implicitly using the non-repo functions and the_repository. Also, make it accept a path argument so that we no longer rely have to rely on struct rebase_options. Finally, make it accept a default_reflog_action argument so we no longer have to rely on DEFAULT_REFLOG_ACTION.
--- builtin/rebase.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
builtin/rebase.c +16 -16
@@ -1269,22 +1269,21 @@ static int check_exec_cmd(const char *cmd)return 0 ; } -static void create_autostash (struct rebase_options *options) +static void create_autostash (struct repository *r,const char *path, +const char *default_reflog_action) {struct strbuf buf =STRBUF_INIT ;struct lock_file lock_file =LOCK_INIT ;int fd; - fd =hold_locked_index (&lock_file,0 ); -refresh_cache (REFRESH_QUIET ); + fd =repo_hold_locked_index (r, &lock_file,0 ); +refresh_index (r->index,REFRESH_QUIET ,NULL ,NULL ,NULL );if (0 <= fd) -repo_update_index_if_able (the_repository, &lock_file); +repo_update_index_if_able (r, &lock_file);rollback_lock_file (&lock_file); -if (has_unstaged_changes (the_repository,1 ) || -has_uncommitted_changes (the_repository,1 )) { -const char *autostash = -state_dir_path ("autostash" , options); +if (has_unstaged_changes (r,1 ) || +has_uncommitted_changes (r,1 )) {struct child_process stash =CHILD_PROCESS_INIT ;struct object_id oid;
@@ -1302,18 +1301,18 @@ static void create_autostash(struct rebase_options *options)strbuf_reset (&buf);strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); -if (safe_create_leading_directories_const (autostash)) +if (safe_create_leading_directories_const (path))die (_ ("Could not create directory for '%s'" ), - options->state_dir); -write_file (autostash,"%s" ,oid_to_hex (&oid)); + path); +write_file (path,"%s" ,oid_to_hex (&oid));printf (_ ("Created autostash: %s\n" ), buf.buf); -if (reset_head (the_repository,NULL ,"reset --hard" , +if (reset_head (r,NULL ,"reset --hard" ,NULL ,RESET_HEAD_HARD ,NULL ,NULL , -DEFAULT_REFLOG_ACTION ) <0 ) + default_reflog_action) <0 )die (_ ("could not reset --hard" )); -if (discard_index (the_repository->index) <0 || -repo_read_index (the_repository) <0 ) +if (discard_index (r->index) <0 || +repo_read_index (r) <0 )die (_ ("could not read index" )); }strbuf_release (&buf);
@@ -1951,7 +1950,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)die (_ ("could not read index" ));if (options.autostash) { -create_autostash (&options); +create_autostash (the_repository,state_dir_path ("autostash" , &options), +DEFAULT_REFLOG_ACTION ); } if (require_clean_work_tree(the_repository, "rebase",
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 17/23 sequencer: extract perform_autostash() from rebase
In reply to Denton LiuLib-ify the autostash code by extracting perform_autostash() from rebase into sequencer. In a future commit, this will be used to implement --autostash in other builtins.
This patch is best viewed with --color-moved.
--- builtin/rebase.c | 49 ----------------------------------------------- sequencer.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ sequencer.h | 2 ++ 3 files changed, 52 insertions(+), 49 deletions(-)
builtin/rebase.c +0 -49
@@ -1269,55 +1269,6 @@ static int check_exec_cmd(const char *cmd)return 0 ; } -static void create_autostash (struct repository *r,const char *path, -const char *default_reflog_action) -{ -struct strbuf buf =STRBUF_INIT ; -struct lock_file lock_file =LOCK_INIT ; -int fd; - - fd =repo_hold_locked_index (r, &lock_file,0 ); -refresh_index (r->index,REFRESH_QUIET ,NULL ,NULL ,NULL ); -if (0 <= fd) -repo_update_index_if_able (r, &lock_file); -rollback_lock_file (&lock_file); - -if (has_unstaged_changes (r,1 ) || -has_uncommitted_changes (r,1 )) { -struct child_process stash =CHILD_PROCESS_INIT ; -struct object_id oid; - -argv_array_pushl (&stash.args, -"stash" ,"create" ,"autostash" ,NULL ); - stash.git_cmd =1 ; - stash.no_stdin =1 ; -strbuf_reset (&buf); -if (capture_command (&stash, &buf,GIT_MAX_HEXSZ )) -die (_ ("Cannot autostash" )); -strbuf_trim_trailing_newline (&buf); -if (get_oid (buf.buf, &oid)) -die (_ ("Unexpected stash response: '%s'" ), - buf.buf); -strbuf_reset (&buf); -strbuf_add_unique_abbrev (&buf, &oid,DEFAULT_ABBREV ); - -if (safe_create_leading_directories_const (path)) -die (_ ("Could not create directory for '%s'" ), - path); -write_file (path,"%s" ,oid_to_hex (&oid)); -printf (_ ("Created autostash: %s\n" ), buf.buf); -if (reset_head (r,NULL ,"reset --hard" , -NULL ,RESET_HEAD_HARD ,NULL ,NULL , - default_reflog_action) <0 ) -die (_ ("could not reset --hard" )); - -if (discard_index (r->index) <0 || -repo_read_index (r) <0 ) -die (_ ("could not read index" )); - } -strbuf_release (&buf); -} -int cmd_rebase (int argc,const char **argv,const char *prefix) {struct rebase_options options =REBASE_OPTIONS_INIT ;
sequencer.c +50 -0
62 lines not on this page · raw message
sequencer.h +2 -0
@@ -191,6 +191,8 @@ void commit_post_rewrite(struct repository *r,const struct commit *current_head, const struct object_id *new_head); +void create_autostash (struct repository *r,const char *path, +const char *default_reflog_action);int apply_autostash (const char *path);#define SUMMARY_INITIAL_COMMIT (1 << 0)
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 18/23 sequencer: unlink autostash in apply_autostash()
In reply to Denton LiuExplicitly remove autostash file in apply_autostash() once it has been applied successfully.
This is currently a no-op because the only users of this function will unlink the state (including the autostash file) after this function runs. However, in the future, we will introduce a user of the function that does not explicitly remove the state so we do it here.
--- sequencer.c | 1 + 1 file changed, 1 insertion(+)
sequencer.c +1 -0
@@ -3740,6 +3740,7 @@ int apply_autostash(const char *path) " \"git stash drop\" at any time.\n")); } +unlink (path);strbuf_release (&stash_oid);return ret; }
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 20/23 sequencer: implement apply_autostash_oid()
In reply to Denton LiuSplit apply_save_autostash() into apply_autostash_oid() and apply_save_autostash() where the former operates on an OID string and the latter reads the OID from a file before passing it into apply_save_autostash_oid().
This function is required for a future commmit which will rely on being able to apply an autostash whose OID is stored as a string.
--- sequencer.c | 38 ++++++++++++++++++++++++++------------ sequencer.h | 1 + 2 files changed, 27 insertions(+), 12 deletions(-)
sequencer.c +26 -12
@@ -3699,26 +3699,18 @@ void create_autostash(struct repository *r, const char *path,strbuf_release (&buf); } -static int apply_save_autostash (const char *path,int attempt_apply) +static int apply_save_autostash_oid (const char *stash_oid,int attempt_apply) { -struct strbuf stash_oid =STRBUF_INIT ;struct child_process child =CHILD_PROCESS_INIT ;int ret =0 ; -if (!read_oneliner (&stash_oid, path, -READ_ONELINER_SKIP_IF_EMPTY )) { -strbuf_release (&stash_oid); -return 0 ; - } -strbuf_trim (&stash_oid); -if (attempt_apply) { child.git_cmd =1 ; child.no_stdout =1 ; child.no_stderr =1 ;argv_array_push (&child.args,"stash" );argv_array_push (&child.args,"apply" ); -argv_array_push (&child.args, stash_oid.buf); +argv_array_push (&child.args, stash_oid); ret =run_command (&child); }
@@ -3733,9 +3725,9 @@ static int apply_save_autostash(const char *path, int attempt_apply)argv_array_push (&store.args,"-m" );argv_array_push (&store.args,"autostash" );argv_array_push (&store.args,"-q" ); -argv_array_push (&store.args, stash_oid.buf); +argv_array_push (&store.args, stash_oid);if (run_command (&store)) - ret =error (_ ("cannot store %s" ), stash_oid.buf); + ret =error (_ ("cannot store %s" ), stash_oid);else fprintf(stderr, _("%s\n"
@@ -3747,6 +3739,23 @@ static int apply_save_autostash(const char *path, int attempt_apply) _("Autostash exists; creating a new stash entry.")); } +return ret; +} + +static int apply_save_autostash (const char *path,int attempt_apply) +{ +struct strbuf stash_oid =STRBUF_INIT ; +int ret =0 ; + +if (!read_oneliner (&stash_oid, path, +READ_ONELINER_SKIP_IF_EMPTY )) { +strbuf_release (&stash_oid); +return 0 ; + } +strbuf_trim (&stash_oid); + + ret =apply_save_autostash_oid (stash_oid.buf, attempt_apply); +unlink (path);strbuf_release (&stash_oid);return ret;
@@ -3762,6 +3771,11 @@ int apply_autostash(const char *path)return apply_save_autostash (path,1 ); } +int apply_autostash_oid (const char *stash_oid) +{ +return apply_save_autostash_oid (stash_oid,1 ); +} +static const char *reflog_message (struct replay_opts *opts,const char *sub_action,const char *fmt, ...) {
sequencer.h +1 -0
@@ -195,6 +195,7 @@ void create_autostash(struct repository *r, const char *path, const char *default_reflog_action);int save_autostash (const char *path);int apply_autostash (const char *path); +int apply_autostash_oid (const char *stash_oid);#define SUMMARY_INITIAL_COMMIT (1 << 0)#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 14/23 reset: extract reset_head() from rebase
In reply to Denton LiuContinue the process of lib-ifying the autostash code. In a future commit, this will be used to implement --autostash in other builtins.
This patch is best viewed with --color-moved.
--- Makefile | 1 + builtin/rebase.c | 139 +--------------------------------------------- reset.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++ reset.h | 20 +++++++ 4 files changed, 162 insertions(+), 138 deletions(-) create mode 100644 reset.c create mode 100644 reset.h
Makefile +1 -0
@@ -970,6 +970,7 @@ LIB_OBJS += replace-object.o LIB_OBJS += repo-settings.o LIB_OBJS += repository.o LIB_OBJS += rerere.o +LIB_OBJS += reset.o LIB_OBJS += resolve-undo.o LIB_OBJS += revision.o LIB_OBJS += run-command.o
builtin/rebase.c +1 -138
151 lines not on this page · raw message
reset.c new file +140 -0
140 lines not on this page · raw message
reset.h new file +20 -0
@@ -0,0 +1,20 @@ +#ifndef RESET_H +#define RESET_H + +#include "hash.h" +#include "repository.h" + +#define GIT_REFLOG_ACTION_ENVIRONMENT "GIT_REFLOG_ACTION" + +#define RESET_HEAD_DETACH (1<<0) +#define RESET_HEAD_HARD (1<<1) +#define RESET_HEAD_RUN_POST_CHECKOUT_HOOK (1<<2) +#define RESET_HEAD_REFS_ONLY (1<<3) +#define RESET_ORIG_HEAD (1<<4) + +int reset_head (struct repository *r,struct object_id *oid,const char *action, +const char *switch_to_branch,unsigned flags, +const char *reflog_orig_head,const char *reflog_head, +const char *default_reflog_action); + +#endif
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 22/23 t5520: make test_pull_autostash() accept expect_parent_num
In reply to Denton LiuBefore, test_pull_autostash() was hardcoded to run test_cmp_rev HEAD^ copy to test that a rebase happened. However, in a future patch, we plan on testing merging as well. Make test_pull_autostash() accept a parent number as an argument so that, in the future, we can test if a merge happened in addition to a rebase.
--- t/t5520-pull.sh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
t/t5520-pull.sh +8 -6
@@ -10,11 +10,13 @@ modify () { } test_pull_autostash () { + expect_parent_num="$1" && + shift && git reset --hard before-rebase && echo dirty >new_file && git add new_file && git pull "$@" . copy && - test_cmp_rev HEAD^ copy && + test_cmp_rev HEAD^"$expect_parent_num" copy && echo dirty >expect && test_cmp expect new_file && echo "modified again" >expect &&
@@ -369,22 +371,22 @@ test_expect_success '--rebase fails with multiple branches' ' test_expect_success 'pull --rebase succeeds with dirty working directory and rebase.autostash set' ' test_config rebase.autostash true && - test_pull_autostash --rebase + test_pull_autostash 1 --rebase ' test_expect_success 'pull --rebase --autostash & rebase.autostash=true' ' test_config rebase.autostash true && - test_pull_autostash --rebase --autostash + test_pull_autostash 1 --rebase --autostash ' test_expect_success 'pull --rebase --autostash & rebase.autostash=false' ' test_config rebase.autostash false && - test_pull_autostash --rebase --autostash + test_pull_autostash 1 --rebase --autostash ' test_expect_success 'pull --rebase --autostash & rebase.autostash unset' ' test_unconfig rebase.autostash && - test_pull_autostash --rebase --autostash + test_pull_autostash 1 --rebase --autostash ' test_expect_success 'pull --rebase --no-autostash & rebase.autostash=true' '
@@ -422,7 +424,7 @@ test_expect_success 'pull.rebase' ' test_expect_success 'pull --autostash & pull.rebase=true' ' test_config pull.rebase true && - test_pull_autostash --autostash + test_pull_autostash 1 --autostash ' test_expect_success 'pull --no-autostash & pull.rebase=true' '
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 19/23 sequencer: implement save_autostash()
In reply to Denton LiuExtract common functionality of apply_autostash() into apply_save_autostash() and use it to implement save_autostash(). This function will be used in a future commit.
The difference between save_autostash() and apply_autostash() is that the latter does not try to apply the stash. It skips that step and just stores the created entry in the stash reflog.
--- sequencer.c | 37 +++++++++++++++++++++++++++---------- sequencer.h | 1 + 2 files changed, 28 insertions(+), 10 deletions(-)
sequencer.c +27 -10
@@ -3699,7 +3699,7 @@ void create_autostash(struct repository *r, const char *path,strbuf_release (&buf); } -int apply_autostash (const char *path) +static int apply_save_autostash (const char *path,int attempt_apply) {struct strbuf stash_oid =STRBUF_INIT ;struct child_process child =CHILD_PROCESS_INIT ;
@@ -3712,13 +3712,17 @@ int apply_autostash(const char *path) }strbuf_trim (&stash_oid); - child.git_cmd =1 ; - child.no_stdout =1 ; - child.no_stderr =1 ; -argv_array_push (&child.args,"stash" ); -argv_array_push (&child.args,"apply" ); -argv_array_push (&child.args, stash_oid.buf); -if (!run_command (&child)) +if (attempt_apply) { + child.git_cmd =1 ; + child.no_stdout =1 ; + child.no_stderr =1 ; +argv_array_push (&child.args,"stash" ); +argv_array_push (&child.args,"apply" ); +argv_array_push (&child.args, stash_oid.buf); + ret =run_command (&child); + } + +if (attempt_apply && !ret)fprintf (stderr,_ ("Applied autostash.\n" ));else {struct child_process store =CHILD_PROCESS_INIT ;
@@ -3734,10 +3738,13 @@ int apply_autostash(const char *path) ret =error (_ ("cannot store %s" ), stash_oid.buf); else fprintf(stderr, - _("Applying autostash resulted in conflicts.\n" + _("%s\n""Your changes are safe in the stash.\n" "You can run \"git stash pop\" or" - " \"git stash drop\" at any time.\n")); + " \"git stash drop\" at any time.\n"), + attempt_apply ? +_ ("Applying autostash resulted in conflicts." ) : + _("Autostash exists; creating a new stash entry.")); }unlink (path);
@@ -3745,6 +3752,16 @@ int apply_autostash(const char *path)return ret; } +int save_autostash (const char *path) +{ +return apply_save_autostash (path,0 ); +} + +int apply_autostash (const char *path) +{ +return apply_save_autostash (path,1 ); +} +static const char *reflog_message (struct replay_opts *opts,const char *sub_action,const char *fmt, ...) {
sequencer.h +1 -0
@@ -193,6 +193,7 @@ void commit_post_rewrite(struct repository *r,void create_autostash (struct repository *r,const char *path,const char *default_reflog_action); +int save_autostash (const char *path);int apply_autostash (const char *path);#define SUMMARY_INITIAL_COMMIT (1 << 0)
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 23/23 pull: pass --autostash to merge
In reply to Denton LiuBefore, --autostash only worked with git pull --rebase. However, in the last patch, merge learned --autostash as well so there's no reason why we should have this restriction anymore. Teach pull to pass --autostash to merge, just like it did for rebase.
--- Documentation/git-pull.txt | 9 ------- Documentation/merge-options.txt | 4 +-- builtin/pull.c | 9 ++++--- t/t5520-pull.sh | 43 +++++++++++++++++++++++++++------ 4 files changed, 42 insertions(+), 23 deletions(-)
Documentation/git-pull.txt +0 -9
@@ -133,15 +133,6 @@ unless you have read linkgit:git-rebase[1] carefully. --no-rebase:: Override earlier --rebase. ---autostash:: ---no-autostash:: - Before starting rebase, stash local modifications away (see - linkgit:git-stash[1]) if needed, and apply the stash entry when - done. `--no-autostash` is useful to override the `rebase.autoStash` - configuration variable (see linkgit:git-config[1]). -+ -This option is only valid when "--rebase" is used. - Options related to fetching ~~~~~~~~~~~~~~~~~~~~~~~~~~~
Documentation/merge-options.txt +2 -2
@@ -155,6 +155,8 @@ ifndef::git-pull[] Note that not all merge strategies may support progress reporting. +endif::git-pull[] + --autostash:: --no-autostash:: Automatically create a temporary stash entry before the operation
@@ -163,8 +165,6 @@ ifndef::git-pull[] with care: the final stash application after a successful merge might result in non-trivial conflicts. -endif::git-pull[] - --allow-unrelated-histories:: By default, `git merge` command refuses to merge histories that do not share a common ancestor. This option can be
builtin/pull.c +5 -4
@@ -163,7 +163,7 @@ static struct option pull_options[] = { N_("verify that the named commit has a valid GPG signature"), PARSE_OPT_NOARG),OPT_BOOL (0 ,"autostash" , &opt_autostash, -N_ ("automatically stash/stash pop before and after rebase" )), +N_ ("automatically stash/stash pop before and after" )),OPT_PASSTHRU_ARGV ('s' ,"strategy" , &opt_strategies,N_ ("strategy" ),N_ ("merge strategy to use" ), 0),
@@ -661,6 +661,10 @@ static int run_merge(void)argv_array_pushv (&args, opt_strategy_opts.argv);if (opt_gpg_sign)argv_array_push (&args, opt_gpg_sign); +if (opt_autostash ==0 ) +argv_array_push (&args,"--no-autostash" ); +else if (opt_autostash ==1 ) +argv_array_push (&args,"--autostash" );if (opt_allow_unrelated_histories >0 )argv_array_push (&args,"--allow-unrelated-histories" );
@@ -908,9 +912,6 @@ int cmd_pull(int argc, const char **argv, const char *prefix)if (get_oid ("HEAD" , &orig_head))oidclr (&orig_head); -if (!opt_rebase && opt_autostash !=-1 ) -die (_ ("--[no-]autostash option is only valid with --rebase." )); - autostash = config_autostash;if (opt_rebase) {if (opt_autostash !=-1 )
t/t5520-pull.sh +35 -8
55 lines not on this page · raw message
Signature · 1 line
2.26.0.159.g23e2136ad0
PATCH v4 10/23 sequencer: make apply_rebase() accept a path
In reply to Denton LiuIn order to make apply_rebase() more generic for future extraction, make it accept a path argument so that the location from where to read the reference to the autostash commit can be customized. Remove the opts argument since it was unused before anyway.
--- sequencer.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
sequencer.c +7 -7
@@ -3649,13 +3649,13 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)return -1 ; } -static int apply_autostash (struct replay_opts *opts) +static int apply_autostash (const char *path) {struct strbuf stash_sha1 =STRBUF_INIT ;struct child_process child =CHILD_PROCESS_INIT ;int ret =0 ; -if (!read_oneliner (&stash_sha1,rebase_path_autostash (), +if (!read_oneliner (&stash_sha1, path,READ_ONELINER_SKIP_IF_EMPTY )) {strbuf_release (&stash_sha1);return 0 ;
@@ -3748,7 +3748,7 @@ static int checkout_onto(struct repository *r, struct replay_opts *opts,return error (_ ("%s: not a valid OID" ), orig_head);if (run_git_checkout (r, opts,oid_to_hex (onto), action)) { -apply_autostash (opts); +apply_autostash (rebase_path_autostash ());sequencer_remove_state (opts);return error (_ ("could not detach HEAD" )); }
@@ -4061,7 +4061,7 @@ static int pick_commits(struct repository *r,run_command (&hook); } } -apply_autostash (opts); +apply_autostash (rebase_path_autostash ());if (!opts->quiet) {if (!opts->verbose)
@@ -5070,7 +5070,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned flatodo_list_add_exec_commands (todo_list, commands);if (count_commands (todo_list) ==0 ) { -apply_autostash (opts); +apply_autostash (rebase_path_autostash ());sequencer_remove_state (opts);return error (_ ("nothing to do" ));
@@ -5081,12 +5081,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned flaif (res ==-1 )return -1 ;else if (res ==-2 ) { -apply_autostash (opts); +apply_autostash (rebase_path_autostash ());sequencer_remove_state (opts);return -1 ; }else if (res ==-3 ) { -apply_autostash (opts); +apply_autostash (rebase_path_autostash ());sequencer_remove_state (opts);todo_list_release (&new_todo);
Signature · 1 line
2.26.0.159.g23e2136ad0