Hi,
This may look intimidating, but it's actually 3.5 separate things:
merge-recursive: remove dead conditional in update_stages() merge-recursive: internal flag to avoid touching the worktree merge-recursive: -Xindex-only to leave worktree unchanged
These are unchanged from tr/merge-recursive-index-only. I'm just resending them here because it's been a while, the rest depends on it, and it makes it more obvious how patch 8 fits in.
pretty: refactor add_merge_info() into parts log: add a merge base inspection option
This seemed a good idea when I wrote it, but the more I think about it, the less useful it appears to me. I left it here as a demonstration that computing the merge bases as we go is feasible.
combine-diff: do not pass revs->dense_combined_merges redundantly Fold all merge diff variants into an enum
Some initial refactoring.
merge-recursive: allow storing conflict hunks in index log --remerge-diff: show what the conflict resolution changed
The real meat.
I'll only describe the last patches here.
Context: last summer (yes, it's been a while) I investigated some angles of looking at merges that make it easier to investigate mismerges. The problem space is: it is exceedingly hard to find instances of
git merge -s ours foo git merge -Xours foo manually resolve some hunks in favor of one side
after some time has passed. The only ways I found with existing machinery involve too much manual inspection.
At the time I made some scripts that look at the problem in various ways:
https://github.com/trast/evilmergediff
This series explores another angle, which I call "remerge diff". It works by re-doing the merge in core, using features from patches 1-3 and 7. Likely that will result in conflicts, which are formatted in the usual <<<<<<< way. Then it diffs this "remerge" against the merge's tree that is recorded in history.
This tends to give nice results in practice; I pasted a particularly pretty example at the bottom.
However, this is RFC because nontrivial merges can easily lead to states that I cannot handle with the current -- pretty neat and simple, I think -- implementation.
For example, a delete/modify conflict does not result in a nice conflict-hunk formatted file, it just leaves 2 (out of 3) stages in the index. I can then not write a tree from that.
This could be fixed by adding a pass that turns a missing stage 2 or 3 into an empty file at that stage, so that the resulting file looks like
<<<<<<< ======= ... stage 3 content goes here ... >>>>>>>
(analogously for stage 3 missing).
But that still doesn't handle at least directory/file conflicts.
So I would welcome comments, and/or better ideas, on the following proposed resolution:
* Implement what I described last, to take care of delete/modify conflicts.
* Punt on more complex conflicts, by removing those files from the index, and emitting a warning about those files instead.
Note that even without delete/modify handling, this covers the vast majority of merges: among some 8090 merges I sampled from git.git, there are only 13 that trigger the "BUG: ..." message, and 34 octopuses.
Example run:
$ git show --remerge-diff a39b15b4f6a3f08b67b17d968935d177821e680f
[...]
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 0f4473e..175e6e3 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -490,16 +490,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
OPT_BOOLEAN('u', "unmerged", &show_unmerged,
N_("show unmerged files in the output")),
OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo,
-<<<<<<< f12e49ae877ad0644b9b9939b7cb742da98691d2
N_("show resolve-undo information")),
- { OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], N_("pattern"),
+ { OPTION_CALLBACK, 'x', "exclude", &exclude_list, N_("pattern"),
N_("skip files matching pattern"),
-=======
- "show resolve-undo information"),
- { OPTION_CALLBACK, 'x', "exclude",
- &exclude_list, "pattern",
- "skip files matching pattern",
->>>>>>> 72aeb18772deeb386da7dd8997b969877bd29e41
0, option_parse_exclude },
{ OPTION_CALLBACK, 'X', "exclude-from", &dir, N_("file"),
N_("exclude patterns are read from <file>"),
diff --git a/dir.c b/dir.c
index 8c58ce4..cf1e6b0 100644
--- a/dir.c
+++ b/dir.c
@@ -1674,14 +1674,14 @@ void free_pathspec(struct pathspec *pathspec)
pathspec->items = NULL;
}
-<<<<<<< f12e49ae877ad0644b9b9939b7cb742da98691d2
int limit_pathspec_to_literal(void)
{
static int flag = -1;
if (flag < 0)
flag = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
return flag;
-=======
+}
+
/*
* Frees memory within dir which was allocated for exclude lists and
* the exclude_stack. Does not free dir itself.
@@ -1710,5 +1710,4 @@ void clear_directory(struct dir_struct *dir)
free(stk);
stk = prev;
}
->>>>>>> 72aeb18772deeb386da7dd8997b969877bd29e41
}Documentation/merge-strategies.txt | 9 +++ Documentation/rev-list-options.txt | 14 ++++ builtin/diff-files.c | 5 +- builtin/diff-tree.c | 2 +- builtin/diff.c | 12 +-- builtin/fmt-merge-msg.c | 2 +- builtin/log.c | 9 +-- builtin/merge.c | 1 - combine-diff.c | 13 ++-- commit.h | 1 + diff-lib.c | 13 ++-- diff.h | 6 +- log-tree.c | 69 +++++++++++++++++- merge-recursive.c | 52 +++++++------ merge-recursive.h | 3 + pretty.c | 41 +++++++---- revision.c | 17 ++--- revision.h | 26 ++++++- submodule.c | 3 +- t/t3030-merge-recursive.sh | 33 +++++++++ t/t4202-log.sh | 34 +++++++++ t/t4213-log-remerge-diff.sh | 145 +++++++++++++++++++++++++++++++++++++ 22 files changed, 426 insertions(+), 84 deletions(-) create mode 100755 t/t4213-log-remerge-diff.sh
-- 1.9.rc2.232.gdd31389