char * gen_conflict_path_wrapper (const char *repo_id, int version, const char *head, const char *in_repo_path, const char *original_path) { char *modifier; gint64 mtime; /* XXX: this function is only used in client, so store_id is always * the same as repo_id. This can be changed if it's also called in * server. */ if (get_file_modifier_mtime (repo_id, repo_id, version, head, in_repo_path, &modifier, &mtime) < 0) return NULL; return gen_conflict_path (original_path, modifier, mtime); }
/* * Per entry merge function for D/F (and/or rename) conflicts. In the * cases we can cleanly resolve D/F conflicts, process_entry() can * clean out all the files below the directory for us. All D/F * conflict cases must be handled here at the end to make sure any * directories that can be cleaned out, are. */ static int process_df_entry(struct merge_options *o, const char *path, struct stage_data *entry) { int clean_merge = 1; unsigned o_mode = entry->stages[1].mode; unsigned a_mode = entry->stages[2].mode; unsigned b_mode = entry->stages[3].mode; unsigned char *o_sha = o_mode ? entry->stages[1].sha : NULL; unsigned char *a_sha = a_mode ? entry->stages[2].sha : NULL; unsigned char *b_sha = b_mode ? entry->stages[3].sha : NULL; SeafStat st; char *real_path = g_build_path(PATH_SEPERATOR, o->worktree, path, NULL); char *new_path = NULL; char *conflict_suffix = NULL; entry->processed = 1; if (o_sha && (!a_sha || !b_sha)) { clean_merge = 0; /* Modify/delete; deleted side may have put a directory in the way */ if (b_sha) { if (seaf_stat (real_path, &st) == 0 && S_ISDIR(st.st_mode)) { /* D/F conflict. */ /* If b is an empty dir, don't check it out. */ if (S_ISDIR(b_mode)) goto out; if (!o->collect_blocks_only) { conflict_suffix = get_last_changer_of_file (o->remote_head, path); if (!conflict_suffix) conflict_suffix = g_strdup(o->branch2); new_path = gen_conflict_path (path, conflict_suffix); } update_file(o, 0, b_sha, b_mode, new_path); g_free (new_path); g_free (conflict_suffix); } else { /* Modify/Delete conflict. */ update_file(o, 0, b_sha, b_mode, path); } } /* If the conflicting file is mine, it's already in the work tree. * And the conflicting dir should have proper suffix. * So nothing need to be processed here. */ } else if (!o_sha && !!a_sha != !!b_sha) { /* directory -> (directory, file) */ if (b_sha) { if (seaf_stat (real_path, &st) == 0 && S_ISDIR(st.st_mode)) { /* D/F conflict. */ clean_merge = 0; if (S_ISDIR (b_mode)) goto out; if (!o->collect_blocks_only) { conflict_suffix = get_last_changer_of_file (o->remote_head, path); if (!conflict_suffix) conflict_suffix = g_strdup(o->branch2); new_path = gen_conflict_path (path, conflict_suffix); } update_file(o, 0, b_sha, b_mode, new_path); g_free (new_path); g_free (conflict_suffix); } else { /* Clean merge. */ update_file(o, 1, b_sha, b_mode, path); } } } else { entry->processed = 0; g_free(real_path); return 1; /* not handled; assume clean until processed */ } out: g_free(real_path); return clean_merge; }
/* Per entry merge function */ static int process_entry(struct merge_options *o, const char *path, struct stage_data *entry) { /* printf("processing entry, clean cache: %s\n", index_only ? "yes": "no"); print_index_entry("\tpath: ", entry); */ int clean_merge = 1; unsigned o_mode = entry->stages[1].mode; unsigned a_mode = entry->stages[2].mode; unsigned b_mode = entry->stages[3].mode; unsigned char *o_sha = o_mode ? entry->stages[1].sha : NULL; unsigned char *a_sha = a_mode ? entry->stages[2].sha : NULL; unsigned char *b_sha = b_mode ? entry->stages[3].sha : NULL; unsigned int a_ctime = entry->stages[2].ctime; unsigned int a_mtime = entry->stages[2].mtime; /* if (entry->rename_df_conflict_info) */ /* return 1; /\* Such cases are handled elsewhere. *\/ */ entry->processed = 1; if (o_sha && (!a_sha || !b_sha)) { /* Case A: Deleted in one */ if ((!a_sha && !b_sha) || (!b_sha && memcmp(o_sha, a_sha, 20) == 0 && o_mode == a_mode) || (!a_sha && memcmp(o_sha, b_sha, 20) == 0 && o_mode == b_mode)) { /* Deleted in both or deleted in one and * unchanged in the other */ /* do not touch working file if it did not exist */ /* do not remove working file if it's changed. */ remove_file(o, 1, path, !a_sha, a_ctime, a_mtime); } else if (g_hash_table_lookup(o->current_directory_set, path)) { /* file -> (file, directory), the file side. */ entry->processed = 0; return 1; } else { /* Deleted in one and changed in the other */ /* or directory -> (file, directory), directory side */ clean_merge = 0; handle_delete_modify(o, path, path, a_sha, a_mode, b_sha, b_mode); } } else if ((!o_sha && a_sha && !b_sha) || (!o_sha && !a_sha && b_sha)) { /* Case B: Added in one. */ if (g_hash_table_lookup(o->current_directory_set, path)) { /* directory -> (file, directory), file side. */ entry->processed = 0; return 1; } else { /* Added in one */ /* or file -> (file, directory), directory side */ if (b_sha) clean_merge = update_file(o, 1, b_sha, b_mode, path); else /* For my file, just set index entry to stage 0, * without updating worktree. */ update_file_flags (o, a_sha, a_mode, path, 1, 0); } } else if (a_sha && b_sha) { /* Case C: Added in both (check for same permissions) and */ /* case D: Modified in both, but differently. */ if (memcmp(a_sha, b_sha, 20) != 0 || a_mode != b_mode) { char *new_path = NULL; char *conflict_suffix = NULL; clean_merge = 0; if (S_ISDIR (b_mode)) goto out; if (!o->collect_blocks_only) { conflict_suffix = get_last_changer_of_file (o->remote_head, path); if (!conflict_suffix) conflict_suffix = g_strdup(o->branch2); new_path = gen_conflict_path (path, conflict_suffix); } /* Dont update index. */ /* Keep my version, rename other's version. */ update_file_flags(o, b_sha, b_mode, new_path, 0, 1); g_free (new_path); g_free (conflict_suffix); } else { update_file_flags (o, a_sha, b_mode, path, 1, 0); } } else if (!o_sha && !a_sha && !b_sha) { /* * this entry was deleted altogether. a_mode == 0 means * we had that path and want to actively remove it. */ remove_file(o, 1, path, !a_mode, a_ctime, a_mtime); } else g_error("Fatal merge failure, shouldn't happen."); out: return clean_merge; }