int update_worktree (struct unpack_trees_options *o, gboolean recover_merge, const char *conflict_head_id, const char *default_conflict_suffix, int *finished_entries) { struct index_state *result = &o->result; int i; struct cache_entry *ce; char *conflict_suffix = NULL; int errs = 0; GHashTable *conflict_hash, *no_conflict_hash; for (i = 0; i < result->cache_nr; ++i) { ce = result->cache[i]; if (ce->ce_flags & CE_WT_REMOVE) errs |= unlink_entry (ce, o); } conflict_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); no_conflict_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); for (i = 0; i < result->cache_nr; ++i) { ce = result->cache[i]; if (ce->ce_flags & CE_UPDATE) { if (conflict_head_id) { conflict_suffix = get_last_changer_of_file (conflict_head_id, ce->name); if (!conflict_suffix) conflict_suffix = g_strdup(default_conflict_suffix); } errs |= checkout_entry (ce, o, recover_merge, conflict_suffix, conflict_hash, no_conflict_hash); g_free (conflict_suffix); } if (finished_entries) *finished_entries = *finished_entries + 1; } g_hash_table_destroy (conflict_hash); g_hash_table_destroy (no_conflict_hash); if (errs != 0) return -1; return 0; }
int update_worktree (struct unpack_trees_options *o, gboolean recover_merge, const char *conflict_head_id, const char *default_conflict_suffix, int *finished_entries) { struct index_state *result = &o->result; int i; struct cache_entry *ce; char *conflict_suffix = NULL; int errs = 0; for (i = 0; i < result->cache_nr; ++i) { ce = result->cache[i]; if (ce->ce_flags & CE_WT_REMOVE) errs |= unlink_entry (ce, o); } for (i = 0; i < result->cache_nr; ++i) { ce = result->cache[i]; if (ce->ce_flags & CE_UPDATE) { if (conflict_head_id) { conflict_suffix = get_last_changer_of_file (conflict_head_id, ce->name); if (!conflict_suffix) conflict_suffix = g_strdup(default_conflict_suffix); } errs |= checkout_entry (ce, o, recover_merge, conflict_suffix); g_free (conflict_suffix); } if (finished_entries) *finished_entries = *finished_entries + 1; } return errs != 0; }
/* * 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; }
static int update_file_flags(struct merge_options *o, const unsigned char *sha, unsigned mode, const char *path, int update_cache, int update_wd) { char *real_path; char file_id[41]; int clean = 1; if (update_wd && o->collect_blocks_only) { fill_seafile_blocks (sha, o->bl); return clean; } real_path = g_build_path(PATH_SEPERATOR, o->worktree, path, NULL); if (update_wd) { char *new_path; SeafStat st; char *conflict_suffix; /* When creating a conflict directory, we use o->branch2 as conflict * suffix instead of the last changer name of path. * This is because there may be more than one conflicting file * under this directory, each has different changer. */ if (make_room_for_path(o->index, path, real_path, &new_path, o->branch2, &clean) < 0) { g_free (real_path); return clean; } g_free (real_path); /* Checkout an empty dir. */ if (S_ISDIR (mode)) { if (g_mkdir (path, 0777) < 0) g_warning ("Failed to create empty dir %s.\n", path); return 0; } /* We're checking out a clean file in recover merge. * Note that file is clean only when it's added by others. */ if (update_cache && o->recover_merge && seaf_stat(new_path, &st) == 0 && S_ISREG(st.st_mode)) { if (compare_file_content (new_path, &st, sha, o->crypt) == 0) { real_path = new_path; goto update_cache; } /* If the file was checked out and changed by user, we * don't need to check out again, since the user should * know the content of this file. */ g_free (new_path); return clean; } conflict_suffix = get_last_changer_of_file (o->remote_head, path); if (!conflict_suffix) conflict_suffix = g_strdup(o->branch2); rawdata_to_hex(sha, file_id, 20); if (seaf_fs_manager_checkout_file(seaf->fs_mgr, file_id, new_path, mode, o->crypt, conflict_suffix) < 0) { g_warning("Failed to checkout file %s.\n", file_id); g_free(new_path); g_free (conflict_suffix); return clean; } g_free (conflict_suffix); /* replace real_path with new_path. */ real_path = new_path; } update_cache: if (update_cache) add_cacheinfo(o->index, mode, sha, path, real_path, 0, update_wd, ADD_CACHE_OK_TO_ADD); g_free(real_path); return clean; }