mirror of
https://github.com/git/git.git
synced 2024-11-05 16:52:59 +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");
|
argc = setup_revisions(argc, argv, rev, "HEAD");
|
||||||
if (rev->diffopt.pickaxe || rev->diffopt.filter)
|
if (rev->diffopt.pickaxe || rev->diffopt.filter)
|
||||||
rev->always_show_header = 0;
|
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++) {
|
for (i = 1; i < argc; i++) {
|
||||||
const char *arg = argv[i];
|
const char *arg = argv[i];
|
||||||
if (!strcmp(arg, "--decorate")) {
|
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"))
|
else if (!strcmp(arg, "--find-copies-harder"))
|
||||||
options->find_copies_harder = 1;
|
options->find_copies_harder = 1;
|
||||||
|
else if (!strcmp(arg, "--follow"))
|
||||||
|
options->follow_renames = 1;
|
||||||
else if (!strcmp(arg, "--abbrev"))
|
else if (!strcmp(arg, "--abbrev"))
|
||||||
options->abbrev = DEFAULT_ABBREV;
|
options->abbrev = DEFAULT_ABBREV;
|
||||||
else if (!prefixcmp(arg, "--abbrev=")) {
|
else if (!prefixcmp(arg, "--abbrev=")) {
|
||||||
|
|
1
diff.h
1
diff.h
|
@ -55,6 +55,7 @@ struct diff_options {
|
||||||
full_index:1,
|
full_index:1,
|
||||||
silent_on_remove:1,
|
silent_on_remove:1,
|
||||||
find_copies_harder:1,
|
find_copies_harder:1,
|
||||||
|
follow_renames:1,
|
||||||
color_diff:1,
|
color_diff:1,
|
||||||
color_diff_words:1,
|
color_diff_words:1,
|
||||||
has_changes: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) {
|
if (revs->prune_data) {
|
||||||
diff_tree_setup_paths(revs->prune_data, &revs->pruning);
|
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)
|
if (!revs->full_diff)
|
||||||
diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
|
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 "cache.h"
|
||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
|
#include "diffcore.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
|
|
||||||
static char *malloc_base(const char *base, int baselen, const char *path, int pathlen)
|
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;
|
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)
|
int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base, struct diff_options *opt)
|
||||||
{
|
{
|
||||||
void *tree1, *tree2;
|
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(&t1, tree1, size1);
|
||||||
init_tree_desc(&t2, tree2, size2);
|
init_tree_desc(&t2, tree2, size2);
|
||||||
retval = diff_tree(&t1, &t2, base, opt);
|
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(tree1);
|
||||||
free(tree2);
|
free(tree2);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
Loading…
Reference in a new issue