aboutsummaryrefslogtreecommitdiff
path: root/diffcore-rename.c
diff options
context:
space:
mode:
authorJunio C Hamano <junkio@cox.net>2005-05-22 21:26:09 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-05-23 11:49:30 -0700
commitf7c1512af8ff4f821c530f9a4bc8f8ff25733d51 (patch)
tree78713f9ae744a9c5331298787d4c74f958e2e90f /diffcore-rename.c
parent60896c7bfed67f1c7364595213ef9239642f83c5 (diff)
downloadgit-f7c1512af8ff4f821c530f9a4bc8f8ff25733d51.tar.gz
git-f7c1512af8ff4f821c530f9a4bc8f8ff25733d51.tar.xz
[PATCH] Rename/copy detection fix.
The rename/copy detection logic in earlier round was only good enough to show patch output and discussion on the mailing list about the diff-raw format updates revealed many problems with it. This patch fixes all the ones known to me, without making things I want to do later impossible, mostly related to patch reordering. (1) Earlier rename/copy detector determined which one is rename and which one is copy too early, which made it impossible to later introduce diffcore transformers to reorder patches. This patch fixes it by moving that logic to the very end of the processing. (2) Earlier output routine diff_flush() was pruning all the "no-change" entries indiscriminatingly. This was done due to my false assumption that one of the requirements in the diff-raw output was not to show such an entry (which resulted in my incorrect comment about "diff-helper never being able to be equivalent to built-in diff driver"). My special thanks go to Linus for correcting me about this. When we produce diff-raw output, for the downstream to be able to tell renames from copies, sometimes it _is_ necessary to output "no-change" entries, and this patch adds diffcore_prune() function for doing it. (3) Earlier diff_filepair structure was trying to be not too specific about rename/copy operations, but the purpose of the structure was to record one or two paths, which _was_ indeed about rename/copy. This patch discards xfrm_msg field which was trying to be generic for this wrong reason, and introduces a couple of fields (rename_score and rename_rank) that are explicitly specific to rename/copy logic. One thing to note is that the information in a single diff_filepair structure _still_ does not distinguish renames from copies, and it is deliberately so. This is to allow patches to be reordered in later stages. (4) This patch also adds some tests about diff-raw format output and makes sure that necessary "no-change" entries appear on the output. Signed-off-by: Junio C Hamano <junkio@cox.net> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'diffcore-rename.c')
-rw-r--r--diffcore-rename.c112
1 files changed, 37 insertions, 75 deletions
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 52f09d231..e8de49358 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -119,27 +119,34 @@ static void record_rename_pair(struct diff_queue_struct *outq,
int rank,
int score)
{
- /* The rank is used to sort the final output, because there
- * are certain dependencies.
- *
- * - rank #0 depends on deleted ones.
- * - rank #1 depends on kept files before they are modified.
- * - rank #2 depends on kept files after they are modified;
- * currently not used.
- *
- * Therefore, the final output order should be:
+ /*
+ * These ranks are used to sort the final output, because there
+ * are certain dependencies:
*
- * 1. rank #0 rename/copy diffs.
+ * 1. rename/copy that depends on deleted ones.
* 2. deletions in the original.
- * 3. rank #1 rename/copy diffs.
- * 4. additions and modifications in the original.
- * 5. rank #2 rename/copy diffs; currently not used.
+ * 3. rename/copy that depends on the pre-edit image of kept files.
+ * 4. additions, modifications and no-modifications in the original.
+ * 5. rename/copy that depends on the post-edit image of kept files
+ * (note that we currently do not detect such rename/copy).
+ *
+ * The downstream diffcore transformers are free to reorder
+ * the entries as long as they keep file pairs that has the
+ * same p->one->path in earlier rename_rank to appear before
+ * later ones. This ordering is used by the diff_flush()
+ * logic to tell renames from copies, and also used by the
+ * diffcore_prune() logic to omit unnecessary
+ * "no-modification" entries.
*
- * To achieve this sort order, we give xform_work the number
- * above.
+ * To the final output routine, and in the diff-raw format
+ * output, a rename/copy that is based on a path that has a
+ * later entry that shares the same p->one->path and is not a
+ * deletion is a copy. Otherwise it is a rename.
*/
+
struct diff_filepair *dp = diff_queue(outq, src, dst);
- dp->xfrm_work = (rank * 2 + 1) | (score<<RENAME_SCORE_SHIFT);
+ dp->rename_rank = rank * 2 + 1;
+ dp->score = score;
dst->xfrm_flags |= RENAME_DST_MATCHED;
}
@@ -161,10 +168,8 @@ static void debug_filepair(const struct diff_filepair *p, int i)
{
debug_filespec(p->one, i, "one");
debug_filespec(p->two, i, "two");
- fprintf(stderr, "pair flags %d, orig order %d, score %d\n",
- (p->xfrm_work & ((1<<RENAME_SCORE_SHIFT) - 1)),
- p->orig_order,
- (p->xfrm_work >> RENAME_SCORE_SHIFT));
+ fprintf(stderr, "pair rank %d, orig order %d, score %d\n",
+ p->rename_rank, p->orig_order, p->score);
}
static void debug_queue(const char *msg, struct diff_queue_struct *q)
@@ -191,8 +196,8 @@ static int rank_compare(const void *a_, const void *b_)
{
const struct diff_filepair *a = *(const struct diff_filepair **)a_;
const struct diff_filepair *b = *(const struct diff_filepair **)b_;
- int a_rank = a->xfrm_work & ((1<<RENAME_SCORE_SHIFT) - 1);
- int b_rank = b->xfrm_work & ((1<<RENAME_SCORE_SHIFT) - 1);
+ int a_rank = a->rename_rank;
+ int b_rank = b->rename_rank;
if (a_rank != b_rank)
return a_rank - b_rank;
@@ -209,28 +214,6 @@ static int score_compare(const void *a_, const void *b_)
return b->score - a->score;
}
-static int needs_to_stay(struct diff_queue_struct *q, int i,
- struct diff_filespec *it)
-{
- /* If it will be used in later entry (either stay or used
- * as the source of rename/copy), we need to copy, not rename.
- */
- while (i < q->nr) {
- struct diff_filepair *p = q->queue[i++];
- if (!DIFF_FILE_VALID(p->two))
- continue; /* removed is fine */
- if (strcmp(p->one->path, it->path))
- continue; /* not relevant */
-
- /* p has its src set to *it and it is not a delete;
- * it will be used for in-place change or rename/copy,
- * so we cannot rename it out.
- */
- return 1;
- }
- return 0;
-}
-
int diff_scoreopt_parse(const char *opt)
{
int diglen, num, scale, i;
@@ -359,27 +342,24 @@ void diffcore_rename(int detect_rename, int minimum_score)
* downstream, so we assign the sort keys in this loop.
*
* See comments at the top of record_rename_pair for numbers used
- * to assign xfrm_work.
- *
- * Note that we have not annotated the diff_filepair with any comment
- * so there is nothing other than p to free.
+ * to assign rename_rank.
*/
for (i = 0; i < q->nr; i++) {
struct diff_filepair *dp, *p = q->queue[i];
if (!DIFF_FILE_VALID(p->one)) {
/* creation or unmerged entries */
dp = diff_queue(&outq, p->one, p->two);
- dp->xfrm_work = 4;
+ dp->rename_rank = 4;
}
else if (!DIFF_FILE_VALID(p->two)) {
/* deletion */
dp = diff_queue(&outq, p->one, p->two);
- dp->xfrm_work = 2;
+ dp->rename_rank = 2;
}
else {
/* modification, or stay as is */
dp = diff_queue(&outq, p->one, p->two);
- dp->xfrm_work = 4;
+ dp->rename_rank = 4;
}
free(p);
}
@@ -415,39 +395,21 @@ void diffcore_rename(int detect_rename, int minimum_score)
/* rename or copy */
struct diff_filepair *dp =
diff_queue(q, p->one, p->two);
- int msglen = (strlen(p->one->path) +
- strlen(p->two->path) + 100);
- int score = (p->xfrm_work >> RENAME_SCORE_SHIFT);
- dp->xfrm_msg = xmalloc(msglen);
+ dp->score = p->score;
/* if we have a later entry that is a rename/copy
* that depends on p->one, then we copy here.
* otherwise we rename it.
*/
- if (needs_to_stay(&outq, i+1, p->one)) {
- /* copy it */
- sprintf(dp->xfrm_msg,
- "similarity index %d%%\n"
- "copy from %s\n"
- "copy to %s\n",
- (int)(0.5 + score * 100 / MAX_SCORE),
- p->one->path, p->two->path);
- }
- else {
- /* rename it, and mark it as gone. */
+ if (!diff_needs_to_stay(&outq, i+1, p->one))
+ /* this is the last one, so mark it as gone.
+ */
p->one->xfrm_flags |= RENAME_SRC_GONE;
- sprintf(dp->xfrm_msg,
- "similarity index %d%%\n"
- "rename old %s\n"
- "rename new %s\n",
- (int)(0.5 + score * 100 / MAX_SCORE),
- p->one->path, p->two->path);
- }
}
else
- /* otherwise it is a modified (or stayed) entry */
+ /* otherwise it is a modified (or "stay") entry */
diff_queue(q, p->one, p->two);
- diff_free_filepair(p);
+ free(p);
}
free(outq.queue);