default merge driver has no clue how to deal with this. Furthermore
the conflicts are presented with more <<<< ==== >>>> markers than
necessary; this is because the default merge driver makes pointless
- effects to look at the individual line changes inside a ChangeLog entry.
+ efforts to look at the individual line changes inside a ChangeLog entry.
This program serves as a 'git' merge driver that avoids these problems.
1. It produces no conflict when ChangeLog entries have been inserted
{
char *string;
size_t length;
+ /* Cache for the hash code. */
+ bool hashcode_cached;
+ size_t hashcode;
};
+/* Create an entry.
+ The memory region passed by the caller must of indefinite extent. It is
+ *not* copied here. */
+static struct entry *
+entry_create (char *string, size_t length)
+{
+ struct entry *result = XMALLOC (struct entry);
+ result->string = string;
+ result->length = length;
+ result->hashcode_cached = false;
+ return result;
+}
+
/* Compare two entries for equality. */
static bool
entry_equals (const void *elt1, const void *elt2)
const struct entry *entry2 = (const struct entry *) elt2;
return entry1->length == entry2->length
&& memcmp (entry1->string, entry2->string, entry1->length) == 0;
-};
+}
/* Return a hash code of the contents of a ChangeLog entry. */
static size_t
entry_hashcode (const void *elt)
{
- const struct entry *entry = (const struct entry *) elt;
- /* See http://www.haible.de/bruno/hashfunc.html. */
- const char *s;
- size_t n;
- size_t h = 0;
+ struct entry *entry = (struct entry *) elt;
+ if (!entry->hashcode_cached)
+ {
+ /* See http://www.haible.de/bruno/hashfunc.html. */
+ const char *s;
+ size_t n;
+ size_t h = 0;
- for (s = entry->string, n = entry->length; n > 0; s++, n--)
- h = (unsigned char) *s + ((h << 9) | (h >> (sizeof (size_t) * CHAR_BIT - 9)));
+ for (s = entry->string, n = entry->length; n > 0; s++, n--)
+ h = (unsigned char) *s + ((h << 9) | (h >> (sizeof (size_t) * CHAR_BIT - 9)));
- return h;
+ entry->hashcode = h;
+ entry->hashcode_cached = true;
+ }
+ return entry->hashcode;
}
/* Perform a fuzzy comparison of two ChangeLog entries.
}
}
- curr = XMALLOC (struct entry);
- curr->string = start;
- curr->length = ptr - start;
+ curr = entry_create (start, ptr - start);
gl_list_add_last (result->entries_list, curr);
gl_list_add_first (result->entries_reversed, curr);
if (best_similarity < FSTRCMP_STRICTER_THRESHOLD)
return false;
- new_split[0] = XMALLOC (struct entry);
- new_split[0]->string = new_entry->string;
- new_split[0]->length = best_split_offset + 1;
+ new_split[0] = entry_create (new_entry->string, best_split_offset + 1);
- new_split[1] = XMALLOC (struct entry);
{
size_t len1 = new_title_len;
size_t len2 = new_entry->length - best_split_offset;
char *combined = XNMALLOC (len1 + len2, char);
memcpy (combined, new_entry->string, len1);
memcpy (combined + len1, new_entry->string + best_split_offset, len2);
- new_split[1]->string = combined;
- new_split[1]->length = len1 + len2;
+ new_split[1] = entry_create (combined, len1 + len2);
}
return true;
/* Long options. */
static const struct option long_options[] =
-{
+{
{ "help", no_argument, NULL, 'h' },
{ "split-merged-entry", no_argument, NULL, CHAR_MAX + 1 },
{ "version", no_argument, NULL, 'V' },
How to distinguish these situation? There are several hints:
- During a "git stash apply", GIT_REFLOG_ACTION is not set. During
a "git pull", it is set to 'pull '. During a "git pull --rebase",
- it is set to 'pull --rebase'.
+ it is set to 'pull --rebase'. During a "git cherry-pick", it is
+ set to 'cherry-pick'.
- During a "git stash apply", there is an environment variable of
the form GITHEAD_<40_hex_digits>='Stashed changes'. */
{
downstream = true;
else
{
- /* "git stash apply", "git rebase" and similar. */
+ /* "git stash apply", "git rebase", "git cherry-pick" and
+ similar. */
downstream = false;
}
}
result_entries_pointers[k],
changed_entry);
}
- else
+ else if (!entry_equals (ancestor_file.entries[i],
+ changed_entry))
{
struct conflict *c = XMALLOC (struct conflict);
c->num_old_entries = 1;
}
else
{
- struct conflict *c = XMALLOC (struct conflict);
+ struct conflict *c;
+ ASSERT (!entry_equals (ancestor_file.entries[i],
+ changed_entry));
+ c = XMALLOC (struct conflict);
c->num_old_entries = 1;
c->old_entries =
XNMALLOC (c->num_old_entries, struct entry *);
}
else
{
- struct conflict *c = XMALLOC (struct conflict);
+ struct conflict *c;
+ ASSERT (!entry_equals (ancestor_file.entries[i],
+ changed_entry));
+ c = XMALLOC (struct conflict);
c->num_old_entries = 1;
c->old_entries =
XNMALLOC (c->num_old_entries, struct entry *);
for (i = 0; i < n; i++)
conflict_write (fp, (struct conflict *) gl_list_get_at (result_conflicts, i));
}
+ /* Output the modified and unmodified entries, in order. */
{
- size_t n = gl_list_size (result_entries);
- size_t i;
- for (i = 0; i < n; i++)
- entry_write (fp, (struct entry *) gl_list_get_at (result_entries, i));
+ gl_list_iterator_t iter = gl_list_iterator (result_entries);
+ const void *elt;
+ gl_list_node_t node;
+ while (gl_list_iterator_next (&iter, &elt, &node))
+ entry_write (fp, (struct entry *) elt);
+ gl_list_iterator_free (&iter);
}
if (fwriteerror (fp))