mirror of
https://github.com/git/git.git
synced 2024-11-23 18:05:29 +08:00
Merge branch 'dl/merge-autostash'
"git merge" learns the "--autostash" option. * dl/merge-autostash: (22 commits) pull: pass --autostash to merge t5520: make test_pull_autostash() accept expect_parent_num merge: teach --autostash option sequencer: implement apply_autostash_oid() sequencer: implement save_autostash() sequencer: unlink autostash in apply_autostash() sequencer: extract perform_autostash() from rebase rebase: generify create_autostash() rebase: extract create_autostash() reset: extract reset_head() from rebase rebase: generify reset_head() rebase: use apply_autostash() from sequencer.c sequencer: rename stash_sha1 to stash_oid sequencer: make apply_autostash() accept a path rebase: use read_oneliner() sequencer: make read_oneliner() extern sequencer: configurably warn on non-existent files sequencer: make read_oneliner() accept flags sequencer: make file exists check more efficient sequencer: stop leaking buf ...
This commit is contained in:
commit
bf10200871
@ -70,6 +70,16 @@ merge.stat::
|
||||
Whether to print the diffstat between ORIG_HEAD and the merge result
|
||||
at the end of the merge. True by default.
|
||||
|
||||
merge.autoStash::
|
||||
When set to true, automatically create a temporary stash entry
|
||||
before the operation begins, and apply it after the operation
|
||||
ends. This means that you can run merge on a dirty worktree.
|
||||
However, use with care: the final stash application after a
|
||||
successful merge might result in non-trivial conflicts.
|
||||
This option can be overridden by the `--no-autostash` and
|
||||
`--autostash` options of linkgit:git-merge[1].
|
||||
Defaults to false.
|
||||
|
||||
merge.tool::
|
||||
Controls which merge tool is used by linkgit:git-mergetool[1].
|
||||
The list below shows the valid built-in values.
|
||||
|
@ -94,7 +94,8 @@ will be appended to the specified message.
|
||||
|
||||
--abort::
|
||||
Abort the current conflict resolution process, and
|
||||
try to reconstruct the pre-merge state.
|
||||
try to reconstruct the pre-merge state. If an autostash entry is
|
||||
present, apply it to the worktree.
|
||||
+
|
||||
If there were uncommitted worktree changes present when the merge
|
||||
started, 'git merge --abort' will in some cases be unable to
|
||||
@ -102,11 +103,15 @@ 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 in
|
||||
which case 'git merge --abort' applies the stash entry to the worktree
|
||||
whereas 'git reset --merge' will save the stashed changes in the stash
|
||||
reflog.
|
||||
|
||||
--quit::
|
||||
Forget about the current merge in progress. Leave the index
|
||||
and the working tree as-is.
|
||||
and the working tree as-is. If `MERGE_AUTOSTASH` is present, the
|
||||
stash entry will be saved to the stash reflog.
|
||||
|
||||
--continue::
|
||||
After a 'git merge' stops due to conflicts you can conclude the
|
||||
|
@ -134,15 +134,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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -160,6 +160,14 @@ ifndef::git-pull[]
|
||||
|
||||
endif::git-pull[]
|
||||
|
||||
--autostash::
|
||||
--no-autostash::
|
||||
Automatically create a temporary stash entry before the operation
|
||||
begins, and apply it after the operation ends. This means
|
||||
that you can run the operation on a dirty worktree. However, use
|
||||
with care: the final stash application after a successful
|
||||
merge might result in non-trivial conflicts.
|
||||
|
||||
--allow-unrelated-histories::
|
||||
By default, `git merge` command refuses to merge histories
|
||||
that do not share a common ancestor. This option can be
|
||||
|
78
Makefile
78
Makefile
@ -610,8 +610,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
|
||||
@ -679,9 +679,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 =
|
||||
@ -703,15 +703,16 @@ 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
|
||||
TEST_BUILTINS_OBJS += test-match-trees.o
|
||||
TEST_BUILTINS_OBJS += test-mergesort.o
|
||||
TEST_BUILTINS_OBJS += test-mktemp.o
|
||||
TEST_BUILTINS_OBJS += test-oid-array.o
|
||||
TEST_BUILTINS_OBJS += test-oidmap.o
|
||||
TEST_BUILTINS_OBJS += test-online-cpus.o
|
||||
TEST_BUILTINS_OBJS += test-parse-options.o
|
||||
@ -732,7 +733,6 @@ 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-oid-array.o
|
||||
TEST_BUILTINS_OBJS += test-sha256.o
|
||||
TEST_BUILTINS_OBJS += test-sigchain.o
|
||||
TEST_BUILTINS_OBJS += test-strcmp-offset.o
|
||||
@ -742,10 +742,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.
|
||||
@ -782,10 +782,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
|
||||
|
||||
@ -825,9 +825,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
|
||||
@ -843,9 +843,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
|
||||
@ -859,17 +859,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
|
||||
@ -888,7 +888,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
|
||||
@ -898,9 +897,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
|
||||
@ -909,32 +909,32 @@ 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 += oid-array.o
|
||||
LIB_OBJS += oidmap.o
|
||||
LIB_OBJS += oidset.o
|
||||
LIB_OBJS += oid-array.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,8 +952,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
|
||||
@ -961,12 +962,12 @@ 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
|
||||
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
|
||||
@ -975,8 +976,8 @@ LIB_OBJS += sequencer.o
|
||||
LIB_OBJS += serve.o
|
||||
LIB_OBJS += server-info.o
|
||||
LIB_OBJS += setup.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
|
||||
@ -986,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
|
||||
@ -1007,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
|
||||
@ -1051,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
|
||||
@ -1084,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
|
||||
@ -1110,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
|
||||
@ -2331,16 +2332,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
|
||||
|
||||
@ -3148,9 +3149,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::
|
||||
|
1
branch.c
1
branch.c
@ -344,6 +344,7 @@ void remove_merge_branch_state(struct repository *r)
|
||||
unlink(git_path_merge_rr(r));
|
||||
unlink(git_path_merge_msg(r));
|
||||
unlink(git_path_merge_mode(r));
|
||||
save_autostash(git_path_merge_autostash(r));
|
||||
}
|
||||
|
||||
void remove_branch_state(struct repository *r, int verbose)
|
||||
|
@ -1721,6 +1721,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
|
||||
&oid, flags);
|
||||
}
|
||||
|
||||
apply_autostash(git_path_merge_autostash(the_repository));
|
||||
|
||||
UNLEAK(err);
|
||||
UNLEAK(sb);
|
||||
return 0;
|
||||
|
@ -82,6 +82,7 @@ static int show_progress = -1;
|
||||
static int default_to_upstream = 1;
|
||||
static int signoff;
|
||||
static const char *sign_commit;
|
||||
static int autostash;
|
||||
static int no_verify;
|
||||
|
||||
static struct strategy all_strategy[] = {
|
||||
@ -286,6 +287,7 @@ static struct option builtin_merge_options[] = {
|
||||
OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
|
||||
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
|
||||
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
|
||||
OPT_AUTOSTASH(&autostash),
|
||||
OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
|
||||
OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
|
||||
OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
|
||||
@ -475,6 +477,7 @@ static void finish(struct commit *head_commit,
|
||||
/* Run a post-merge hook */
|
||||
run_hook_le(NULL, "post-merge", squash ? "1" : "0", NULL);
|
||||
|
||||
apply_autostash(git_path_merge_autostash(the_repository));
|
||||
strbuf_release(&reflog_message);
|
||||
}
|
||||
|
||||
@ -636,6 +639,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);
|
||||
@ -1283,6 +1289,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
||||
if (abort_current_merge) {
|
||||
int nargc = 2;
|
||||
const char *nargv[] = {"reset", "--merge", NULL};
|
||||
struct strbuf stash_oid = STRBUF_INIT;
|
||||
|
||||
if (orig_argc != 2)
|
||||
usage_msg_opt(_("--abort expects no arguments"),
|
||||
@ -1291,8 +1298,17 @@ 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))
|
||||
unlink(git_path_merge_autostash(the_repository));
|
||||
|
||||
/* Invoke 'git reset --merge' */
|
||||
ret = cmd_reset(nargc, nargv, prefix);
|
||||
|
||||
if (stash_oid.len)
|
||||
apply_autostash_oid(stash_oid.buf);
|
||||
|
||||
strbuf_release(&stash_oid);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -1515,6 +1531,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (autostash)
|
||||
create_autostash(the_repository,
|
||||
git_path_merge_autostash(the_repository),
|
||||
"merge");
|
||||
if (checkout_fast_forward(the_repository,
|
||||
&head_commit->object.oid,
|
||||
&commit->object.oid,
|
||||
@ -1581,6 +1601,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
||||
if (fast_forward == FF_ONLY)
|
||||
die(_("Not possible to fast-forward, aborting."));
|
||||
|
||||
if (autostash)
|
||||
create_autostash(the_repository,
|
||||
git_path_merge_autostash(the_repository),
|
||||
"merge");
|
||||
|
||||
/* We are going to make a new commit. */
|
||||
git_committer_info(IDENT_STRICT);
|
||||
|
||||
|
@ -164,7 +164,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),
|
||||
@ -695,6 +695,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");
|
||||
|
||||
@ -942,9 +946,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)
|
||||
|
308
builtin/rebase.c
308
builtin/rebase.c
@ -27,6 +27,9 @@
|
||||
#include "branch.h"
|
||||
#include "sequencer.h"
|
||||
#include "rebase-interactive.h"
|
||||
#include "reset.h"
|
||||
|
||||
#define DEFAULT_REFLOG_ACTION "rebase"
|
||||
|
||||
static char const * const builtin_rebase_usage[] = {
|
||||
N_("git rebase [-i] [options] [--exec <cmd>] "
|
||||
@ -590,15 +593,6 @@ static const char *state_dir_path(const char *filename, struct rebase_options *o
|
||||
return path.buf;
|
||||
}
|
||||
|
||||
/* Read one file, then strip line endings */
|
||||
static int read_one(const char *path, struct strbuf *buf)
|
||||
{
|
||||
if (strbuf_read_file(buf, path, 0) < 0)
|
||||
return error_errno(_("could not read '%s'"), path);
|
||||
strbuf_trim_trailing_newline(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize the rebase options from the state directory. */
|
||||
static int read_basic_state(struct rebase_options *opts)
|
||||
{
|
||||
@ -606,8 +600,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;
|
||||
@ -623,9 +619,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_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);
|
||||
@ -645,8 +643,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;
|
||||
@ -659,8 +657,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_MISSING))
|
||||
return -1;
|
||||
free(opts->gpg_sign_opt);
|
||||
opts->gpg_sign_opt = xstrdup(buf.buf);
|
||||
@ -668,7 +666,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_MISSING))
|
||||
return -1;
|
||||
free(opts->strategy);
|
||||
opts->strategy = xstrdup(buf.buf);
|
||||
@ -676,7 +675,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_MISSING))
|
||||
return -1;
|
||||
free(opts->strategy_opts);
|
||||
opts->strategy_opts = xstrdup(buf.buf);
|
||||
@ -719,51 +719,6 @@ static int rebase_write_basic_state(struct rebase_options *opts)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_autostash(struct rebase_options *opts)
|
||||
{
|
||||
const char *path = state_dir_path("autostash", opts);
|
||||
struct strbuf autostash = STRBUF_INIT;
|
||||
struct child_process stash_apply = CHILD_PROCESS_INIT;
|
||||
|
||||
if (!file_exists(path))
|
||||
return 0;
|
||||
|
||||
if (read_one(path, &autostash))
|
||||
return error(_("Could not read '%s'"), path);
|
||||
/* Ensure that the hash is not mistaken for a number */
|
||||
strbuf_addstr(&autostash, "^0");
|
||||
argv_array_pushl(&stash_apply.args,
|
||||
"stash", "apply", autostash.buf, NULL);
|
||||
stash_apply.git_cmd = 1;
|
||||
stash_apply.no_stderr = stash_apply.no_stdout =
|
||||
stash_apply.no_stdin = 1;
|
||||
if (!run_command(&stash_apply))
|
||||
printf(_("Applied autostash.\n"));
|
||||
else {
|
||||
struct argv_array args = ARGV_ARRAY_INIT;
|
||||
int res = 0;
|
||||
|
||||
argv_array_pushl(&args,
|
||||
"stash", "store", "-m", "autostash", "-q",
|
||||
autostash.buf, NULL);
|
||||
if (run_command_v_opt(args.argv, RUN_GIT_CMD))
|
||||
res = error(_("Cannot store %s"), autostash.buf);
|
||||
argv_array_clear(&args);
|
||||
strbuf_release(&autostash);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
fprintf(stderr,
|
||||
_("Applying autostash resulted in conflicts.\n"
|
||||
"Your changes are safe in the stash.\n"
|
||||
"You can run \"git stash pop\" or \"git stash drop\" "
|
||||
"at any time.\n"));
|
||||
}
|
||||
|
||||
strbuf_release(&autostash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int finish_rebase(struct rebase_options *opts)
|
||||
{
|
||||
struct strbuf dir = STRBUF_INIT;
|
||||
@ -771,7 +726,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
|
||||
@ -816,144 +771,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;
|
||||
init_checkout_metadata(&unpack_tree_opts.meta, switch_to_branch, oid, NULL);
|
||||
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;
|
||||
@ -969,8 +786,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);
|
||||
@ -1058,8 +877,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"
|
||||
@ -1218,7 +1038,7 @@ finished_rebase:
|
||||
} 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);
|
||||
@ -1459,7 +1279,6 @@ static int check_exec_cmd(const char *cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int cmd_rebase(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct rebase_options options = REBASE_OPTIONS_INIT;
|
||||
@ -1562,8 +1381,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
|
||||
{ OPTION_STRING, 'S', "gpg-sign", &gpg_sign, N_("key-id"),
|
||||
N_("GPG-sign commits"),
|
||||
PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
|
||||
OPT_BOOL(0, "autostash", &options.autostash,
|
||||
N_("automatically stash/stash pop before and after")),
|
||||
OPT_AUTOSTASH(&options.autostash),
|
||||
OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
|
||||
N_("add exec lines after each commit of the "
|
||||
"editable list")),
|
||||
@ -1720,8 +1538,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))
|
||||
@ -1738,9 +1556,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);
|
||||
@ -2098,49 +1916,8 @@ 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"));
|
||||
}
|
||||
create_autostash(the_repository, state_dir_path("autostash", &options),
|
||||
DEFAULT_REFLOG_ACTION);
|
||||
}
|
||||
|
||||
if (require_clean_work_tree(the_repository, "rebase",
|
||||
@ -2174,10 +1951,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);
|
||||
@ -2249,10 +2028,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);
|
||||
|
||||
@ -2267,8 +2046,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;
|
||||
|
@ -336,5 +336,6 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);
|
||||
#define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
|
||||
#define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file"))
|
||||
#define OPT_PATHSPEC_FILE_NUL(v) OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character"))
|
||||
#define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after"))
|
||||
|
||||
#endif
|
||||
|
1
path.c
1
path.c
@ -1535,5 +1535,6 @@ REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
|
||||
REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
|
||||
REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE")
|
||||
REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
|
||||
REPO_GIT_PATH_FUNC(merge_autostash, "MERGE_AUTOSTASH")
|
||||
REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")
|
||||
REPO_GIT_PATH_FUNC(shallow, "shallow")
|
||||
|
4
path.h
4
path.h
@ -177,11 +177,12 @@ struct path_cache {
|
||||
const char *merge_rr;
|
||||
const char *merge_mode;
|
||||
const char *merge_head;
|
||||
const char *merge_autostash;
|
||||
const char *fetch_head;
|
||||
const char *shallow;
|
||||
};
|
||||
|
||||
#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
|
||||
#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
|
||||
|
||||
const char *git_path_cherry_pick_head(struct repository *r);
|
||||
const char *git_path_revert_head(struct repository *r);
|
||||
@ -190,6 +191,7 @@ const char *git_path_merge_msg(struct repository *r);
|
||||
const char *git_path_merge_rr(struct repository *r);
|
||||
const char *git_path_merge_mode(struct repository *r);
|
||||
const char *git_path_merge_head(struct repository *r);
|
||||
const char *git_path_merge_autostash(struct repository *r);
|
||||
const char *git_path_fetch_head(struct repository *r);
|
||||
const char *git_path_shallow(struct repository *r);
|
||||
|
||||
|
141
reset.c
Normal file
141
reset.c
Normal file
@ -0,0 +1,141 @@
|
||||
#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;
|
||||
init_checkout_metadata(&unpack_tree_opts.meta, switch_to_branch, oid, NULL);
|
||||
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;
|
||||
|
||||
}
|
20
reset.h
Normal file
20
reset.h
Normal file
@ -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
|
176
sequencer.c
176
sequencer.c
@ -32,6 +32,7 @@
|
||||
#include "alias.h"
|
||||
#include "commit-reach.h"
|
||||
#include "rebase-interactive.h"
|
||||
#include "reset.h"
|
||||
|
||||
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
|
||||
|
||||
@ -419,25 +420,15 @@ 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 read_oneliner(struct strbuf *buf,
|
||||
const char *path, unsigned flags)
|
||||
{
|
||||
int orig_len = buf->len;
|
||||
|
||||
if (!file_exists(path))
|
||||
return 0;
|
||||
|
||||
if (strbuf_read_file(buf, path, 0) < 0) {
|
||||
warning_errno(_("could not read '%s'"), path);
|
||||
if ((flags & READ_ONELINER_WARN_MISSING) ||
|
||||
(errno != ENOENT && errno != ENOTDIR))
|
||||
warning_errno(_("could not read '%s'"), path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -447,7 +438,7 @@ static int read_oneliner(struct strbuf *buf,
|
||||
buf->buf[buf->len] = '\0';
|
||||
}
|
||||
|
||||
if (skip_if_empty && buf->len == orig_len)
|
||||
if ((flags & READ_ONELINER_SKIP_IF_EMPTY) && buf->len == orig_len)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
@ -2504,8 +2495,10 @@ 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(),
|
||||
READ_ONELINER_SKIP_IF_EMPTY)) {
|
||||
if (!starts_with(buf.buf, "-S"))
|
||||
strbuf_reset(&buf);
|
||||
else {
|
||||
@ -2515,7 +2508,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"))
|
||||
@ -2544,10 +2538,11 @@ 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)) {
|
||||
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'))) {
|
||||
@ -2557,12 +2552,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()))
|
||||
@ -3677,25 +3676,71 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int apply_autostash(struct replay_opts *opts)
|
||||
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);
|
||||
}
|
||||
|
||||
static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
|
||||
{
|
||||
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)) {
|
||||
strbuf_release(&stash_sha1);
|
||||
return 0;
|
||||
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);
|
||||
ret = run_command(&child);
|
||||
}
|
||||
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 && !ret)
|
||||
fprintf(stderr, _("Applied autostash.\n"));
|
||||
else {
|
||||
struct child_process store = CHILD_PROCESS_INIT;
|
||||
@ -3706,21 +3751,57 @@ static int apply_autostash(struct replay_opts *opts)
|
||||
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);
|
||||
if (run_command(&store))
|
||||
ret = error(_("cannot store %s"), stash_sha1.buf);
|
||||
ret = error(_("cannot store %s"), stash_oid);
|
||||
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."));
|
||||
}
|
||||
|
||||
strbuf_release(&stash_sha1);
|
||||
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;
|
||||
}
|
||||
|
||||
int save_autostash(const char *path)
|
||||
{
|
||||
return apply_save_autostash(path, 0);
|
||||
}
|
||||
|
||||
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, ...)
|
||||
{
|
||||
@ -3776,7 +3857,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"));
|
||||
}
|
||||
@ -4095,7 +4176,7 @@ cleanup_head_ref:
|
||||
run_command(&hook);
|
||||
}
|
||||
}
|
||||
apply_autostash(opts);
|
||||
apply_autostash(rebase_path_autostash());
|
||||
|
||||
if (!opts->quiet) {
|
||||
if (!opts->verbose)
|
||||
@ -4313,7 +4394,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);
|
||||
@ -5118,7 +5200,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
|
||||
todo_list_add_exec_commands(todo_list, commands);
|
||||
|
||||
if (count_commands(todo_list) == 0) {
|
||||
apply_autostash(opts);
|
||||
apply_autostash(rebase_path_autostash());
|
||||
sequencer_remove_state(opts);
|
||||
|
||||
return error(_("nothing to do"));
|
||||
@ -5129,12 +5211,12 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla
|
||||
if (res == -1)
|
||||
return -1;
|
||||
else if (res == -2) {
|
||||
apply_autostash(opts);
|
||||
apply_autostash(rebase_path_autostash());
|
||||
sequencer_remove_state(opts);
|
||||
|
||||
return -1;
|
||||
} else if (res == -3) {
|
||||
apply_autostash(opts);
|
||||
apply_autostash(rebase_path_autostash());
|
||||
sequencer_remove_state(opts);
|
||||
todo_list_release(&new_todo);
|
||||
|
||||
|
20
sequencer.h
20
sequencer.h
@ -191,6 +191,12 @@ 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 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)
|
||||
void print_commit_summary(struct repository *repo,
|
||||
@ -198,6 +204,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)
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
@ -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
|
||||
|
@ -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 &&
|
||||
@ -26,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 '
|
||||
@ -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' '
|
||||
@ -402,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 &&
|
||||
@ -422,7 +451,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' '
|
||||
|
154
t/t7600-merge.sh
154
t/t7600-merge.sh
@ -29,15 +29,19 @@ 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
|
||||
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 () {
|
||||
echo "Merge tag 'c2'" >msg.1-5 &&
|
||||
@ -81,7 +85,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 +99,7 @@ verify_parents () {
|
||||
}
|
||||
|
||||
verify_mergeheads () {
|
||||
printf '%s\n' "$@" >mergehead.expected &&
|
||||
test_write_lines "$@" >mergehead.expected &&
|
||||
while read sha1 rest
|
||||
do
|
||||
git rev-parse $sha1
|
||||
@ -675,6 +679,134 @@ test_expect_success 'refresh the index before merging' '
|
||||
git merge c3
|
||||
'
|
||||
|
||||
test_expect_success 'merge with --autostash' '
|
||||
git reset --hard c1 &&
|
||||
git merge-file file file.orig file.9 &&
|
||||
git merge --autostash c2 2>err &&
|
||||
test_i18ngrep "Applied autostash." err &&
|
||||
git show HEAD:file >merge-result &&
|
||||
test_cmp result.1-5 merge-result &&
|
||||
test_cmp result.1-5-9 file
|
||||
'
|
||||
|
||||
test_expect_success 'merge with merge.autoStash' '
|
||||
test_config merge.autoStash true &&
|
||||
git reset --hard c1 &&
|
||||
git merge-file file file.orig file.9 &&
|
||||
git merge c2 2>err &&
|
||||
test_i18ngrep "Applied autostash." err &&
|
||||
git show HEAD:file >merge-result &&
|
||||
test_cmp result.1-5 merge-result &&
|
||||
test_cmp result.1-5-9 file
|
||||
'
|
||||
|
||||
test_expect_success 'fast-forward merge with --autostash' '
|
||||
git reset --hard c0 &&
|
||||
git merge-file file file.orig file.5 &&
|
||||
git merge --autostash c1 2>err &&
|
||||
test_i18ngrep "Applied autostash." err &&
|
||||
test_cmp result.1-5 file
|
||||
'
|
||||
|
||||
test_expect_success 'octopus merge with --autostash' '
|
||||
git reset --hard c1 &&
|
||||
git merge-file file file.orig file.3 &&
|
||||
git merge --autostash c2 c3 2>err &&
|
||||
test_i18ngrep "Applied autostash." err &&
|
||||
git show HEAD:file >merge-result &&
|
||||
test_cmp result.1-5-9 merge-result &&
|
||||
test_cmp result.1-3-5-9 file
|
||||
'
|
||||
|
||||
test_expect_success 'conflicted merge with --autostash, --abort restores stash' '
|
||||
git reset --hard c3 &&
|
||||
cp file.1 file &&
|
||||
test_must_fail git merge --autostash c7 &&
|
||||
git merge --abort 2>err &&
|
||||
test_i18ngrep "Applied autostash." err &&
|
||||
test_cmp file.1 file
|
||||
'
|
||||
|
||||
test_expect_success 'completed merge (git commit) with --no-commit and --autostash' '
|
||||
git reset --hard c1 &&
|
||||
git merge-file file file.orig file.9 &&
|
||||
git diff >expect &&
|
||||
git merge --no-commit --autostash c2 &&
|
||||
git stash show -p MERGE_AUTOSTASH >actual &&
|
||||
test_cmp expect actual &&
|
||||
git commit 2>err &&
|
||||
test_i18ngrep "Applied autostash." err &&
|
||||
git show HEAD:file >merge-result &&
|
||||
test_cmp result.1-5 merge-result &&
|
||||
test_cmp result.1-5-9 file
|
||||
'
|
||||
|
||||
test_expect_success 'completed merge (git merge --continue) with --no-commit and --autostash' '
|
||||
git reset --hard c1 &&
|
||||
git merge-file file file.orig file.9 &&
|
||||
git diff >expect &&
|
||||
git merge --no-commit --autostash c2 &&
|
||||
git stash show -p MERGE_AUTOSTASH >actual &&
|
||||
test_cmp expect actual &&
|
||||
git merge --continue 2>err &&
|
||||
test_i18ngrep "Applied autostash." err &&
|
||||
git show HEAD:file >merge-result &&
|
||||
test_cmp result.1-5 merge-result &&
|
||||
test_cmp result.1-5-9 file
|
||||
'
|
||||
|
||||
test_expect_success 'aborted merge (merge --abort) with --no-commit and --autostash' '
|
||||
git reset --hard c1 &&
|
||||
git merge-file file file.orig file.9 &&
|
||||
git diff >expect &&
|
||||
git merge --no-commit --autostash c2 &&
|
||||
git stash show -p MERGE_AUTOSTASH >actual &&
|
||||
test_cmp expect actual &&
|
||||
git merge --abort 2>err &&
|
||||
test_i18ngrep "Applied autostash." err &&
|
||||
git diff >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'aborted merge (reset --hard) with --no-commit and --autostash' '
|
||||
git reset --hard c1 &&
|
||||
git merge-file file file.orig file.9 &&
|
||||
git diff >expect &&
|
||||
git merge --no-commit --autostash c2 &&
|
||||
git stash show -p MERGE_AUTOSTASH >actual &&
|
||||
test_cmp expect actual &&
|
||||
git reset --hard 2>err &&
|
||||
test_i18ngrep "Autostash exists; creating a new stash entry." err &&
|
||||
git diff --exit-code
|
||||
'
|
||||
|
||||
test_expect_success 'quit merge with --no-commit and --autostash' '
|
||||
git reset --hard c1 &&
|
||||
git merge-file file file.orig file.9 &&
|
||||
git diff >expect &&
|
||||
git merge --no-commit --autostash c2 &&
|
||||
git stash show -p MERGE_AUTOSTASH >actual &&
|
||||
test_cmp expect actual &&
|
||||
git diff HEAD >expect &&
|
||||
git merge --quit 2>err &&
|
||||
test_i18ngrep "Autostash exists; creating a new stash entry." err &&
|
||||
git diff HEAD >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'merge 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
|
||||
|
Loading…
Reference in New Issue
Block a user