Harald Nordgren (4):
stash: add --ours-label, --theirs-label, --base-label for apply
sequencer: allow create_autostash to run silently
sequencer: teach autostash apply to take optional conflict marker
labels
checkout: -m (--merge) uses autostash when switching branches Documentation/git-checkout.adoc | 58 +++++-----
Documentation/git-stash.adoc | 11 +-
Documentation/git-switch.adoc | 33 +++---
builtin/checkout.c | 138 ++++++++++------------
builtin/stash.c | 32 ++++--
sequencer.c | 67 ++++++++---
sequencer.h | 4 +
t/t3420-rebase-autostash.sh | 24 +++-
t/t3903-stash.sh | 37 ++++++
t/t7201-co.sh | 195 ++++++++++++++++++++++++++++++++
t/t7600-merge.sh | 2 +-
xdiff-interface.c | 12 ++
xdiff-interface.h | 1 +
xdiff/xmerge.c | 6 +-
14 files changed, 463 insertions(+), 157 deletions(-)
base-commit: b15384c06f77bc2d34d0d3623a8a58218313a561
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2234%2FHaraldNordgren%2Fcheckout_autostash-v8
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2234/HaraldNordgren/checkout_autostash-v8
Pull-Request: https://github.com/git/git/pull/2234
Range-diff vs v7:
1: 284075600a ! 1: 8fcf377820 stash: add --ours-label, --theirs-label, --base-label for apply
@@ t/t3903-stash.sh: test_expect_success 'restore untracked files even when we hit
+ echo upstream >file &&
+ git add file &&
+ git commit -m upstream &&
-+ test_must_fail git stash apply --ours-label=UP --theirs-label=STASH &&
-+ grep "^<<<<<<< UP" file &&
-+ grep "^>>>>>>> STASH" file
++ test_must_fail git -c merge.conflictStyle=diff3 stash apply --ours-label=UP --theirs-label=STASH &&
++ test_grep "^<<<<<<< UP" file &&
++ test_grep "^||||||| Stash base" file &&
++ test_grep "^>>>>>>> STASH" file
++ )
++'
++
++test_expect_success 'apply with empty conflict labels' '
++ git init empty_labels &&
++ (
++ cd empty_labels &&
++ echo base >file &&
++ git add file &&
++ git commit -m base &&
++ echo stashed >file &&
++ git stash push -m "stashed" &&
++ echo upstream >file &&
++ git add file &&
++ git commit -m upstream &&
++ test_must_fail git stash apply --ours-label= --theirs-label= &&
++ test_grep "^<<<<<<<$" file &&
++ test_grep "^>>>>>>>$" file
+ )
+'
+
test_expect_success 'stash create reports a locked index' '
test_when_finished "rm -rf repo" &&
git init repo &&
+
+ ## xdiff/xmerge.c ##
+@@ xdiff/xmerge.c: static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
+ int size, int i, int style,
+ xdmerge_t *m, char *dest, int marker_size)
+ {
+- int marker1_size = (name1 ? strlen(name1) + 1 : 0);
+- int marker2_size = (name2 ? strlen(name2) + 1 : 0);
+- int marker3_size = (name3 ? strlen(name3) + 1 : 0);
++ int marker1_size = (name1 && *name1 ? strlen(name1) + 1 : 0);
++ int marker2_size = (name2 && *name2 ? strlen(name2) + 1 : 0);
++ int marker3_size = (name3 && *name3 ? strlen(name3) + 1 : 0);
+ int needs_cr = is_cr_needed(xe1, xe2, m);
+
+ if (marker_size <= 0)
2: 64261e3cb6 = 2: 86cf68d024 sequencer: allow create_autostash to run silently
3: c0d6b4b4c0 ! 3: 78300e0e9a sequencer: teach autostash apply to take optional conflict marker labels
@@ sequencer.c: void create_autostash_ref_silent(struct repository *r, const char *
-static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
+static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply,
-+ const char *label1, const char *label2,
-+ const char *label_ancestor)
++ const char *label_ours, const char *label_theirs,
++ const char *label_base)
{
struct child_process child = CHILD_PROCESS_INIT;
int ret = 0;
@@ sequencer.c: static int apply_save_autostash_oid(const char *stash_oid, int atte
child.no_stderr = 1;
strvec_push(&child.args, "stash");
strvec_push(&child.args, "apply");
-+ if (label1)
-+ strvec_pushf(&child.args, "--ours-label=%s", label1);
-+ if (label2)
-+ strvec_pushf(&child.args, "--theirs-label=%s", label2);
-+ if (label_ancestor)
-+ strvec_pushf(&child.args, "--base-label=%s", label_ancestor);
++ if (label_ours)
++ strvec_pushf(&child.args, "--ours-label=%s", label_ours);
++ if (label_theirs)
++ strvec_pushf(&child.args, "--theirs-label=%s", label_theirs);
++ if (label_base)
++ strvec_pushf(&child.args, "--base-label=%s", label_base);
strvec_push(&child.args, stash_oid);
ret = run_command(&child);
}
@@ sequencer.c: int apply_autostash(const char *path)
static int apply_save_autostash_ref(struct repository *r, const char *refname,
- int attempt_apply)
+ int attempt_apply,
-+ const char *label1, const char *label2,
-+ const char *label_ancestor)
++ const char *label_ours, const char *label_theirs,
++ const char *label_base)
{
struct object_id stash_oid;
char stash_oid_hex[GIT_MAX_HEXSZ + 1];
@@ sequencer.c: static int apply_save_autostash_ref(struct repository *r, const cha
oid_to_hex_r(stash_oid_hex, &stash_oid);
- ret = apply_save_autostash_oid(stash_oid_hex, attempt_apply);
+ ret = apply_save_autostash_oid(stash_oid_hex, attempt_apply,
-+ label1, label2, label_ancestor);
++ label_ours, label_theirs, label_base);
refs_delete_ref(get_main_ref_store(r), "", refname,
&stash_oid, REF_NO_DEREF);
@@ sequencer.c: static int apply_save_autostash_ref(struct repository *r, const cha
+}
+
+int apply_autostash_ref_with_labels(struct repository *r, const char *refname,
-+ const char *label1, const char *label2,
-+ const char *label_ancestor)
++ const char *label_ours, const char *label_theirs,
++ const char *label_base)
+{
+ return apply_save_autostash_ref(r, refname, 1,
-+ label1, label2, label_ancestor);
++ label_ours, label_theirs, label_base);
}
static int checkout_onto(struct repository *r, struct replay_opts *opts,
@@ sequencer.h: int save_autostash_ref(struct repository *r, const char *refname);
int apply_autostash_oid(const char *stash_oid);
int apply_autostash_ref(struct repository *r, const char *refname);
+int apply_autostash_ref_with_labels(struct repository *r, const char *refname,
-+ const char *label1, const char *label2,
-+ const char *label_ancestor);
++ const char *label_ours, const char *label_theirs,
++ const char *label_base);
#define SUMMARY_INITIAL_COMMIT (1 << 0)
#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
4: 1c29e19971 ! 4: aa18313362 checkout: -m (--merge) uses autostash when switching branches
@@ Documentation/git-checkout.adoc: $ git checkout mytopic
+When the `--merge` (`-m`) option is in effect and the locally
+modified files overlap with files that need to be updated by the
+branch switch, the changes are stashed and reapplied after the
-+switch. If the stash application results in conflicts, they are not
-+resolved and the stash is saved to the stash list:
++switch. If this process results in conflicts, a stash entry is saved
++and made available in `git stash list`:
------------
$ git checkout -m mytopic
@@ Documentation/git-checkout.adoc: $ git checkout mytopic
## Documentation/git-switch.adoc ##
@@ Documentation/git-switch.adoc: variable.
- If you have local modifications to one or more files that are
- different between the current branch and the branch to which
- you are switching, the command refuses to switch branches in
+
+ `-m`::
+ `--merge`::
+- If you have local modifications to one or more files that are
+- different between the current branch and the branch to which
+- you are switching, the command refuses to switch branches in
- order to preserve your modifications in context. However,
- with this option, a three-way merge between the current
- branch, your working tree contents, and the new branch is
@@ Documentation/git-switch.adoc: variable.
-paths are left unmerged, and you need to resolve the conflicts
-and mark the resolved paths with `git add` (or `git rm` if the merge
-should result in deletion of the path).
-+ order to preserve your modifications in context. With this
-+ option, the conflicting local changes are automatically
-+ stashed before the switch and reapplied afterwards. If the
-+ local changes do not overlap with the differences between
-+ branches, the switch proceeds without stashing. If
-+ reapplying the stash results in conflicts, the entry is
-+ saved to the stash list. Resolve the conflicts and run
-+ `git stash drop` when done, or clear the working tree
-+ (e.g. with `git reset --hard`) before running `git stash pop`
-+ later to re-apply your changes.
++ If you have local modifications to one or more files that
++ are different between the current branch and the branch to
++ which you are switching, the command normally refuses to
++ switch branches in order to preserve your modifications in
++ context. However, with this option, the conflicting local
++ changes are automatically stashed before the switch and
++ reapplied afterwards. If the local changes do not overlap
++ with the differences between branches, the switch proceeds
++ without stashing. If reapplying the stash results in
++ conflicts, the entry is saved to the stash list. Resolve
++ the conflicts and run `git stash drop` when done, or clear
++ the working tree (e.g. with `git reset --hard`) before
++ running `git stash pop` later to re-apply your changes.
`--conflict=<style>`::
The same as `--merge` option above, but changes the way the
@@ t/t7201-co.sh: test_expect_success 'checkout --merge --conflict=diff3 <branch>'
+ git clean -f &&
+
+ fill 0 x y z >same &&
++ git stash list >stash-before &&
+ git checkout -m side >actual 2>&1 &&
+ test_grep ! "Created autostash" actual &&
++ git stash list >stash-after &&
++ test_cmp stash-before stash-after &&
+ fill 0 x y z >expect &&
+ test_cmp expect same
+'
@@ t/t7201-co.sh: test_expect_success 'checkout --merge --conflict=diff3 <branch>'
+
+ fill DIRTY a b c INSERTED d >file &&
+
++ git stash list >stash-before &&
+ git checkout -m nonoverlap_base 2>stderr &&
+ test_grep "Applied autostash" stderr &&
+ test_grep ! "resulted in conflicts" stderr &&
+
++ git stash list >stash-after &&
++ test_cmp stash-before stash-after &&
++
+ fill DIRTY a b c d >expect &&
+ test_cmp expect file &&
+