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; }
static int checkout_entry (struct cache_entry *ce, struct unpack_trees_options *o, gboolean recover_merge, const char *conflict_suffix) { int base_len = strlen(o->base); int len = ce_namelen(ce); int full_len; char path[PATH_MAX]; int offset; struct stat st; char file_id[41]; if (!len) { g_warning ("entry name should not be empty.\n"); return -1; } snprintf (path, PATH_MAX, "%s/", o->base); /* first create all leading directories. */ full_len = base_len + len + 1; offset = base_len + 1; while (offset < full_len) { do { path[offset] = ce->name[offset-base_len-1]; offset++; } while (offset < full_len && ce->name[offset-base_len-1] != '/'); if (offset >= full_len) break; path[offset] = 0; if (g_lstat (path, &st) == 0 && S_ISDIR(st.st_mode)) continue; if (ccnet_mkdir (path, 0777) < 0) { g_warning ("Failed to create directory %s.\n", path); return -1; } } path[offset] = 0; if (!S_ISDIR(ce->ce_mode)) { /* In case that we're replacing an empty dir with a file, * we need first to remove the empty dir. */ if (g_lstat (path, &st) == 0 && S_ISDIR(st.st_mode)) { if (g_rmdir (path) < 0) { g_warning ("Failed to remove dir %s: %s\n", path, strerror(errno)); /* Don't quit since we can handle conflict later. */ } } } else { /* For simplicity, we just don't checkout the empty dir if there is * already a file with the same name in the worktree. * This implies, you can't remove a file then create an empty directory * with the same name. But it's a rare requirement. */ if (g_mkdir (path, 0777) < 0) { g_warning ("Failed to create empty dir %s.\n", path); } return 0; } if (!o->reset && g_lstat (path, &st) == 0 && S_ISREG(st.st_mode) && (ce->ce_ctime.sec != st.st_ctime || ce->ce_mtime.sec != st.st_mtime)) { /* If we're recovering an interrupted merge, we don't know whether * the file was changed by checkout or by the user. So we have to * calculate the sha1 for that file and compare it with the one in * cache entry. */ if (!recover_merge || compare_file_content (path, &st, ce->sha1, o->crypt) != 0) { g_warning ("File %s is changed. Skip checking out.\n", path); return -1; } /* Recover merge and file content matches index entry. * We were interrupted before updating the index, update index * entry timestamp now. */ goto update_cache; } /* then checkout the file. */ rawdata_to_hex (ce->sha1, file_id, 20); if (seaf_fs_manager_checkout_file (seaf->fs_mgr, file_id, path, ce->ce_mode, o->crypt, conflict_suffix) < 0) { g_warning ("Failed to checkout file %s.\n", path); return -1; } update_cache: /* finally fill cache_entry info */ g_lstat (path, &st); fill_stat_cache_info (ce, &st); return 0; }
static int checkout_entry (struct cache_entry *ce, struct unpack_trees_options *o, gboolean recover_merge, const char *conflict_head_id, GHashTable *conflict_hash, GHashTable *no_conflict_hash) { char *path_in, *path; SeafStat st; char file_id[41]; gboolean case_conflict = FALSE; gboolean force_conflict = FALSE; path_in = g_build_path ("/", o->base, ce->name, NULL); #ifndef __linux__ path = build_case_conflict_free_path (o->base, ce->name, conflict_hash, no_conflict_hash, &case_conflict, FALSE); #else path = build_checkout_path (o->base, ce->name, ce_namelen(ce)); #endif g_free (path_in); if (!path) return -1; if (!S_ISDIR(ce->ce_mode)) { /* In case that we're replacing an empty dir with a file, * we need first to remove the empty dir. */ if (seaf_stat (path, &st) == 0 && S_ISDIR(st.st_mode)) { if (g_rmdir (path) < 0) { g_warning ("Failed to remove dir %s: %s\n", path, strerror(errno)); /* Don't quit since we can handle conflict later. */ } } } else { if (g_mkdir (path, 0777) < 0) { g_warning ("Failed to create empty dir %s in checkout.\n", path); g_free (path); return -1; } if (ce->ce_mtime.sec != 0 && seaf_set_file_time (path, ce->ce_mtime.sec) < 0) { g_warning ("Failed to set mtime for %s.\n", path); } goto update_cache; } if (!o->reset && seaf_stat (path, &st) == 0 && S_ISREG(st.st_mode) && (ce->current_mtime != st.st_mtime)) { /* If we're recovering an interrupted merge, we don't know whether * the file was changed by checkout or by the user. So we have to * calculate the sha1 for that file and compare it with the one in * cache entry. */ if (!recover_merge || compare_file_content (path, &st, ce->sha1, o->crypt, o->version) != 0) { g_warning ("File %s is changed. Checkout to conflict file.\n", path); force_conflict = TRUE; } else { /* Recover merge and file content matches index entry. * We were interrupted before updating the index, update index * entry timestamp now. */ goto update_cache; } } /* then checkout the file. */ gboolean conflicted = FALSE; rawdata_to_hex (ce->sha1, file_id, 20); if (seaf_fs_manager_checkout_file (seaf->fs_mgr, o->repo_id, o->version, file_id, path, ce->ce_mode, ce->ce_mtime.sec, o->crypt, ce->name, conflict_head_id, force_conflict, &conflicted) < 0) { g_warning ("Failed to checkout file %s.\n", path); g_free (path); return -1; } /* If case conflict, this file has been checked out to another path. * Remove the current entry, otherwise it won't be removed later * since it's timestamp is 0. */ if (case_conflict) { ce->ce_flags |= CE_REMOVE; g_free (path); return 0; } if (conflicted) { g_free (path); return 0; } update_cache: /* finally fill cache_entry info */ /* Only update index if we checked out the file without any error * or conflicts. The timestamp of the entry will remain 0 if error * or conflicted. */ seaf_stat (path, &st); fill_stat_cache_info (ce, &st); g_free (path); return 0; }