mirror of
https://github.com/git/git.git
synced 2024-11-05 08:47:56 +01:00
Merge branch 'lt/follow'
* lt/follow: Fix up "git log --follow" a bit.. Finally implement "git log --follow"
This commit is contained in:
commit
1066c2c548
5 changed files with 89 additions and 1 deletions
|
@ -58,6 +58,11 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
|
|||
argc = setup_revisions(argc, argv, rev, "HEAD");
|
||||
if (rev->diffopt.pickaxe || rev->diffopt.filter)
|
||||
rev->always_show_header = 0;
|
||||
if (rev->diffopt.follow_renames) {
|
||||
rev->always_show_header = 0;
|
||||
if (rev->diffopt.nr_paths != 1)
|
||||
usage("git logs can only follow renames on one pathname at a time");
|
||||
}
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if (!strcmp(arg, "--decorate")) {
|
||||
|
|
2
diff.c
2
diff.c
|
@ -2210,6 +2210,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
|
|||
}
|
||||
else if (!strcmp(arg, "--find-copies-harder"))
|
||||
options->find_copies_harder = 1;
|
||||
else if (!strcmp(arg, "--follow"))
|
||||
options->follow_renames = 1;
|
||||
else if (!strcmp(arg, "--abbrev"))
|
||||
options->abbrev = DEFAULT_ABBREV;
|
||||
else if (!prefixcmp(arg, "--abbrev=")) {
|
||||
|
|
1
diff.h
1
diff.h
|
@ -55,6 +55,7 @@ struct diff_options {
|
|||
full_index:1,
|
||||
silent_on_remove:1,
|
||||
find_copies_harder:1,
|
||||
follow_renames:1,
|
||||
color_diff:1,
|
||||
color_diff_words:1,
|
||||
has_changes:1,
|
||||
|
|
|
@ -1230,7 +1230,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
|||
|
||||
if (revs->prune_data) {
|
||||
diff_tree_setup_paths(revs->prune_data, &revs->pruning);
|
||||
revs->prune_fn = try_to_simplify_commit;
|
||||
/* Can't prune commits with rename following: the paths change.. */
|
||||
if (!revs->diffopt.follow_renames)
|
||||
revs->prune_fn = try_to_simplify_commit;
|
||||
if (!revs->full_diff)
|
||||
diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
|
||||
}
|
||||
|
|
78
tree-diff.c
78
tree-diff.c
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
#include "cache.h"
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
#include "tree.h"
|
||||
|
||||
static char *malloc_base(const char *base, int baselen, const char *path, int pathlen)
|
||||
|
@ -290,6 +291,78 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, stru
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does it look like the resulting diff might be due to a rename?
|
||||
* - single entry
|
||||
* - not a valid previous file
|
||||
*/
|
||||
static inline int diff_might_be_rename(void)
|
||||
{
|
||||
return diff_queued_diff.nr == 1 &&
|
||||
!DIFF_FILE_VALID(diff_queued_diff.queue[0]->one);
|
||||
}
|
||||
|
||||
static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
|
||||
{
|
||||
struct diff_options diff_opts;
|
||||
struct diff_queue_struct *q = &diff_queued_diff;
|
||||
struct diff_filepair *choice;
|
||||
const char *paths[1];
|
||||
int i;
|
||||
|
||||
/* Remove the file creation entry from the diff queue, and remember it */
|
||||
choice = q->queue[0];
|
||||
q->nr = 0;
|
||||
|
||||
diff_setup(&diff_opts);
|
||||
diff_opts.recursive = 1;
|
||||
diff_opts.detect_rename = DIFF_DETECT_RENAME;
|
||||
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
||||
diff_opts.single_follow = opt->paths[0];
|
||||
paths[0] = NULL;
|
||||
diff_tree_setup_paths(paths, &diff_opts);
|
||||
if (diff_setup_done(&diff_opts) < 0)
|
||||
die("unable to set up diff options to follow renames");
|
||||
diff_tree(t1, t2, base, &diff_opts);
|
||||
diffcore_std(&diff_opts);
|
||||
|
||||
/* Go through the new set of filepairing, and see if we find a more interesting one */
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filepair *p = q->queue[i];
|
||||
|
||||
/*
|
||||
* Found a source? Not only do we use that for the new
|
||||
* diff_queued_diff, we will also use that as the path in
|
||||
* the future!
|
||||
*/
|
||||
if ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, opt->paths[0])) {
|
||||
/* Switch the file-pairs around */
|
||||
q->queue[i] = choice;
|
||||
choice = p;
|
||||
|
||||
/* Update the path we use from now on.. */
|
||||
opt->paths[0] = xstrdup(p->one->path);
|
||||
diff_tree_setup_paths(opt->paths, opt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Then, discard all the non-relevane file pairs...
|
||||
*/
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
struct diff_filepair *p = q->queue[i];
|
||||
diff_free_filepair(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* .. and re-instate the one we want (which might be either the
|
||||
* original one, or the rename/copy we found)
|
||||
*/
|
||||
q->queue[0] = choice;
|
||||
q->nr = 1;
|
||||
}
|
||||
|
||||
int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base, struct diff_options *opt)
|
||||
{
|
||||
void *tree1, *tree2;
|
||||
|
@ -306,6 +379,11 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
|
|||
init_tree_desc(&t1, tree1, size1);
|
||||
init_tree_desc(&t2, tree2, size2);
|
||||
retval = diff_tree(&t1, &t2, base, opt);
|
||||
if (opt->follow_renames && diff_might_be_rename()) {
|
||||
init_tree_desc(&t1, tree1, size1);
|
||||
init_tree_desc(&t2, tree2, size2);
|
||||
try_to_follow_renames(&t1, &t2, base, opt);
|
||||
}
|
||||
free(tree1);
|
||||
free(tree2);
|
||||
return retval;
|
||||
|
|
Loading…
Reference in a new issue