Beispiel #1
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;
    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;
}
Beispiel #2
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;
}
Beispiel #3
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;
}
Beispiel #4
0
/* 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;
}
Beispiel #5
0
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;
}