*
* Copyright (C) Linus Torvalds, 2005
*/
-#define DBRT_DEBUG 1
#include "cache.h"
-
#include "object.h"
#include "tree.h"
#include "tree-walk.h"
#include "cache-tree.h"
-#include <sys/time.h>
-#include <signal.h>
+#include "unpack-trees.h"
#include "builtin.h"
-static int reset = 0;
-static int merge = 0;
-static int update = 0;
-static int index_only = 0;
-static int nontrivial_merge = 0;
-static int trivial_merges_only = 0;
-static int aggressive = 0;
-static int verbose_update = 0;
-static volatile int progress_update = 0;
-static const char *prefix = NULL;
-
-static int head_idx = -1;
-static int merge_size = 0;
-
static struct object_list *trees = NULL;
-static struct cache_entry df_conflict_entry;
-
-struct tree_entry_list {
- struct tree_entry_list *next;
- unsigned directory : 1;
- unsigned executable : 1;
- unsigned symlink : 1;
- unsigned int mode;
- const char *name;
- const unsigned char *sha1;
-};
-
-static struct tree_entry_list df_conflict_list;
-
-typedef int (*merge_fn_t)(struct cache_entry **src);
-
-static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
-{
- struct tree_desc desc;
- struct name_entry one;
- struct tree_entry_list *ret = NULL;
- struct tree_entry_list **list_p = &ret;
-
- desc.buf = tree->buffer;
- desc.size = tree->size;
-
- while (tree_entry(&desc, &one)) {
- struct tree_entry_list *entry;
-
- entry = xmalloc(sizeof(struct tree_entry_list));
- entry->name = one.path;
- entry->sha1 = one.sha1;
- entry->mode = one.mode;
- entry->directory = S_ISDIR(one.mode) != 0;
- entry->executable = (one.mode & S_IXUSR) != 0;
- entry->symlink = S_ISLNK(one.mode) != 0;
- entry->next = NULL;
-
- *list_p = entry;
- list_p = &entry->next;
- }
- return ret;
-}
-
-static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
-{
- int len1 = strlen(name1);
- int len2 = strlen(name2);
- int len = len1 < len2 ? len1 : len2;
- int ret = memcmp(name1, name2, len);
- unsigned char c1, c2;
- if (ret)
- return ret;
- c1 = name1[len];
- c2 = name2[len];
- if (!c1 && dir1)
- c1 = '/';
- if (!c2 && dir2)
- c2 = '/';
- ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
- if (c1 && c2 && !ret)
- ret = len1 - len2;
- return ret;
-}
-
-static int unpack_trees_rec(struct tree_entry_list **posns, int len,
- const char *base, merge_fn_t fn, int *indpos)
-{
- int baselen = strlen(base);
- int src_size = len + 1;
- do {
- int i;
- const char *first;
- int firstdir = 0;
- int pathlen;
- unsigned ce_size;
- struct tree_entry_list **subposns;
- struct cache_entry **src;
- int any_files = 0;
- int any_dirs = 0;
- char *cache_name;
- int ce_stage;
-
- /* Find the first name in the input. */
-
- first = NULL;
- cache_name = NULL;
-
- /* Check the cache */
- if (merge && *indpos < active_nr) {
- /* This is a bit tricky: */
- /* If the index has a subdirectory (with
- * contents) as the first name, it'll get a
- * filename like "foo/bar". But that's after
- * "foo", so the entry in trees will get
- * handled first, at which point we'll go into
- * "foo", and deal with "bar" from the index,
- * because the base will be "foo/". The only
- * way we can actually have "foo/bar" first of
- * all the things is if the trees don't
- * contain "foo" at all, in which case we'll
- * handle "foo/bar" without going into the
- * directory, but that's fine (and will return
- * an error anyway, with the added unknown
- * file case.
- */
-
- cache_name = active_cache[*indpos]->name;
- if (strlen(cache_name) > baselen &&
- !memcmp(cache_name, base, baselen)) {
- cache_name += baselen;
- first = cache_name;
- } else {
- cache_name = NULL;
- }
- }
-
-#if DBRT_DEBUG > 1
- if (first)
- printf("index %s\n", first);
-#endif
- for (i = 0; i < len; i++) {
- if (!posns[i] || posns[i] == &df_conflict_list)
- continue;
-#if DBRT_DEBUG > 1
- printf("%d %s\n", i + 1, posns[i]->name);
-#endif
- if (!first || entcmp(first, firstdir,
- posns[i]->name,
- posns[i]->directory) > 0) {
- first = posns[i]->name;
- firstdir = posns[i]->directory;
- }
- }
- /* No name means we're done */
- if (!first)
- return 0;
-
- pathlen = strlen(first);
- ce_size = cache_entry_size(baselen + pathlen);
-
- src = xcalloc(src_size, sizeof(struct cache_entry *));
-
- subposns = xcalloc(len, sizeof(struct tree_list_entry *));
-
- if (cache_name && !strcmp(cache_name, first)) {
- any_files = 1;
- src[0] = active_cache[*indpos];
- remove_cache_entry_at(*indpos);
- }
-
- for (i = 0; i < len; i++) {
- struct cache_entry *ce;
-
- if (!posns[i] ||
- (posns[i] != &df_conflict_list &&
- strcmp(first, posns[i]->name))) {
- continue;
- }
-
- if (posns[i] == &df_conflict_list) {
- src[i + merge] = &df_conflict_entry;
- continue;
- }
-
- if (posns[i]->directory) {
- struct tree *tree = lookup_tree(posns[i]->sha1);
- any_dirs = 1;
- parse_tree(tree);
- subposns[i] = create_tree_entry_list(tree);
- posns[i] = posns[i]->next;
- src[i + merge] = &df_conflict_entry;
- continue;
- }
-
- if (!merge)
- ce_stage = 0;
- else if (i + 1 < head_idx)
- ce_stage = 1;
- else if (i + 1 > head_idx)
- ce_stage = 3;
- else
- ce_stage = 2;
-
- ce = xcalloc(1, ce_size);
- ce->ce_mode = create_ce_mode(posns[i]->mode);
- ce->ce_flags = create_ce_flags(baselen + pathlen,
- ce_stage);
- memcpy(ce->name, base, baselen);
- memcpy(ce->name + baselen, first, pathlen + 1);
-
- any_files = 1;
-
- memcpy(ce->sha1, posns[i]->sha1, 20);
- src[i + merge] = ce;
- subposns[i] = &df_conflict_list;
- posns[i] = posns[i]->next;
- }
- if (any_files) {
- if (merge) {
- int ret;
-
-#if DBRT_DEBUG > 1
- printf("%s:\n", first);
- for (i = 0; i < src_size; i++) {
- printf(" %d ", i);
- if (src[i])
- printf("%s\n", sha1_to_hex(src[i]->sha1));
- else
- printf("\n");
- }
-#endif
- ret = fn(src);
-
-#if DBRT_DEBUG > 1
- printf("Added %d entries\n", ret);
-#endif
- *indpos += ret;
- } else {
- for (i = 0; i < src_size; i++) {
- if (src[i]) {
- add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
- }
- }
- }
- }
- if (any_dirs) {
- char *newbase = xmalloc(baselen + 2 + pathlen);
- memcpy(newbase, base, baselen);
- memcpy(newbase + baselen, first, pathlen);
- newbase[baselen + pathlen] = '/';
- newbase[baselen + pathlen + 1] = '\0';
- if (unpack_trees_rec(subposns, len, newbase, fn,
- indpos))
- return -1;
- free(newbase);
- }
- free(subposns);
- free(src);
- } while (1);
-}
-
-static void reject_merge(struct cache_entry *ce)
-{
- die("Entry '%s' would be overwritten by merge. Cannot merge.",
- ce->name);
-}
-
-/* Unlink the last component and attempt to remove leading
- * directories, in case this unlink is the removal of the
- * last entry in the directory -- empty directories are removed.
- */
-static void unlink_entry(char *name)
-{
- char *cp, *prev;
-
- if (unlink(name))
- return;
- prev = NULL;
- while (1) {
- int status;
- cp = strrchr(name, '/');
- if (prev)
- *prev = '/';
- if (!cp)
- break;
-
- *cp = 0;
- status = rmdir(name);
- if (status) {
- *cp = '/';
- break;
- }
- prev = cp;
- }
-}
-
-static void progress_interval(int signum)
-{
- progress_update = 1;
-}
-
-static void setup_progress_signal(void)
-{
- struct sigaction sa;
- struct itimerval v;
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = progress_interval;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- sigaction(SIGALRM, &sa, NULL);
-
- v.it_interval.tv_sec = 1;
- v.it_interval.tv_usec = 0;
- v.it_value = v.it_interval;
- setitimer(ITIMER_REAL, &v, NULL);
-}
-
-static struct checkout state;
-static void check_updates(struct cache_entry **src, int nr)
-{
- unsigned short mask = htons(CE_UPDATE);
- unsigned last_percent = 200, cnt = 0, total = 0;
-
- if (update && verbose_update) {
- for (total = cnt = 0; cnt < nr; cnt++) {
- struct cache_entry *ce = src[cnt];
- if (!ce->ce_mode || ce->ce_flags & mask)
- total++;
- }
-
- /* Don't bother doing this for very small updates */
- if (total < 250)
- total = 0;
-
- if (total) {
- fprintf(stderr, "Checking files out...\n");
- setup_progress_signal();
- progress_update = 1;
- }
- cnt = 0;
- }
-
- while (nr--) {
- struct cache_entry *ce = *src++;
-
- if (total) {
- if (!ce->ce_mode || ce->ce_flags & mask) {
- unsigned percent;
- cnt++;
- percent = (cnt * 100) / total;
- if (percent != last_percent ||
- progress_update) {
- fprintf(stderr, "%4u%% (%u/%u) done\r",
- percent, cnt, total);
- last_percent = percent;
- progress_update = 0;
- }
- }
- }
- if (!ce->ce_mode) {
- if (update)
- unlink_entry(ce->name);
- continue;
- }
- if (ce->ce_flags & mask) {
- ce->ce_flags &= ~mask;
- if (update)
- checkout_entry(ce, &state, NULL);
- }
- }
- if (total) {
- signal(SIGALRM, SIG_IGN);
- fputc('\n', stderr);
- }
-}
-
-static int unpack_trees(merge_fn_t fn)
-{
- int indpos = 0;
- unsigned len = object_list_length(trees);
- struct tree_entry_list **posns;
- int i;
- struct object_list *posn = trees;
- merge_size = len;
-
- if (len) {
- posns = xmalloc(len * sizeof(struct tree_entry_list *));
- for (i = 0; i < len; i++) {
- posns[i] = create_tree_entry_list((struct tree *) posn->item);
- posn = posn->next;
- }
- if (unpack_trees_rec(posns, len, prefix ? prefix : "",
- fn, &indpos))
- return -1;
- }
-
- if (trivial_merges_only && nontrivial_merge)
- die("Merge requires file-level merging");
-
- check_updates(active_cache, active_nr);
- return 0;
-}
-
static int list_tree(unsigned char *sha1)
{
struct tree *tree = parse_tree_indirect(sha1);
return 0;
}
-static int same(struct cache_entry *a, struct cache_entry *b)
-{
- if (!!a != !!b)
- return 0;
- if (!a && !b)
- return 1;
- return a->ce_mode == b->ce_mode &&
- !memcmp(a->sha1, b->sha1, 20);
-}
-
-
-/*
- * When a CE gets turned into an unmerged entry, we
- * want it to be up-to-date
- */
-static void verify_uptodate(struct cache_entry *ce)
-{
- struct stat st;
-
- if (index_only || reset)
- return;
-
- if (!lstat(ce->name, &st)) {
- unsigned changed = ce_match_stat(ce, &st, 1);
- if (!changed)
- return;
- errno = 0;
- }
- if (reset) {
- ce->ce_flags |= htons(CE_UPDATE);
- return;
- }
- if (errno == ENOENT)
- return;
- die("Entry '%s' not uptodate. Cannot merge.", ce->name);
-}
-
-static void invalidate_ce_path(struct cache_entry *ce)
-{
- if (ce)
- cache_tree_invalidate_path(active_cache_tree, ce->name);
-}
-
-/*
- * We do not want to remove or overwrite a working tree file that
- * is not tracked.
- */
-static void verify_absent(const char *path, const char *action)
-{
- struct stat st;
-
- if (index_only || reset || !update)
- return;
- if (!lstat(path, &st))
- die("Untracked working tree file '%s' "
- "would be %s by merge.", path, action);
-}
-
-static int merged_entry(struct cache_entry *merge, struct cache_entry *old)
-{
- merge->ce_flags |= htons(CE_UPDATE);
- if (old) {
- /*
- * See if we can re-use the old CE directly?
- * That way we get the uptodate stat info.
- *
- * This also removes the UPDATE flag on
- * a match.
- */
- if (same(old, merge)) {
- *merge = *old;
- } else {
- verify_uptodate(old);
- invalidate_ce_path(old);
- }
- }
- else {
- verify_absent(merge->name, "overwritten");
- invalidate_ce_path(merge);
- }
-
- merge->ce_flags &= ~htons(CE_STAGEMASK);
- add_cache_entry(merge, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
- return 1;
-}
-
-static int deleted_entry(struct cache_entry *ce, struct cache_entry *old)
-{
- if (old)
- verify_uptodate(old);
- else
- verify_absent(ce->name, "removed");
- ce->ce_mode = 0;
- add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
- invalidate_ce_path(ce);
- return 1;
-}
-
-static int keep_entry(struct cache_entry *ce)
-{
- add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
- return 1;
-}
-
-#if DBRT_DEBUG
-static void show_stage_entry(FILE *o,
- const char *label, const struct cache_entry *ce)
-{
- if (!ce)
- fprintf(o, "%s (missing)\n", label);
- else
- fprintf(o, "%s%06o %s %d\t%s\n",
- label,
- ntohl(ce->ce_mode),
- sha1_to_hex(ce->sha1),
- ce_stage(ce),
- ce->name);
-}
-#endif
-
-static int threeway_merge(struct cache_entry **stages)
-{
- struct cache_entry *index;
- struct cache_entry *head;
- struct cache_entry *remote = stages[head_idx + 1];
- int count;
- int head_match = 0;
- int remote_match = 0;
- const char *path = NULL;
-
- int df_conflict_head = 0;
- int df_conflict_remote = 0;
-
- int any_anc_missing = 0;
- int no_anc_exists = 1;
- int i;
-
- for (i = 1; i < head_idx; i++) {
- if (!stages[i])
- any_anc_missing = 1;
- else {
- if (!path)
- path = stages[i]->name;
- no_anc_exists = 0;
- }
- }
-
- index = stages[0];
- head = stages[head_idx];
-
- if (head == &df_conflict_entry) {
- df_conflict_head = 1;
- head = NULL;
- }
-
- if (remote == &df_conflict_entry) {
- df_conflict_remote = 1;
- remote = NULL;
- }
-
- if (!path && index)
- path = index->name;
- if (!path && head)
- path = head->name;
- if (!path && remote)
- path = remote->name;
-
- /* First, if there's a #16 situation, note that to prevent #13
- * and #14.
- */
- if (!same(remote, head)) {
- for (i = 1; i < head_idx; i++) {
- if (same(stages[i], head)) {
- head_match = i;
- }
- if (same(stages[i], remote)) {
- remote_match = i;
- }
- }
- }
-
- /* We start with cases where the index is allowed to match
- * something other than the head: #14(ALT) and #2ALT, where it
- * is permitted to match the result instead.
- */
- /* #14, #14ALT, #2ALT */
- if (remote && !df_conflict_head && head_match && !remote_match) {
- if (index && !same(index, remote) && !same(index, head))
- reject_merge(index);
- return merged_entry(remote, index);
- }
- /*
- * If we have an entry in the index cache, then we want to
- * make sure that it matches head.
- */
- if (index && !same(index, head)) {
- reject_merge(index);
- }
-
- if (head) {
- /* #5ALT, #15 */
- if (same(head, remote))
- return merged_entry(head, index);
- /* #13, #3ALT */
- if (!df_conflict_remote && remote_match && !head_match)
- return merged_entry(head, index);
- }
-
- /* #1 */
- if (!head && !remote && any_anc_missing)
- return 0;
-
- /* Under the new "aggressive" rule, we resolve mostly trivial
- * cases that we historically had git-merge-one-file resolve.
- */
- if (aggressive) {
- int head_deleted = !head && !df_conflict_head;
- int remote_deleted = !remote && !df_conflict_remote;
- /*
- * Deleted in both.
- * Deleted in one and unchanged in the other.
- */
- if ((head_deleted && remote_deleted) ||
- (head_deleted && remote && remote_match) ||
- (remote_deleted && head && head_match)) {
- if (index)
- return deleted_entry(index, index);
- else if (path)
- verify_absent(path, "removed");
- return 0;
- }
- /*
- * Added in both, identically.
- */
- if (no_anc_exists && head && remote && same(head, remote))
- return merged_entry(head, index);
-
- }
-
- /* Below are "no merge" cases, which require that the index be
- * up-to-date to avoid the files getting overwritten with
- * conflict resolution files.
- */
- if (index) {
- verify_uptodate(index);
- }
- else if (path)
- verify_absent(path, "overwritten");
-
- nontrivial_merge = 1;
-
- /* #2, #3, #4, #6, #7, #9, #11. */
- count = 0;
- if (!head_match || !remote_match) {
- for (i = 1; i < head_idx; i++) {
- if (stages[i]) {
- keep_entry(stages[i]);
- count++;
- break;
- }
- }
- }
-#if DBRT_DEBUG
- else {
- fprintf(stderr, "read-tree: warning #16 detected\n");
- show_stage_entry(stderr, "head ", stages[head_match]);
- show_stage_entry(stderr, "remote ", stages[remote_match]);
- }
-#endif
- if (head) { count += keep_entry(head); }
- if (remote) { count += keep_entry(remote); }
- return count;
-}
-
-/*
- * Two-way merge.
- *
- * The rule is to "carry forward" what is in the index without losing
- * information across a "fast forward", favoring a successful merge
- * over a merge failure when it makes sense. For details of the
- * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
- *
- */
-static int twoway_merge(struct cache_entry **src)
-{
- struct cache_entry *current = src[0];
- struct cache_entry *oldtree = src[1], *newtree = src[2];
-
- if (merge_size != 2)
- return error("Cannot do a twoway merge of %d trees",
- merge_size);
-
- if (current) {
- if ((!oldtree && !newtree) || /* 4 and 5 */
- (!oldtree && newtree &&
- same(current, newtree)) || /* 6 and 7 */
- (oldtree && newtree &&
- same(oldtree, newtree)) || /* 14 and 15 */
- (oldtree && newtree &&
- !same(oldtree, newtree) && /* 18 and 19*/
- same(current, newtree))) {
- return keep_entry(current);
- }
- else if (oldtree && !newtree && same(current, oldtree)) {
- /* 10 or 11 */
- return deleted_entry(oldtree, current);
- }
- else if (oldtree && newtree &&
- same(current, oldtree) && !same(current, newtree)) {
- /* 20 or 21 */
- return merged_entry(newtree, current);
- }
- else {
- /* all other failures */
- if (oldtree)
- reject_merge(oldtree);
- if (current)
- reject_merge(current);
- if (newtree)
- reject_merge(newtree);
- return -1;
- }
- }
- else if (newtree)
- return merged_entry(newtree, current);
- else
- return deleted_entry(oldtree, current);
-}
-
-/*
- * Bind merge.
- *
- * Keep the index entries at stage0, collapse stage1 but make sure
- * stage0 does not have anything there.
- */
-static int bind_merge(struct cache_entry **src)
-{
- struct cache_entry *old = src[0];
- struct cache_entry *a = src[1];
-
- if (merge_size != 1)
- return error("Cannot do a bind merge of %d trees\n",
- merge_size);
- if (a && old)
- die("Entry '%s' overlaps. Cannot bind.", a->name);
- if (!a)
- return keep_entry(old);
- else
- return merged_entry(a, NULL);
-}
-
-/*
- * One-way merge.
- *
- * The rule is:
- * - take the stat information from stage0, take the data from stage1
- */
-static int oneway_merge(struct cache_entry **src)
-{
- struct cache_entry *old = src[0];
- struct cache_entry *a = src[1];
-
- if (merge_size != 1)
- return error("Cannot do a oneway merge of %d trees",
- merge_size);
-
- if (!a)
- return deleted_entry(old, old);
- if (old && same(old, a)) {
- if (reset) {
- struct stat st;
- if (lstat(old->name, &st) ||
- ce_match_stat(old, &st, 1))
- old->ce_flags |= htons(CE_UPDATE);
- }
- return keep_entry(old);
- }
- return merged_entry(a, old);
-}
-
static int read_cache_unmerged(void)
{
int i;
if (ce_stage(ce)) {
if (last && !strcmp(ce->name, last->name))
continue;
- invalidate_ce_path(ce);
+ cache_tree_invalidate_path(active_cache_tree, ce->name);
last = ce;
ce->ce_mode = 0;
ce->ce_flags &= ~htons(CE_STAGEMASK);
static struct lock_file lock_file;
- int cmd_read_tree(int argc, const char **argv, const char *prefix)
+ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
{
int i, newfd, stage = 0;
unsigned char sha1[20];
- merge_fn_t fn = NULL;
+ struct unpack_trees_options opts;
- df_conflict_list.next = &df_conflict_list;
- state.base_dir = "";
- state.force = 1;
- state.quiet = 1;
- state.refresh_cache = 1;
+ memset(&opts, 0, sizeof(opts));
+ opts.head_idx = -1;
+ setup_git_directory();
git_config(git_default_config);
- newfd = hold_lock_file_for_update(&lock_file, get_index_file());
- if (newfd < 0)
- die("unable to create new index file");
+ newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
git_config(git_default_config);
- merge = 0;
- reset = 0;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
* the working tree.
*/
if (!strcmp(arg, "-u")) {
- update = 1;
+ opts.update = 1;
continue;
}
if (!strcmp(arg, "-v")) {
- verbose_update = 1;
+ opts.verbose_update = 1;
continue;
}
* not even look at the working tree.
*/
if (!strcmp(arg, "-i")) {
- index_only = 1;
+ opts.index_only = 1;
continue;
}
* given subdirectory.
*/
if (!strncmp(arg, "--prefix=", 9)) {
- if (stage || merge || prefix)
+ if (stage || opts.merge || opts.prefix)
usage(read_tree_usage);
- prefix = arg + 9;
- merge = 1;
+ opts.prefix = arg + 9;
+ opts.merge = 1;
stage = 1;
if (read_cache_unmerged())
die("you need to resolve your current index first");
* correspond to them.
*/
if (!strcmp(arg, "--reset")) {
- if (stage || merge || prefix)
+ if (stage || opts.merge || opts.prefix)
usage(read_tree_usage);
- reset = 1;
- merge = 1;
+ opts.reset = 1;
+ opts.merge = 1;
stage = 1;
read_cache_unmerged();
continue;
}
if (!strcmp(arg, "--trivial")) {
- trivial_merges_only = 1;
+ opts.trivial_merges_only = 1;
continue;
}
if (!strcmp(arg, "--aggressive")) {
- aggressive = 1;
+ opts.aggressive = 1;
continue;
}
/* "-m" stands for "merge", meaning we start in stage 1 */
if (!strcmp(arg, "-m")) {
- if (stage || merge || prefix)
+ if (stage || opts.merge || opts.prefix)
usage(read_tree_usage);
if (read_cache_unmerged())
die("you need to resolve your current index first");
stage = 1;
- merge = 1;
+ opts.merge = 1;
continue;
}
/* using -u and -i at the same time makes no sense */
- if (1 < index_only + update)
+ if (1 < opts.index_only + opts.update)
usage(read_tree_usage);
if (get_sha1(arg, sha1))
die("failed to unpack tree object %s", arg);
stage++;
}
- if ((update||index_only) && !merge)
+ if ((opts.update||opts.index_only) && !opts.merge)
usage(read_tree_usage);
- if (prefix) {
- int pfxlen = strlen(prefix);
+ if (opts.prefix) {
+ int pfxlen = strlen(opts.prefix);
int pos;
- if (prefix[pfxlen-1] != '/')
+ if (opts.prefix[pfxlen-1] != '/')
die("prefix must end with /");
if (stage != 2)
die("binding merge takes only one tree");
- pos = cache_name_pos(prefix, pfxlen);
+ pos = cache_name_pos(opts.prefix, pfxlen);
if (0 <= pos)
die("corrupt index file");
pos = -pos-1;
if (pos < active_nr &&
- !strncmp(active_cache[pos]->name, prefix, pfxlen))
- die("subdirectory '%s' already exists.", prefix);
- pos = cache_name_pos(prefix, pfxlen-1);
+ !strncmp(active_cache[pos]->name, opts.prefix, pfxlen))
+ die("subdirectory '%s' already exists.", opts.prefix);
+ pos = cache_name_pos(opts.prefix, pfxlen-1);
if (0 <= pos)
- die("file '%.*s' already exists.", pfxlen-1, prefix);
+ die("file '%.*s' already exists.",
+ pfxlen-1, opts.prefix);
}
- if (merge) {
+ if (opts.merge) {
if (stage < 2)
die("just how do you expect me to merge %d trees?", stage-1);
switch (stage - 1) {
case 1:
- fn = prefix ? bind_merge : oneway_merge;
+ opts.fn = opts.prefix ? bind_merge : oneway_merge;
break;
case 2:
- fn = twoway_merge;
+ opts.fn = twoway_merge;
break;
case 3:
default:
- fn = threeway_merge;
+ opts.fn = threeway_merge;
cache_tree_free(&active_cache_tree);
break;
}
if (stage - 1 >= 3)
- head_idx = stage - 2;
+ opts.head_idx = stage - 2;
else
- head_idx = 1;
+ opts.head_idx = 1;
}
- unpack_trees(fn);
+ unpack_trees(trees, &opts);
/*
* When reading only one tree (either the most basic form,
* valid cache-tree because the index must match exactly
* what came from the tree.
*/
- if (trees && trees->item && !prefix && (!merge || (stage == 2))) {
+ if (trees && trees->item && !opts.prefix && (!opts.merge || (stage == 2))) {
cache_tree_free(&active_cache_tree);
prime_cache_tree();
}
--- /dev/null
- int fd = hold_lock_file_for_update(lock, current_index_file);
- if (fd < 0)
- die("could not lock %s", lock->filename);
+/*
+ * Recursive Merge algorithm stolen from git-merge-recursive.py by
+ * Fredrik Kuivinen.
+ * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
+ */
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include "cache.h"
+#include "cache-tree.h"
+#include "commit.h"
+#include "blob.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "run-command.h"
+#include "tag.h"
+#include "unpack-trees.h"
+#include "path-list.h"
+
+/*
+ * A virtual commit has
+ * - (const char *)commit->util set to the name, and
+ * - *(int *)commit->object.sha1 set to the virtual id.
+ */
+
+static unsigned commit_list_count(const struct commit_list *l)
+{
+ unsigned c = 0;
+ for (; l; l = l->next )
+ c++;
+ return c;
+}
+
+static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
+{
+ struct commit *commit = xcalloc(1, sizeof(struct commit));
+ static unsigned virtual_id = 1;
+ commit->tree = tree;
+ commit->util = (void*)comment;
+ *(int*)commit->object.sha1 = virtual_id++;
+ /* avoid warnings */
+ commit->object.parsed = 1;
+ return commit;
+}
+
+/*
+ * Since we use get_tree_entry(), which does not put the read object into
+ * the object pool, we cannot rely on a == b.
+ */
+static int sha_eq(const unsigned char *a, const unsigned char *b)
+{
+ if (!a && !b)
+ return 2;
+ return a && b && memcmp(a, b, 20) == 0;
+}
+
+/*
+ * Since we want to write the index eventually, we cannot reuse the index
+ * for these (temporary) data.
+ */
+struct stage_data
+{
+ struct
+ {
+ unsigned mode;
+ unsigned char sha[20];
+ } stages[4];
+ unsigned processed:1;
+};
+
+static struct path_list current_file_set = {NULL, 0, 0, 1};
+static struct path_list current_directory_set = {NULL, 0, 0, 1};
+
+static int output_indent = 0;
+
+static void output(const char *fmt, ...)
+{
+ va_list args;
+ int i;
+ for (i = output_indent; i--;)
+ fputs(" ", stdout);
+ va_start(args, fmt);
+ vfprintf(stdout, fmt, args);
+ va_end(args);
+ fputc('\n', stdout);
+}
+
+static void output_commit_title(struct commit *commit)
+{
+ int i;
+ for (i = output_indent; i--;)
+ fputs(" ", stdout);
+ if (commit->util)
+ printf("virtual %s\n", (char *)commit->util);
+ else {
+ printf("%s ", sha1_to_hex(commit->object.sha1));
+ if (parse_commit(commit) != 0)
+ printf("(bad commit)\n");
+ else {
+ const char *s;
+ int len;
+ for (s = commit->buffer; *s; s++)
+ if (*s == '\n' && s[1] == '\n') {
+ s += 2;
+ break;
+ }
+ for (len = 0; s[len] && '\n' != s[len]; len++)
+ ; /* do nothing */
+ printf("%.*s\n", len, s);
+ }
+ }
+}
+
+static const char *current_index_file = NULL;
+static const char *original_index_file;
+static const char *temporary_index_file;
+static int cache_dirty = 0;
+
+static int flush_cache(void)
+{
+ /* flush temporary index */
+ struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
++ int fd = hold_lock_file_for_update(lock, current_index_file, 1);
+ if (write_cache(fd, active_cache, active_nr) ||
+ close(fd) || commit_lock_file(lock))
+ die ("unable to write %s", current_index_file);
+ discard_cache();
+ cache_dirty = 0;
+ return 0;
+}
+
+static void setup_index(int temp)
+{
+ current_index_file = temp ? temporary_index_file: original_index_file;
+ if (cache_dirty) {
+ discard_cache();
+ cache_dirty = 0;
+ }
+ unlink(temporary_index_file);
+ discard_cache();
+}
+
+static struct cache_entry *make_cache_entry(unsigned int mode,
+ const unsigned char *sha1, const char *path, int stage, int refresh)
+{
+ int size, len;
+ struct cache_entry *ce;
+
+ if (!verify_path(path))
+ return NULL;
+
+ len = strlen(path);
+ size = cache_entry_size(len);
+ ce = xcalloc(1, size);
+
+ memcpy(ce->sha1, sha1, 20);
+ memcpy(ce->name, path, len);
+ ce->ce_flags = create_ce_flags(len, stage);
+ ce->ce_mode = create_ce_mode(mode);
+
+ if (refresh)
+ return refresh_cache_entry(ce, 0);
+
+ return ce;
+}
+
+static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
+ const char *path, int stage, int refresh, int options)
+{
+ struct cache_entry *ce;
+ if (!cache_dirty)
+ read_cache_from(current_index_file);
+ cache_dirty++;
+ ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
+ if (!ce)
+ return error("cache_addinfo failed: %s", strerror(cache_errno));
+ return add_cache_entry(ce, options);
+}
+
+/*
+ * This is a global variable which is used in a number of places but
+ * only written to in the 'merge' function.
+ *
+ * index_only == 1 => Don't leave any non-stage 0 entries in the cache and
+ * don't update the working directory.
+ * 0 => Leave unmerged entries in the cache and update
+ * the working directory.
+ */
+static int index_only = 0;
+
+static int git_read_tree(struct tree *tree)
+{
+ int rc;
+ struct object_list *trees = NULL;
+ struct unpack_trees_options opts;
+
+ if (cache_dirty)
+ die("read-tree with dirty cache");
+
+ memset(&opts, 0, sizeof(opts));
+ object_list_append(&tree->object, &trees);
+ rc = unpack_trees(trees, &opts);
+ cache_tree_free(&active_cache_tree);
+
+ if (rc == 0)
+ cache_dirty = 1;
+
+ return rc;
+}
+
+static int git_merge_trees(int index_only,
+ struct tree *common,
+ struct tree *head,
+ struct tree *merge)
+{
+ int rc;
+ struct object_list *trees = NULL;
+ struct unpack_trees_options opts;
+
+ if (!cache_dirty) {
+ read_cache_from(current_index_file);
+ cache_dirty = 1;
+ }
+
+ memset(&opts, 0, sizeof(opts));
+ if (index_only)
+ opts.index_only = 1;
+ else
+ opts.update = 1;
+ opts.merge = 1;
+ opts.head_idx = 2;
+ opts.fn = threeway_merge;
+
+ object_list_append(&common->object, &trees);
+ object_list_append(&head->object, &trees);
+ object_list_append(&merge->object, &trees);
+
+ rc = unpack_trees(trees, &opts);
+ cache_tree_free(&active_cache_tree);
+
+ cache_dirty = 1;
+
+ return rc;
+}
+
+static struct tree *git_write_tree(void)
+{
+ struct tree *result = NULL;
+
+ if (cache_dirty) {
+ unsigned i;
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (ce_stage(ce))
+ return NULL;
+ }
+ } else
+ read_cache_from(current_index_file);
+
+ if (!active_cache_tree)
+ active_cache_tree = cache_tree();
+
+ if (!cache_tree_fully_valid(active_cache_tree) &&
+ cache_tree_update(active_cache_tree,
+ active_cache, active_nr, 0, 0) < 0)
+ die("error building trees");
+
+ result = lookup_tree(active_cache_tree->sha1);
+
+ flush_cache();
+ cache_dirty = 0;
+
+ return result;
+}
+
+static int save_files_dirs(const unsigned char *sha1,
+ const char *base, int baselen, const char *path,
+ unsigned int mode, int stage)
+{
+ int len = strlen(path);
+ char *newpath = malloc(baselen + len + 1);
+ memcpy(newpath, base, baselen);
+ memcpy(newpath + baselen, path, len);
+ newpath[baselen + len] = '\0';
+
+ if (S_ISDIR(mode))
+ path_list_insert(newpath, ¤t_directory_set);
+ else
+ path_list_insert(newpath, ¤t_file_set);
+ free(newpath);
+
+ return READ_TREE_RECURSIVE;
+}
+
+static int get_files_dirs(struct tree *tree)
+{
+ int n;
+ if (read_tree_recursive(tree, "", 0, 0, NULL, save_files_dirs) != 0)
+ return 0;
+ n = current_file_set.nr + current_directory_set.nr;
+ return n;
+}
+
+/*
+ * Returns a index_entry instance which doesn't have to correspond to
+ * a real cache entry in Git's index.
+ */
+static struct stage_data *insert_stage_data(const char *path,
+ struct tree *o, struct tree *a, struct tree *b,
+ struct path_list *entries)
+{
+ struct path_list_item *item;
+ struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
+ get_tree_entry(o->object.sha1, path,
+ e->stages[1].sha, &e->stages[1].mode);
+ get_tree_entry(a->object.sha1, path,
+ e->stages[2].sha, &e->stages[2].mode);
+ get_tree_entry(b->object.sha1, path,
+ e->stages[3].sha, &e->stages[3].mode);
+ item = path_list_insert(path, entries);
+ item->util = e;
+ return e;
+}
+
+/*
+ * Create a dictionary mapping file names to stage_data objects. The
+ * dictionary contains one entry for every path with a non-zero stage entry.
+ */
+static struct path_list *get_unmerged(void)
+{
+ struct path_list *unmerged = xcalloc(1, sizeof(struct path_list));
+ int i;
+
+ unmerged->strdup_paths = 1;
+ if (!cache_dirty) {
+ read_cache_from(current_index_file);
+ cache_dirty++;
+ }
+ for (i = 0; i < active_nr; i++) {
+ struct path_list_item *item;
+ struct stage_data *e;
+ struct cache_entry *ce = active_cache[i];
+ if (!ce_stage(ce))
+ continue;
+
+ item = path_list_lookup(ce->name, unmerged);
+ if (!item) {
+ item = path_list_insert(ce->name, unmerged);
+ item->util = xcalloc(1, sizeof(struct stage_data));
+ }
+ e = item->util;
+ e->stages[ce_stage(ce)].mode = ntohl(ce->ce_mode);
+ memcpy(e->stages[ce_stage(ce)].sha, ce->sha1, 20);
+ }
+
+ return unmerged;
+}
+
+struct rename
+{
+ struct diff_filepair *pair;
+ struct stage_data *src_entry;
+ struct stage_data *dst_entry;
+ unsigned processed:1;
+};
+
+/*
+ * Get information of all renames which occured between 'o_tree' and
+ * 'tree'. We need the three trees in the merge ('o_tree', 'a_tree' and
+ * 'b_tree') to be able to associate the correct cache entries with
+ * the rename information. 'tree' is always equal to either a_tree or b_tree.
+ */
+static struct path_list *get_renames(struct tree *tree,
+ struct tree *o_tree,
+ struct tree *a_tree,
+ struct tree *b_tree,
+ struct path_list *entries)
+{
+ int i;
+ struct path_list *renames;
+ struct diff_options opts;
+
+ renames = xcalloc(1, sizeof(struct path_list));
+ diff_setup(&opts);
+ opts.recursive = 1;
+ opts.detect_rename = DIFF_DETECT_RENAME;
+ opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ if (diff_setup_done(&opts) < 0)
+ die("diff setup failed");
+ diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts);
+ diffcore_std(&opts);
+ for (i = 0; i < diff_queued_diff.nr; ++i) {
+ struct path_list_item *item;
+ struct rename *re;
+ struct diff_filepair *pair = diff_queued_diff.queue[i];
+ if (pair->status != 'R') {
+ diff_free_filepair(pair);
+ continue;
+ }
+ re = xmalloc(sizeof(*re));
+ re->processed = 0;
+ re->pair = pair;
+ item = path_list_lookup(re->pair->one->path, entries);
+ if (!item)
+ re->src_entry = insert_stage_data(re->pair->one->path,
+ o_tree, a_tree, b_tree, entries);
+ else
+ re->src_entry = item->util;
+
+ item = path_list_lookup(re->pair->two->path, entries);
+ if (!item)
+ re->dst_entry = insert_stage_data(re->pair->two->path,
+ o_tree, a_tree, b_tree, entries);
+ else
+ re->dst_entry = item->util;
+ item = path_list_insert(pair->one->path, renames);
+ item->util = re;
+ }
+ opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ diff_queued_diff.nr = 0;
+ diff_flush(&opts);
+ return renames;
+}
+
+int update_stages(const char *path, struct diff_filespec *o,
+ struct diff_filespec *a, struct diff_filespec *b, int clear)
+{
+ int options = ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE;
+ if (clear)
+ if (remove_file_from_cache(path))
+ return -1;
+ if (o)
+ if (add_cacheinfo(o->mode, o->sha1, path, 1, 0, options))
+ return -1;
+ if (a)
+ if (add_cacheinfo(a->mode, a->sha1, path, 2, 0, options))
+ return -1;
+ if (b)
+ if (add_cacheinfo(b->mode, b->sha1, path, 3, 0, options))
+ return -1;
+ return 0;
+}
+
+static int remove_path(const char *name)
+{
+ int ret, len;
+ char *slash, *dirs;
+
+ ret = unlink(name);
+ if (ret)
+ return ret;
+ len = strlen(name);
+ dirs = malloc(len+1);
+ memcpy(dirs, name, len);
+ dirs[len] = '\0';
+ while ((slash = strrchr(name, '/'))) {
+ *slash = '\0';
+ len = slash - name;
+ if (rmdir(name) != 0)
+ break;
+ }
+ free(dirs);
+ return ret;
+}
+
+int remove_file(int clean, const char *path)
+{
+ int update_cache = index_only || clean;
+ int update_working_directory = !index_only;
+
+ if (update_cache) {
+ if (!cache_dirty)
+ read_cache_from(current_index_file);
+ cache_dirty++;
+ if (remove_file_from_cache(path))
+ return -1;
+ }
+ if (update_working_directory)
+ {
+ unlink(path);
+ if (errno != ENOENT || errno != EISDIR)
+ return -1;
+ remove_path(path);
+ }
+ return 0;
+}
+
+static char *unique_path(const char *path, const char *branch)
+{
+ char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
+ int suffix = 0;
+ struct stat st;
+ char *p = newpath + strlen(path);
+ strcpy(newpath, path);
+ *(p++) = '~';
+ strcpy(p, branch);
+ for (; *p; ++p)
+ if ('/' == *p)
+ *p = '_';
+ while (path_list_has_path(¤t_file_set, newpath) ||
+ path_list_has_path(¤t_directory_set, newpath) ||
+ lstat(newpath, &st) == 0)
+ sprintf(p, "_%d", suffix++);
+
+ path_list_insert(newpath, ¤t_file_set);
+ return newpath;
+}
+
+static int mkdir_p(const char *path, unsigned long mode)
+{
+ /* path points to cache entries, so strdup before messing with it */
+ char *buf = strdup(path);
+ int result = safe_create_leading_directories(buf);
+ free(buf);
+ return result;
+}
+
+static void flush_buffer(int fd, const char *buf, unsigned long size)
+{
+ while (size > 0) {
+ long ret = xwrite(fd, buf, size);
+ if (ret < 0) {
+ /* Ignore epipe */
+ if (errno == EPIPE)
+ break;
+ die("merge-recursive: %s", strerror(errno));
+ } else if (!ret) {
+ die("merge-recursive: disk full?");
+ }
+ size -= ret;
+ buf += ret;
+ }
+}
+
+void update_file_flags(const unsigned char *sha,
+ unsigned mode,
+ const char *path,
+ int update_cache,
+ int update_wd)
+{
+ if (index_only)
+ update_wd = 0;
+
+ if (update_wd) {
+ char type[20];
+ void *buf;
+ unsigned long size;
+
+ buf = read_sha1_file(sha, type, &size);
+ if (!buf)
+ die("cannot read object %s '%s'", sha1_to_hex(sha), path);
+ if (strcmp(type, blob_type) != 0)
+ die("blob expected for %s '%s'", sha1_to_hex(sha), path);
+
+ if (S_ISREG(mode)) {
+ int fd;
+ if (mkdir_p(path, 0777))
+ die("failed to create path %s: %s", path, strerror(errno));
+ unlink(path);
+ if (mode & 0100)
+ mode = 0777;
+ else
+ mode = 0666;
+ fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
+ if (fd < 0)
+ die("failed to open %s: %s", path, strerror(errno));
+ flush_buffer(fd, buf, size);
+ close(fd);
+ } else if (S_ISLNK(mode)) {
+ char *lnk = malloc(size + 1);
+ memcpy(lnk, buf, size);
+ lnk[size] = '\0';
+ mkdir_p(path, 0777);
+ unlink(lnk);
+ symlink(lnk, path);
+ } else
+ die("do not know what to do with %06o %s '%s'",
+ mode, sha1_to_hex(sha), path);
+ }
+ if (update_cache)
+ add_cacheinfo(mode, sha, path, 0, update_wd, ADD_CACHE_OK_TO_ADD);
+}
+
+void update_file(int clean,
+ const unsigned char *sha,
+ unsigned mode,
+ const char *path)
+{
+ update_file_flags(sha, mode, path, index_only || clean, !index_only);
+}
+
+/* Low level file merging, update and removal */
+
+struct merge_file_info
+{
+ unsigned char sha[20];
+ unsigned mode;
+ unsigned clean:1,
+ merge:1;
+};
+
+static char *git_unpack_file(const unsigned char *sha1, char *path)
+{
+ void *buf;
+ char type[20];
+ unsigned long size;
+ int fd;
+
+ buf = read_sha1_file(sha1, type, &size);
+ if (!buf || strcmp(type, blob_type))
+ die("unable to read blob object %s", sha1_to_hex(sha1));
+
+ strcpy(path, ".merge_file_XXXXXX");
+ fd = mkstemp(path);
+ if (fd < 0)
+ die("unable to create temp-file");
+ flush_buffer(fd, buf, size);
+ close(fd);
+ return path;
+}
+
+static struct merge_file_info merge_file(struct diff_filespec *o,
+ struct diff_filespec *a, struct diff_filespec *b,
+ const char *branch1, const char *branch2)
+{
+ struct merge_file_info result;
+ result.merge = 0;
+ result.clean = 1;
+
+ if ((S_IFMT & a->mode) != (S_IFMT & b->mode)) {
+ result.clean = 0;
+ if (S_ISREG(a->mode)) {
+ result.mode = a->mode;
+ memcpy(result.sha, a->sha1, 20);
+ } else {
+ result.mode = b->mode;
+ memcpy(result.sha, b->sha1, 20);
+ }
+ } else {
+ if (!sha_eq(a->sha1, o->sha1) && !sha_eq(b->sha1, o->sha1))
+ result.merge = 1;
+
+ result.mode = a->mode == o->mode ? b->mode: a->mode;
+
+ if (sha_eq(a->sha1, o->sha1))
+ memcpy(result.sha, b->sha1, 20);
+ else if (sha_eq(b->sha1, o->sha1))
+ memcpy(result.sha, a->sha1, 20);
+ else if (S_ISREG(a->mode)) {
+ int code = 1, fd;
+ struct stat st;
+ char orig[PATH_MAX];
+ char src1[PATH_MAX];
+ char src2[PATH_MAX];
+ const char *argv[] = {
+ "merge", "-L", NULL, "-L", NULL, "-L", NULL,
+ NULL, NULL, NULL,
+ NULL
+ };
+ char *la, *lb, *lo;
+
+ git_unpack_file(o->sha1, orig);
+ git_unpack_file(a->sha1, src1);
+ git_unpack_file(b->sha1, src2);
+
+ argv[2] = la = strdup(mkpath("%s/%s", branch1, a->path));
+ argv[6] = lb = strdup(mkpath("%s/%s", branch2, b->path));
+ argv[4] = lo = strdup(mkpath("orig/%s", o->path));
+ argv[7] = src1;
+ argv[8] = orig;
+ argv[9] = src2,
+
+ code = run_command_v(10, argv);
+
+ free(la);
+ free(lb);
+ free(lo);
+ if (code && code < -256) {
+ die("Failed to execute 'merge'. merge(1) is used as the "
+ "file-level merge tool. Is 'merge' in your path?");
+ }
+ fd = open(src1, O_RDONLY);
+ if (fd < 0 || fstat(fd, &st) < 0 ||
+ index_fd(result.sha, fd, &st, 1,
+ "blob"))
+ die("Unable to add %s to database", src1);
+
+ unlink(orig);
+ unlink(src1);
+ unlink(src2);
+
+ result.clean = WEXITSTATUS(code) == 0;
+ } else {
+ if (!(S_ISLNK(a->mode) || S_ISLNK(b->mode)))
+ die("cannot merge modes?");
+
+ memcpy(result.sha, a->sha1, 20);
+
+ if (!sha_eq(a->sha1, b->sha1))
+ result.clean = 0;
+ }
+ }
+
+ return result;
+}
+
+static void conflict_rename_rename(struct rename *ren1,
+ const char *branch1,
+ struct rename *ren2,
+ const char *branch2)
+{
+ char *del[2];
+ int delp = 0;
+ const char *ren1_dst = ren1->pair->two->path;
+ const char *ren2_dst = ren2->pair->two->path;
+ const char *dst_name1 = ren1_dst;
+ const char *dst_name2 = ren2_dst;
+ if (path_list_has_path(¤t_directory_set, ren1_dst)) {
+ dst_name1 = del[delp++] = unique_path(ren1_dst, branch1);
+ output("%s is a directory in %s adding as %s instead",
+ ren1_dst, branch2, dst_name1);
+ remove_file(0, ren1_dst);
+ }
+ if (path_list_has_path(¤t_directory_set, ren2_dst)) {
+ dst_name2 = del[delp++] = unique_path(ren2_dst, branch2);
+ output("%s is a directory in %s adding as %s instead",
+ ren2_dst, branch1, dst_name2);
+ remove_file(0, ren2_dst);
+ }
+ update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1);
+ update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1);
+ while (delp--)
+ free(del[delp]);
+}
+
+static void conflict_rename_dir(struct rename *ren1,
+ const char *branch1)
+{
+ char *new_path = unique_path(ren1->pair->two->path, branch1);
+ output("Renaming %s to %s instead", ren1->pair->one->path, new_path);
+ remove_file(0, ren1->pair->two->path);
+ update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path);
+ free(new_path);
+}
+
+static void conflict_rename_rename_2(struct rename *ren1,
+ const char *branch1,
+ struct rename *ren2,
+ const char *branch2)
+{
+ char *new_path1 = unique_path(ren1->pair->two->path, branch1);
+ char *new_path2 = unique_path(ren2->pair->two->path, branch2);
+ output("Renaming %s to %s and %s to %s instead",
+ ren1->pair->one->path, new_path1,
+ ren2->pair->one->path, new_path2);
+ remove_file(0, ren1->pair->two->path);
+ update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path1);
+ update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, new_path2);
+ free(new_path2);
+ free(new_path1);
+}
+
+static int process_renames(struct path_list *a_renames,
+ struct path_list *b_renames,
+ const char *a_branch,
+ const char *b_branch)
+{
+ int clean_merge = 1, i, j;
+ struct path_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0};
+ const struct rename *sre;
+
+ for (i = 0; i < a_renames->nr; i++) {
+ sre = a_renames->items[i].util;
+ path_list_insert(sre->pair->two->path, &a_by_dst)->util
+ = sre->dst_entry;
+ }
+ for (i = 0; i < b_renames->nr; i++) {
+ sre = b_renames->items[i].util;
+ path_list_insert(sre->pair->two->path, &b_by_dst)->util
+ = sre->dst_entry;
+ }
+
+ for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
+ int compare;
+ char *src;
+ struct path_list *renames1, *renames2, *renames2Dst;
+ struct rename *ren1 = NULL, *ren2 = NULL;
+ const char *branch1, *branch2;
+ const char *ren1_src, *ren1_dst;
+
+ if (i >= a_renames->nr) {
+ compare = 1;
+ ren2 = b_renames->items[j++].util;
+ } else if (j >= b_renames->nr) {
+ compare = -1;
+ ren1 = a_renames->items[i++].util;
+ } else {
+ compare = strcmp(a_renames->items[i].path,
+ b_renames->items[j].path);
+ if (compare <= 0)
+ ren1 = a_renames->items[i++].util;
+ if (compare >= 0)
+ ren2 = b_renames->items[j++].util;
+ }
+
+ /* TODO: refactor, so that 1/2 are not needed */
+ if (ren1) {
+ renames1 = a_renames;
+ renames2 = b_renames;
+ renames2Dst = &b_by_dst;
+ branch1 = a_branch;
+ branch2 = b_branch;
+ } else {
+ struct rename *tmp;
+ renames1 = b_renames;
+ renames2 = a_renames;
+ renames2Dst = &a_by_dst;
+ branch1 = b_branch;
+ branch2 = a_branch;
+ tmp = ren2;
+ ren2 = ren1;
+ ren1 = tmp;
+ }
+ src = ren1->pair->one->path;
+
+ ren1->dst_entry->processed = 1;
+ ren1->src_entry->processed = 1;
+
+ if (ren1->processed)
+ continue;
+ ren1->processed = 1;
+
+ ren1_src = ren1->pair->one->path;
+ ren1_dst = ren1->pair->two->path;
+
+ if (ren2) {
+ const char *ren2_src = ren2->pair->one->path;
+ const char *ren2_dst = ren2->pair->two->path;
+ /* Renamed in 1 and renamed in 2 */
+ if (strcmp(ren1_src, ren2_src) != 0)
+ die("ren1.src != ren2.src");
+ ren2->dst_entry->processed = 1;
+ ren2->processed = 1;
+ if (strcmp(ren1_dst, ren2_dst) != 0) {
+ clean_merge = 0;
+ output("CONFLICT (rename/rename): "
+ "Rename %s->%s in branch %s "
+ "rename %s->%s in %s",
+ src, ren1_dst, branch1,
+ src, ren2_dst, branch2);
+ conflict_rename_rename(ren1, branch1, ren2, branch2);
+ } else {
+ struct merge_file_info mfi;
+ remove_file(1, ren1_src);
+ mfi = merge_file(ren1->pair->one,
+ ren1->pair->two,
+ ren2->pair->two,
+ branch1,
+ branch2);
+ if (mfi.merge || !mfi.clean)
+ output("Renaming %s->%s", src, ren1_dst);
+
+ if (mfi.merge)
+ output("Auto-merging %s", ren1_dst);
+
+ if (!mfi.clean) {
+ output("CONFLICT (content): merge conflict in %s",
+ ren1_dst);
+ clean_merge = 0;
+
+ if (!index_only)
+ update_stages(ren1_dst,
+ ren1->pair->one,
+ ren1->pair->two,
+ ren2->pair->two,
+ 1 /* clear */);
+ }
+ update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+ }
+ } else {
+ /* Renamed in 1, maybe changed in 2 */
+ struct path_list_item *item;
+ /* we only use sha1 and mode of these */
+ struct diff_filespec src_other, dst_other;
+ int try_merge, stage = a_renames == renames1 ? 3: 2;
+
+ remove_file(1, ren1_src);
+
+ memcpy(src_other.sha1,
+ ren1->src_entry->stages[stage].sha, 20);
+ src_other.mode = ren1->src_entry->stages[stage].mode;
+ memcpy(dst_other.sha1,
+ ren1->dst_entry->stages[stage].sha, 20);
+ dst_other.mode = ren1->dst_entry->stages[stage].mode;
+
+ try_merge = 0;
+
+ if (path_list_has_path(¤t_directory_set, ren1_dst)) {
+ clean_merge = 0;
+ output("CONFLICT (rename/directory): Rename %s->%s in %s "
+ " directory %s added in %s",
+ ren1_src, ren1_dst, branch1,
+ ren1_dst, branch2);
+ conflict_rename_dir(ren1, branch1);
+ } else if (sha_eq(src_other.sha1, null_sha1)) {
+ clean_merge = 0;
+ output("CONFLICT (rename/delete): Rename %s->%s in %s "
+ "and deleted in %s",
+ ren1_src, ren1_dst, branch1,
+ branch2);
+ update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
+ } else if (!sha_eq(dst_other.sha1, null_sha1)) {
+ const char *new_path;
+ clean_merge = 0;
+ try_merge = 1;
+ output("CONFLICT (rename/add): Rename %s->%s in %s. "
+ "%s added in %s",
+ ren1_src, ren1_dst, branch1,
+ ren1_dst, branch2);
+ new_path = unique_path(ren1_dst, branch2);
+ output("Adding as %s instead", new_path);
+ update_file(0, dst_other.sha1, dst_other.mode, new_path);
+ } else if ((item = path_list_lookup(ren1_dst, renames2Dst))) {
+ ren2 = item->util;
+ clean_merge = 0;
+ ren2->processed = 1;
+ output("CONFLICT (rename/rename): Rename %s->%s in %s. "
+ "Rename %s->%s in %s",
+ ren1_src, ren1_dst, branch1,
+ ren2->pair->one->path, ren2->pair->two->path, branch2);
+ conflict_rename_rename_2(ren1, branch1, ren2, branch2);
+ } else
+ try_merge = 1;
+
+ if (try_merge) {
+ struct diff_filespec *o, *a, *b;
+ struct merge_file_info mfi;
+ src_other.path = (char *)ren1_src;
+
+ o = ren1->pair->one;
+ if (a_renames == renames1) {
+ a = ren1->pair->two;
+ b = &src_other;
+ } else {
+ b = ren1->pair->two;
+ a = &src_other;
+ }
+ mfi = merge_file(o, a, b,
+ a_branch, b_branch);
+
+ if (mfi.merge || !mfi.clean)
+ output("Renaming %s => %s", ren1_src, ren1_dst);
+ if (mfi.merge)
+ output("Auto-merging %s", ren1_dst);
+ if (!mfi.clean) {
+ output("CONFLICT (rename/modify): Merge conflict in %s",
+ ren1_dst);
+ clean_merge = 0;
+
+ if (!index_only)
+ update_stages(ren1_dst,
+ o, a, b, 1);
+ }
+ update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);
+ }
+ }
+ }
+ path_list_clear(&a_by_dst, 0);
+ path_list_clear(&b_by_dst, 0);
+
+ if (cache_dirty)
+ flush_cache();
+ return clean_merge;
+}
+
+static unsigned char *has_sha(const unsigned char *sha)
+{
+ return memcmp(sha, null_sha1, 20) == 0 ? NULL: (unsigned char *)sha;
+}
+
+/* Per entry merge function */
+static int process_entry(const char *path, struct stage_data *entry,
+ const char *branch1,
+ const char *branch2)
+{
+ /*
+ printf("processing entry, clean cache: %s\n", index_only ? "yes": "no");
+ print_index_entry("\tpath: ", entry);
+ */
+ int clean_merge = 1;
+ unsigned char *o_sha = has_sha(entry->stages[1].sha);
+ unsigned char *a_sha = has_sha(entry->stages[2].sha);
+ unsigned char *b_sha = has_sha(entry->stages[3].sha);
+ unsigned o_mode = entry->stages[1].mode;
+ unsigned a_mode = entry->stages[2].mode;
+ unsigned b_mode = entry->stages[3].mode;
+
+ if (o_sha && (!a_sha || !b_sha)) {
+ /* Case A: Deleted in one */
+ if ((!a_sha && !b_sha) ||
+ (sha_eq(a_sha, o_sha) && !b_sha) ||
+ (!a_sha && sha_eq(b_sha, o_sha))) {
+ /* Deleted in both or deleted in one and
+ * unchanged in the other */
+ if (a_sha)
+ output("Removing %s", path);
+ remove_file(1, path);
+ } else {
+ /* Deleted in one and changed in the other */
+ clean_merge = 0;
+ if (!a_sha) {
+ output("CONFLICT (delete/modify): %s deleted in %s "
+ "and modified in %s. Version %s of %s left in tree.",
+ path, branch1,
+ branch2, branch2, path);
+ update_file(0, b_sha, b_mode, path);
+ } else {
+ output("CONFLICT (delete/modify): %s deleted in %s "
+ "and modified in %s. Version %s of %s left in tree.",
+ path, branch2,
+ branch1, branch1, path);
+ update_file(0, a_sha, a_mode, path);
+ }
+ }
+
+ } else if ((!o_sha && a_sha && !b_sha) ||
+ (!o_sha && !a_sha && b_sha)) {
+ /* Case B: Added in one. */
+ const char *add_branch;
+ const char *other_branch;
+ unsigned mode;
+ const unsigned char *sha;
+ const char *conf;
+
+ if (a_sha) {
+ add_branch = branch1;
+ other_branch = branch2;
+ mode = a_mode;
+ sha = a_sha;
+ conf = "file/directory";
+ } else {
+ add_branch = branch2;
+ other_branch = branch1;
+ mode = b_mode;
+ sha = b_sha;
+ conf = "directory/file";
+ }
+ if (path_list_has_path(¤t_directory_set, path)) {
+ const char *new_path = unique_path(path, add_branch);
+ clean_merge = 0;
+ output("CONFLICT (%s): There is a directory with name %s in %s. "
+ "Adding %s as %s",
+ conf, path, other_branch, path, new_path);
+ remove_file(0, path);
+ update_file(0, sha, mode, new_path);
+ } else {
+ output("Adding %s", path);
+ update_file(1, sha, mode, path);
+ }
+ } else if (!o_sha && a_sha && b_sha) {
+ /* Case C: Added in both (check for same permissions). */
+ if (sha_eq(a_sha, b_sha)) {
+ if (a_mode != b_mode) {
+ clean_merge = 0;
+ output("CONFLICT: File %s added identically in both branches, "
+ "but permissions conflict %06o->%06o",
+ path, a_mode, b_mode);
+ output("CONFLICT: adding with permission: %06o", a_mode);
+ update_file(0, a_sha, a_mode, path);
+ } else {
+ /* This case is handled by git-read-tree */
+ assert(0 && "This case must be handled by git-read-tree");
+ }
+ } else {
+ const char *new_path1, *new_path2;
+ clean_merge = 0;
+ new_path1 = unique_path(path, branch1);
+ new_path2 = unique_path(path, branch2);
+ output("CONFLICT (add/add): File %s added non-identically "
+ "in both branches. Adding as %s and %s instead.",
+ path, new_path1, new_path2);
+ remove_file(0, path);
+ update_file(0, a_sha, a_mode, new_path1);
+ update_file(0, b_sha, b_mode, new_path2);
+ }
+
+ } else if (o_sha && a_sha && b_sha) {
+ /* case D: Modified in both, but differently. */
+ struct merge_file_info mfi;
+ struct diff_filespec o, a, b;
+
+ output("Auto-merging %s", path);
+ o.path = a.path = b.path = (char *)path;
+ memcpy(o.sha1, o_sha, 20);
+ o.mode = o_mode;
+ memcpy(a.sha1, a_sha, 20);
+ a.mode = a_mode;
+ memcpy(b.sha1, b_sha, 20);
+ b.mode = b_mode;
+
+ mfi = merge_file(&o, &a, &b,
+ branch1, branch2);
+
+ if (mfi.clean)
+ update_file(1, mfi.sha, mfi.mode, path);
+ else {
+ clean_merge = 0;
+ output("CONFLICT (content): Merge conflict in %s", path);
+
+ if (index_only)
+ update_file(0, mfi.sha, mfi.mode, path);
+ else
+ update_file_flags(mfi.sha, mfi.mode, path,
+ 0 /* update_cache */, 1 /* update_working_directory */);
+ }
+ } else
+ die("Fatal merge failure, shouldn't happen.");
+
+ if (cache_dirty)
+ flush_cache();
+
+ return clean_merge;
+}
+
+static int merge_trees(struct tree *head,
+ struct tree *merge,
+ struct tree *common,
+ const char *branch1,
+ const char *branch2,
+ struct tree **result)
+{
+ int code, clean;
+ if (sha_eq(common->object.sha1, merge->object.sha1)) {
+ output("Already uptodate!");
+ *result = head;
+ return 1;
+ }
+
+ code = git_merge_trees(index_only, common, head, merge);
+
+ if (code != 0)
+ die("merging of trees %s and %s failed",
+ sha1_to_hex(head->object.sha1),
+ sha1_to_hex(merge->object.sha1));
+
+ *result = git_write_tree();
+
+ if (!*result) {
+ struct path_list *entries, *re_head, *re_merge;
+ int i;
+ path_list_clear(¤t_file_set, 1);
+ path_list_clear(¤t_directory_set, 1);
+ get_files_dirs(head);
+ get_files_dirs(merge);
+
+ entries = get_unmerged();
+ re_head = get_renames(head, common, head, merge, entries);
+ re_merge = get_renames(merge, common, head, merge, entries);
+ clean = process_renames(re_head, re_merge,
+ branch1, branch2);
+ for (i = 0; i < entries->nr; i++) {
+ const char *path = entries->items[i].path;
+ struct stage_data *e = entries->items[i].util;
+ if (e->processed)
+ continue;
+ if (!process_entry(path, e, branch1, branch2))
+ clean = 0;
+ }
+
+ path_list_clear(re_merge, 0);
+ path_list_clear(re_head, 0);
+ path_list_clear(entries, 1);
+
+ if (clean || index_only)
+ *result = git_write_tree();
+ else
+ *result = NULL;
+ } else {
+ clean = 1;
+ printf("merging of trees %s and %s resulted in %s\n",
+ sha1_to_hex(head->object.sha1),
+ sha1_to_hex(merge->object.sha1),
+ sha1_to_hex((*result)->object.sha1));
+ }
+
+ return clean;
+}
+
+static struct commit_list *reverse_commit_list(struct commit_list *list)
+{
+ struct commit_list *next = NULL, *current, *backup;
+ for (current = list; current; current = backup) {
+ backup = current->next;
+ current->next = next;
+ next = current;
+ }
+ return next;
+}
+
+/*
+ * Merge the commits h1 and h2, return the resulting virtual
+ * commit object and a flag indicating the cleaness of the merge.
+ */
+static
+int merge(struct commit *h1,
+ struct commit *h2,
+ const char *branch1,
+ const char *branch2,
+ int call_depth /* =0 */,
+ struct commit *ancestor /* =None */,
+ struct commit **result)
+{
+ struct commit_list *ca = NULL, *iter;
+ struct commit *merged_common_ancestors;
+ struct tree *mrtree;
+ int clean;
+
+ output("Merging:");
+ output_commit_title(h1);
+ output_commit_title(h2);
+
+ if (ancestor)
+ commit_list_insert(ancestor, &ca);
+ else
+ ca = reverse_commit_list(get_merge_bases(h1, h2, 1));
+
+ output("found %u common ancestor(s):", commit_list_count(ca));
+ for (iter = ca; iter; iter = iter->next)
+ output_commit_title(iter->item);
+
+ merged_common_ancestors = pop_commit(&ca);
+ if (merged_common_ancestors == NULL) {
+ /* if there is no common ancestor, make an empty tree */
+ struct tree *tree = xcalloc(1, sizeof(struct tree));
+ unsigned char hdr[40];
+ int hdrlen;
+
+ tree->object.parsed = 1;
+ tree->object.type = OBJ_TREE;
+ write_sha1_file_prepare(NULL, 0, tree_type, tree->object.sha1,
+ hdr, &hdrlen);
+ merged_common_ancestors = make_virtual_commit(tree, "ancestor");
+ }
+
+ for (iter = ca; iter; iter = iter->next) {
+ output_indent = call_depth + 1;
+ /*
+ * When the merge fails, the result contains files
+ * with conflict markers. The cleanness flag is
+ * ignored, it was never acutally used, as result of
+ * merge_trees has always overwritten it: the commited
+ * "conflicts" were already resolved.
+ */
+ merge(merged_common_ancestors, iter->item,
+ "Temporary merge branch 1",
+ "Temporary merge branch 2",
+ call_depth + 1,
+ NULL,
+ &merged_common_ancestors);
+ output_indent = call_depth;
+
+ if (!merged_common_ancestors)
+ die("merge returned no commit");
+ }
+
+ if (call_depth == 0) {
+ setup_index(0 /* $GIT_DIR/index */);
+ index_only = 0;
+ } else {
+ setup_index(1 /* temporary index */);
+ git_read_tree(h1->tree);
+ index_only = 1;
+ }
+
+ clean = merge_trees(h1->tree, h2->tree, merged_common_ancestors->tree,
+ branch1, branch2, &mrtree);
+
+ if (!ancestor && (clean || index_only)) {
+ *result = make_virtual_commit(mrtree, "merged tree");
+ commit_list_insert(h1, &(*result)->parents);
+ commit_list_insert(h2, &(*result)->parents->next);
+ } else
+ *result = NULL;
+
+ return clean;
+}
+
+static struct commit *get_ref(const char *ref)
+{
+ unsigned char sha1[20];
+ struct object *object;
+
+ if (get_sha1(ref, sha1))
+ die("Could not resolve ref '%s'", ref);
+ object = deref_tag(parse_object(sha1), ref, strlen(ref));
+ if (object->type != OBJ_COMMIT)
+ return NULL;
+ if (parse_commit((struct commit *)object))
+ die("Could not parse commit '%s'", sha1_to_hex(object->sha1));
+ return (struct commit *)object;
+}
+
+int main(int argc, char *argv[])
+{
+ static const char *bases[2];
+ static unsigned bases_count = 0;
+ int i, clean;
+ const char *branch1, *branch2;
+ struct commit *result, *h1, *h2;
+
+ original_index_file = getenv("GIT_INDEX_FILE");
+
+ if (!original_index_file)
+ original_index_file = strdup(git_path("index"));
+
+ temporary_index_file = strdup(git_path("mrg-rcrsv-tmp-idx"));
+
+ if (argc < 4)
+ die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);
+
+ for (i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "--"))
+ break;
+ if (bases_count < sizeof(bases)/sizeof(*bases))
+ bases[bases_count++] = argv[i];
+ }
+ if (argc - i != 3) /* "--" "<head>" "<remote>" */
+ die("Not handling anything other than two heads merge.");
+
+ branch1 = argv[++i];
+ branch2 = argv[++i];
+ printf("Merging %s with %s\n", branch1, branch2);
+
+ h1 = get_ref(branch1);
+ h2 = get_ref(branch2);
+
+ if (bases_count == 1) {
+ struct commit *ancestor = get_ref(bases[0]);
+ clean = merge(h1, h2, branch1, branch2, 0, ancestor, &result);
+ } else
+ clean = merge(h1, h2, branch1, branch2, 0, NULL, &result);
+
+ if (cache_dirty)
+ flush_cache();
+
+ return clean ? 0: 1;
+}
+
+/*
+vim: sw=8 noet
+*/