Merge branch 'tr/log-full-diff-keep-true-parents' into maint
Output from "git log --full-diff -- <pathspec>" looked strange, because comparison was done with the previous ancestor that touched the specified <pathspec>, causing the patches for paths outside the pathspec to show more than the single commit has changed. * tr/log-full-diff-keep-true-parents: log: use true parents for diff when walking reflogs log: use true parents for diff even when rewriting
This commit is contained in:
commit
6930cd10de
@ -10,6 +10,7 @@
|
|||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
#include "userdiff.h"
|
#include "userdiff.h"
|
||||||
#include "sha1-array.h"
|
#include "sha1-array.h"
|
||||||
|
#include "revision.h"
|
||||||
|
|
||||||
static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
|
static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
|
||||||
{
|
{
|
||||||
@ -1383,7 +1384,7 @@ void diff_tree_combined(const unsigned char *sha1,
|
|||||||
void diff_tree_combined_merge(const struct commit *commit, int dense,
|
void diff_tree_combined_merge(const struct commit *commit, int dense,
|
||||||
struct rev_info *rev)
|
struct rev_info *rev)
|
||||||
{
|
{
|
||||||
struct commit_list *parent = commit->parents;
|
struct commit_list *parent = get_saved_parents(rev, commit);
|
||||||
struct sha1_array parents = SHA1_ARRAY_INIT;
|
struct sha1_array parents = SHA1_ARRAY_INIT;
|
||||||
|
|
||||||
while (parent) {
|
while (parent) {
|
||||||
|
16
commit.c
16
commit.c
@ -377,6 +377,22 @@ unsigned commit_list_count(const struct commit_list *l)
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct commit_list *copy_commit_list(struct commit_list *list)
|
||||||
|
{
|
||||||
|
struct commit_list *head = NULL;
|
||||||
|
struct commit_list **pp = &head;
|
||||||
|
while (list) {
|
||||||
|
struct commit_list *new;
|
||||||
|
new = xmalloc(sizeof(struct commit_list));
|
||||||
|
new->item = list->item;
|
||||||
|
new->next = NULL;
|
||||||
|
*pp = new;
|
||||||
|
pp = &new->next;
|
||||||
|
list = list->next;
|
||||||
|
}
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
void free_commit_list(struct commit_list *list)
|
void free_commit_list(struct commit_list *list)
|
||||||
{
|
{
|
||||||
while (list) {
|
while (list) {
|
||||||
|
3
commit.h
3
commit.h
@ -62,6 +62,9 @@ struct commit_list *commit_list_insert_by_date(struct commit *item,
|
|||||||
struct commit_list **list);
|
struct commit_list **list);
|
||||||
void commit_list_sort_by_date(struct commit_list **list);
|
void commit_list_sort_by_date(struct commit_list **list);
|
||||||
|
|
||||||
|
/* Shallow copy of the input list */
|
||||||
|
struct commit_list *copy_commit_list(struct commit_list *list);
|
||||||
|
|
||||||
void free_commit_list(struct commit_list *list);
|
void free_commit_list(struct commit_list *list);
|
||||||
|
|
||||||
/* Commit formats */
|
/* Commit formats */
|
||||||
|
@ -738,7 +738,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
|
|||||||
sha1 = commit->tree->object.sha1;
|
sha1 = commit->tree->object.sha1;
|
||||||
|
|
||||||
/* Root commit? */
|
/* Root commit? */
|
||||||
parents = commit->parents;
|
parents = get_saved_parents(opt, commit);
|
||||||
if (!parents) {
|
if (!parents) {
|
||||||
if (opt->show_root_diff) {
|
if (opt->show_root_diff) {
|
||||||
diff_root_tree_sha1(sha1, "", &opt->diffopt);
|
diff_root_tree_sha1(sha1, "", &opt->diffopt);
|
||||||
|
65
revision.c
65
revision.c
@ -15,6 +15,7 @@
|
|||||||
#include "string-list.h"
|
#include "string-list.h"
|
||||||
#include "line-log.h"
|
#include "line-log.h"
|
||||||
#include "mailmap.h"
|
#include "mailmap.h"
|
||||||
|
#include "commit-slab.h"
|
||||||
|
|
||||||
volatile show_early_output_fn_t show_early_output;
|
volatile show_early_output_fn_t show_early_output;
|
||||||
|
|
||||||
@ -2763,7 +2764,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int want_ancestry(struct rev_info *revs)
|
static inline int want_ancestry(const struct rev_info *revs)
|
||||||
{
|
{
|
||||||
return (revs->rewrite_parents || revs->children.name);
|
return (revs->rewrite_parents || revs->children.name);
|
||||||
}
|
}
|
||||||
@ -2820,6 +2821,14 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
|
|||||||
if (action == commit_show &&
|
if (action == commit_show &&
|
||||||
!revs->show_all &&
|
!revs->show_all &&
|
||||||
revs->prune && revs->dense && want_ancestry(revs)) {
|
revs->prune && revs->dense && want_ancestry(revs)) {
|
||||||
|
/*
|
||||||
|
* --full-diff on simplified parents is no good: it
|
||||||
|
* will show spurious changes from the commits that
|
||||||
|
* were elided. So we save the parents on the side
|
||||||
|
* when --full-diff is in effect.
|
||||||
|
*/
|
||||||
|
if (revs->full_diff)
|
||||||
|
save_parents(revs, commit);
|
||||||
if (rewrite_parents(revs, commit, rewrite_one) < 0)
|
if (rewrite_parents(revs, commit, rewrite_one) < 0)
|
||||||
return commit_error;
|
return commit_error;
|
||||||
}
|
}
|
||||||
@ -2839,6 +2848,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
|
|||||||
free(entry);
|
free(entry);
|
||||||
|
|
||||||
if (revs->reflog_info) {
|
if (revs->reflog_info) {
|
||||||
|
save_parents(revs, commit);
|
||||||
fake_reflog_parent(revs->reflog_info, commit);
|
fake_reflog_parent(revs->reflog_info, commit);
|
||||||
commit->object.flags &= ~(ADDED | SEEN | SHOWN);
|
commit->object.flags &= ~(ADDED | SEEN | SHOWN);
|
||||||
}
|
}
|
||||||
@ -3038,6 +3048,8 @@ struct commit *get_revision(struct rev_info *revs)
|
|||||||
c = get_revision_internal(revs);
|
c = get_revision_internal(revs);
|
||||||
if (c && revs->graph)
|
if (c && revs->graph)
|
||||||
graph_update(revs->graph, c);
|
graph_update(revs->graph, c);
|
||||||
|
if (!c)
|
||||||
|
free_saved_parents(revs);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3069,3 +3081,54 @@ void put_revision_mark(const struct rev_info *revs, const struct commit *commit)
|
|||||||
fputs(mark, stdout);
|
fputs(mark, stdout);
|
||||||
putchar(' ');
|
putchar(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define_commit_slab(saved_parents, struct commit_list *);
|
||||||
|
|
||||||
|
#define EMPTY_PARENT_LIST ((struct commit_list *)-1)
|
||||||
|
|
||||||
|
void save_parents(struct rev_info *revs, struct commit *commit)
|
||||||
|
{
|
||||||
|
struct commit_list **pp;
|
||||||
|
|
||||||
|
if (!revs->saved_parents_slab) {
|
||||||
|
revs->saved_parents_slab = xmalloc(sizeof(struct saved_parents));
|
||||||
|
init_saved_parents(revs->saved_parents_slab);
|
||||||
|
}
|
||||||
|
|
||||||
|
pp = saved_parents_at(revs->saved_parents_slab, commit);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When walking with reflogs, we may visit the same commit
|
||||||
|
* several times: once for each appearance in the reflog.
|
||||||
|
*
|
||||||
|
* In this case, save_parents() will be called multiple times.
|
||||||
|
* We want to keep only the first set of parents. We need to
|
||||||
|
* store a sentinel value for an empty (i.e., NULL) parent
|
||||||
|
* list to distinguish it from a not-yet-saved list, however.
|
||||||
|
*/
|
||||||
|
if (*pp)
|
||||||
|
return;
|
||||||
|
if (commit->parents)
|
||||||
|
*pp = copy_commit_list(commit->parents);
|
||||||
|
else
|
||||||
|
*pp = EMPTY_PARENT_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit)
|
||||||
|
{
|
||||||
|
struct commit_list *parents;
|
||||||
|
|
||||||
|
if (!revs->saved_parents_slab)
|
||||||
|
return commit->parents;
|
||||||
|
|
||||||
|
parents = *saved_parents_at(revs->saved_parents_slab, commit);
|
||||||
|
if (parents == EMPTY_PARENT_LIST)
|
||||||
|
return NULL;
|
||||||
|
return parents;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_saved_parents(struct rev_info *revs)
|
||||||
|
{
|
||||||
|
if (revs->saved_parents_slab)
|
||||||
|
clear_saved_parents(revs->saved_parents_slab);
|
||||||
|
}
|
||||||
|
20
revision.h
20
revision.h
@ -25,6 +25,7 @@
|
|||||||
struct rev_info;
|
struct rev_info;
|
||||||
struct log_info;
|
struct log_info;
|
||||||
struct string_list;
|
struct string_list;
|
||||||
|
struct saved_parents;
|
||||||
|
|
||||||
struct rev_cmdline_info {
|
struct rev_cmdline_info {
|
||||||
unsigned int nr;
|
unsigned int nr;
|
||||||
@ -187,6 +188,9 @@ struct rev_info {
|
|||||||
|
|
||||||
/* line level range that we are chasing */
|
/* line level range that we are chasing */
|
||||||
struct decoration line_log_data;
|
struct decoration line_log_data;
|
||||||
|
|
||||||
|
/* copies of the parent lists, for --full-diff display */
|
||||||
|
struct saved_parents *saved_parents_slab;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define REV_TREE_SAME 0
|
#define REV_TREE_SAME 0
|
||||||
@ -273,4 +277,20 @@ typedef enum rewrite_result (*rewrite_parent_fn_t)(struct rev_info *revs, struct
|
|||||||
|
|
||||||
extern int rewrite_parents(struct rev_info *revs, struct commit *commit,
|
extern int rewrite_parents(struct rev_info *revs, struct commit *commit,
|
||||||
rewrite_parent_fn_t rewrite_parent);
|
rewrite_parent_fn_t rewrite_parent);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save a copy of the parent list, and return the saved copy. This is
|
||||||
|
* used by the log machinery to retrieve the original parents when
|
||||||
|
* commit->parents has been modified by history simpification.
|
||||||
|
*
|
||||||
|
* You may only call save_parents() once per commit (this is checked
|
||||||
|
* for non-root commits).
|
||||||
|
*
|
||||||
|
* get_saved_parents() will transparently return commit->parents if
|
||||||
|
* history simplification is off.
|
||||||
|
*/
|
||||||
|
extern void save_parents(struct rev_info *revs, struct commit *commit);
|
||||||
|
extern struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit);
|
||||||
|
extern void free_saved_parents(struct rev_info *revs);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -144,4 +144,26 @@ test_expect_success 'empty reflog file' '
|
|||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
# This guards against the alternative of showing the diffs vs. the
|
||||||
|
# reflog ancestor. The reflog used is designed to list the commits
|
||||||
|
# more than once, so as to exercise the corresponding logic.
|
||||||
|
test_expect_success 'git log -g -p shows diffs vs. parents' '
|
||||||
|
test_commit two &&
|
||||||
|
git branch flipflop &&
|
||||||
|
git update-ref refs/heads/flipflop -m flip1 HEAD^ &&
|
||||||
|
git update-ref refs/heads/flipflop -m flop1 HEAD &&
|
||||||
|
git update-ref refs/heads/flipflop -m flip2 HEAD^ &&
|
||||||
|
git log -g -p flipflop >reflog &&
|
||||||
|
grep -v ^Reflog reflog >actual &&
|
||||||
|
git log -1 -p HEAD^ >log.one &&
|
||||||
|
git log -1 -p HEAD >log.two &&
|
||||||
|
(
|
||||||
|
cat log.one; echo
|
||||||
|
cat log.two; echo
|
||||||
|
cat log.one; echo
|
||||||
|
cat log.two
|
||||||
|
) >expect &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -127,4 +127,10 @@ test_expect_success 'full history simplification without parent' '
|
|||||||
}
|
}
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success '--full-diff is not affected by --parents' '
|
||||||
|
git log -p --pretty="%H" --full-diff -- file >expected &&
|
||||||
|
git log -p --pretty="%H" --full-diff --parents -- file >actual &&
|
||||||
|
test_cmp expected actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user