mirror of
https://github.com/haiwen/seafile.git
synced 2025-01-09 04:17:30 +08:00
Implemented a new merge algorithm.
This algorithm is simplified and doesn't assume worktree and index. It just merge fs objects and write out merged fs objects. Now for repo operations on seahub, if there are concurrent updates, the two commits will be merged.
This commit is contained in:
parent
95efdb3cd7
commit
44ffce92b9
@ -33,6 +33,7 @@ noinst_HEADERS = \
|
||||
block.h \
|
||||
mq-mgr.h \
|
||||
seaf-db.h \
|
||||
merge-new.h \
|
||||
$(proc_headers)
|
||||
|
||||
#check_PROGRAMS = riak-http-test
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#ifndef SEAFILE_SERVER
|
||||
#include "../daemon/vc-utils.h"
|
||||
#include "vc-common.h"
|
||||
#endif /* SEAFILE_SERVER */
|
||||
|
||||
#include "db.h"
|
||||
@ -849,6 +850,12 @@ seaf_dirent_new (const char *sha1, int mode, const char *name)
|
||||
return dent;
|
||||
}
|
||||
|
||||
SeafDirent *
|
||||
seaf_dirent_dup (SeafDirent *dent)
|
||||
{
|
||||
return g_memdup (dent, sizeof(SeafDirent));
|
||||
}
|
||||
|
||||
BlockList *
|
||||
block_list_new ()
|
||||
{
|
||||
|
@ -72,6 +72,8 @@ seaf_metadata_type_from_data (const uint8_t *data, int len);
|
||||
SeafDirent *
|
||||
seaf_dirent_new (const char *sha1, int mode, const char *name);
|
||||
|
||||
SeafDirent *
|
||||
seaf_dirent_dup (SeafDirent *dent);
|
||||
|
||||
typedef struct {
|
||||
/* TODO: GHashTable may be inefficient when we have large number of IDs. */
|
||||
|
@ -100,6 +100,7 @@ static GDebugKey debug_keys[] = {
|
||||
{ "Sync", SEAFILE_DEBUG_SYNC },
|
||||
{ "Watch", SEAFILE_DEBUG_WATCH },
|
||||
{ "Http", SEAFILE_DEBUG_HTTP },
|
||||
{ "Merge", SEAFILE_DEBUG_MERGE },
|
||||
{ "Other", SEAFILE_DEBUG_OTHER },
|
||||
};
|
||||
|
||||
|
@ -24,7 +24,8 @@ typedef enum
|
||||
SEAFILE_DEBUG_SYNC = 1 << 2,
|
||||
SEAFILE_DEBUG_WATCH = 1 << 3, /* wt-monitor */
|
||||
SEAFILE_DEBUG_HTTP = 1 << 4, /* http server */
|
||||
SEAFILE_DEBUG_OTHER = 1 << 5,
|
||||
SEAFILE_DEBUG_MERGE = 1 << 5,
|
||||
SEAFILE_DEBUG_OTHER = 1 << 6,
|
||||
} SeafileDebugFlags;
|
||||
|
||||
void seafile_debug_impl (SeafileDebugFlags flag, const gchar *format, ...);
|
||||
|
547
common/merge-new.c
Normal file
547
common/merge-new.c
Normal file
@ -0,0 +1,547 @@
|
||||
#include "seafile-session.h"
|
||||
#include "merge-new.h"
|
||||
#include "vc-common.h"
|
||||
|
||||
#define DEBUG_FLAG SEAFILE_DEBUG_MERGE
|
||||
#include "log.h"
|
||||
|
||||
static int
|
||||
merge_trees_recursive (int n, SeafDir *trees[],
|
||||
const char *basedir,
|
||||
MergeOptions *opt);
|
||||
|
||||
static char *
|
||||
merge_conflict_filename (const char *remote_head,
|
||||
const char *basedir,
|
||||
const char *filename)
|
||||
{
|
||||
char *path = NULL, *conflict_suffix = NULL, *conflict_name = NULL;
|
||||
SeafCommit *commit;
|
||||
|
||||
path = g_strconcat (basedir, filename, NULL);
|
||||
|
||||
conflict_suffix = get_last_changer_of_file (remote_head, path);
|
||||
if (!conflict_suffix) {
|
||||
commit = seaf_commit_manager_get_commit (seaf->commit_mgr, remote_head);
|
||||
if (!commit) {
|
||||
seaf_warning ("Failed to find remote head %s.\n", remote_head);
|
||||
goto out;
|
||||
}
|
||||
conflict_suffix = g_strdup(commit->creator_name);
|
||||
seaf_commit_unref (commit);
|
||||
}
|
||||
|
||||
conflict_name = gen_conflict_path (filename, conflict_suffix);
|
||||
|
||||
out:
|
||||
g_free (path);
|
||||
g_free (conflict_suffix);
|
||||
return conflict_name;
|
||||
}
|
||||
|
||||
static char *
|
||||
merge_conflict_dirname (const char *remote_head,
|
||||
const char *basedir,
|
||||
const char *dirname)
|
||||
{
|
||||
char *conflict_suffix = NULL, *conflict_name = NULL;
|
||||
SeafCommit *commit;
|
||||
|
||||
commit = seaf_commit_manager_get_commit (seaf->commit_mgr, remote_head);
|
||||
if (!commit) {
|
||||
seaf_warning ("Failed to find remote head %s.\n", remote_head);
|
||||
goto out;
|
||||
}
|
||||
conflict_suffix = g_strdup(commit->creator_name);
|
||||
seaf_commit_unref (commit);
|
||||
|
||||
conflict_name = gen_conflict_path (dirname, conflict_suffix);
|
||||
|
||||
out:
|
||||
g_free (conflict_suffix);
|
||||
return conflict_name;
|
||||
}
|
||||
|
||||
static int
|
||||
merge_entries (int n, SeafDirent *dents[],
|
||||
const char *basedir,
|
||||
GList **dents_out,
|
||||
MergeOptions *opt)
|
||||
{
|
||||
SeafDirent *files[3];
|
||||
int i;
|
||||
|
||||
memset (files, 0, sizeof(files[0])*n);
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (dents[i] && S_ISREG(dents[i]->mode))
|
||||
files[i] = dents[i];
|
||||
}
|
||||
|
||||
/* If we're running 2-way merge, or the caller requires not to
|
||||
* actually merge contents, just call the callback function.
|
||||
*/
|
||||
if (n == 2 || !opt->do_merge)
|
||||
return opt->callback (basedir, files, opt);
|
||||
|
||||
/* Otherwise, we're doing a real 3-way merge of the trees.
|
||||
* It means merge files and handle any conflicts.
|
||||
*/
|
||||
|
||||
SeafDirent *base, *head, *remote;
|
||||
char *conflict_name;
|
||||
|
||||
base = files[0];
|
||||
head = files[1];
|
||||
remote = files[2];
|
||||
|
||||
if (head && remote) {
|
||||
if (strcmp (head->id, remote->id) == 0) {
|
||||
seaf_debug ("%s%s: files match\n", basedir, head->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
} else if (base && strcmp (base->id, head->id) == 0) {
|
||||
seaf_debug ("%s%s: unchanged in head, changed in remote\n",
|
||||
basedir, head->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
} else if (base && strcmp (base->id, remote->id) == 0) {
|
||||
seaf_debug ("%s%s: unchanged in remote, changed in head\n",
|
||||
basedir, head->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
} else {
|
||||
/* File content conflict. */
|
||||
|
||||
seaf_debug ("%s%s: files conflict\n", basedir, head->name);
|
||||
|
||||
conflict_name = merge_conflict_filename(opt->remote_head,
|
||||
basedir,
|
||||
head->name);
|
||||
if (!conflict_name)
|
||||
return -1;
|
||||
|
||||
/* Change remote entry name in place. So opt->callback
|
||||
* will see the conflict name, not the original name.
|
||||
*/
|
||||
g_strlcpy (remote->name, conflict_name, sizeof(remote->name));
|
||||
remote->name_len = strlen (remote->name);
|
||||
g_free (conflict_name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
}
|
||||
} else if (base && !head && remote) {
|
||||
if (strcmp (base->id, remote->id) != 0) {
|
||||
if (dents[1] != NULL) {
|
||||
/* D/F conflict:
|
||||
* Head replaces file with dir, while remote change the file.
|
||||
*/
|
||||
seaf_debug ("%s%s: DFC, file -> dir, file\n",
|
||||
basedir, remote->name);
|
||||
|
||||
conflict_name = merge_conflict_filename(opt->remote_head,
|
||||
basedir,
|
||||
remote->name);
|
||||
if (!conflict_name)
|
||||
return -1;
|
||||
|
||||
/* Change the name of remote, keep dir name in head unchanged.
|
||||
*/
|
||||
g_strlcpy (remote->name, conflict_name, sizeof(remote->name));
|
||||
remote->name_len = strlen (remote->name);
|
||||
g_free (conflict_name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
} else {
|
||||
/* Deleted in head and changed in remote. */
|
||||
|
||||
seaf_debug ("%s%s: deleted in head and changed in remote\n",
|
||||
basedir, remote->name);
|
||||
|
||||
/* Keep version of remote. */
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
}
|
||||
} else {
|
||||
/* If base and remote match, the file should not be added to
|
||||
* the merge result.
|
||||
*/
|
||||
seaf_debug ("%s%s: file deleted in head, unchanged in remote\n",
|
||||
basedir, remote->name);
|
||||
}
|
||||
} else if (base && head && !remote) {
|
||||
if (strcmp (base->id, head->id) != 0) {
|
||||
if (dents[2] != NULL) {
|
||||
/* D/F conflict:
|
||||
* Remote replaces file with dir, while head change the file.
|
||||
*/
|
||||
seaf_debug ("%s%s: DFC, file -> file, dir\n",
|
||||
basedir, head->name);
|
||||
|
||||
/* We use remote head commit author name as conflict
|
||||
* suffix of a dir.
|
||||
*/
|
||||
conflict_name = merge_conflict_dirname (opt->remote_head,
|
||||
basedir, dents[2]->name);
|
||||
if (!conflict_name)
|
||||
return -1;
|
||||
|
||||
/* Change remote dir name to conflict name in place. */
|
||||
g_strlcpy (dents[2]->name, conflict_name, sizeof(dents[2]->name));
|
||||
dents[2]->name_len = strlen (dents[2]->name);
|
||||
g_free (conflict_name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
} else {
|
||||
/* Deleted in remote and changed in head. */
|
||||
|
||||
seaf_debug ("%s%s: deleted in remote and changed in head\n",
|
||||
basedir, head->name);
|
||||
|
||||
/* Keep version of remote. */
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
}
|
||||
} else {
|
||||
/* If base and head match, the file should not be added to
|
||||
* the merge result.
|
||||
*/
|
||||
seaf_debug ("%s%s: file deleted in remote, unchanged in head\n",
|
||||
basedir, head->name);
|
||||
}
|
||||
} else if (!base && !head && remote) {
|
||||
if (!dents[1]) {
|
||||
/* Added in remote. */
|
||||
seaf_debug ("%s%s: added in remote\n", basedir, remote->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
} else if (dents[0] != NULL && strcmp(dents[0]->id, dents[1]->id) == 0) {
|
||||
/* Contents in the dir is not changed.
|
||||
* The dir will be deleted in merge_directories().
|
||||
*/
|
||||
seaf_debug ("%s%s: dir in head will be replaced by file in remote\n",
|
||||
basedir, remote->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
} else {
|
||||
/* D/F conflict:
|
||||
* Contents of the dir is changed in head, while
|
||||
* remote replace the dir with a file.
|
||||
*
|
||||
* Or, head adds a new dir, while remote adds a new file,
|
||||
* with the same name.
|
||||
*/
|
||||
|
||||
seaf_debug ("%s%s: DFC, dir -> dir, file\n", basedir, remote->name);
|
||||
|
||||
conflict_name = merge_conflict_filename(opt->remote_head,
|
||||
basedir,
|
||||
remote->name);
|
||||
if (!conflict_name)
|
||||
return -1;
|
||||
|
||||
g_strlcpy (remote->name, conflict_name, sizeof(remote->name));
|
||||
remote->name_len = strlen (remote->name);
|
||||
g_free (conflict_name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(remote));
|
||||
}
|
||||
} else if (!base && head && !remote) {
|
||||
if (!dents[2]) {
|
||||
/* Added in remote. */
|
||||
seaf_debug ("%s%s: added in head\n", basedir, head->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
} else if (dents[0] != NULL && strcmp(dents[0]->id, dents[2]->id) == 0) {
|
||||
/* Contents in the dir is not changed.
|
||||
* The dir will be deleted in merge_directories().
|
||||
*/
|
||||
seaf_debug ("%s%s: dir in remote will be replaced by file in head\n",
|
||||
basedir, head->name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
} else {
|
||||
/* D/F conflict:
|
||||
* Contents of the dir is changed in remote, while
|
||||
* head replace the dir with a file.
|
||||
*
|
||||
* Or, remote adds a new dir, while head adds a new file,
|
||||
* with the same name.
|
||||
*/
|
||||
|
||||
seaf_debug ("%s%s: DFC, dir -> file, dir\n", basedir, head->name);
|
||||
|
||||
conflict_name = merge_conflict_dirname (opt->remote_head,
|
||||
basedir, dents[2]->name);
|
||||
if (!conflict_name)
|
||||
return -1;
|
||||
|
||||
g_strlcpy (dents[2]->name, conflict_name, sizeof(dents[2]->name));
|
||||
dents[2]->name_len = strlen (dents[2]->name);
|
||||
g_free (conflict_name);
|
||||
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(head));
|
||||
}
|
||||
} else if (base && !head && !remote) {
|
||||
/* Don't need to add anything to dents_out. */
|
||||
seaf_debug ("%s%s: deleted in head and remote\n", basedir, base->name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
merge_directories (int n, SeafDirent *dents[],
|
||||
const char *basedir,
|
||||
GList **dents_out,
|
||||
MergeOptions *opt)
|
||||
{
|
||||
SeafDir *dir;
|
||||
SeafDir *sub_dirs[3];
|
||||
char *dirname = NULL;
|
||||
char *new_basedir;
|
||||
int ret = 0;
|
||||
int dir_mask = 0, i;
|
||||
SeafDirent *merged_dent;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (dents[i] && S_ISDIR(dents[i]->mode))
|
||||
dir_mask |= 1 << i;
|
||||
}
|
||||
|
||||
seaf_debug ("dir_mask = %d\n", dir_mask);
|
||||
|
||||
if (n == 3 && opt->do_merge) {
|
||||
switch (dir_mask) {
|
||||
case 0:
|
||||
g_assert (0);
|
||||
case 1:
|
||||
/* head and remote are not dirs, nothing to merge. */
|
||||
seaf_debug ("%s%s: no dir, no need to merge\n", basedir, dents[0]->name);
|
||||
return 0;
|
||||
case 2:
|
||||
/* only head is dir, add to result directly, no need to merge. */
|
||||
seaf_debug ("%s%s: only head is dir\n", basedir, dents[1]->name);
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[1]));
|
||||
return 0;
|
||||
case 3:
|
||||
if (strcmp (dents[0]->id, dents[1]->id) == 0) {
|
||||
/* Base and head are the same, but deleted in remote. */
|
||||
seaf_debug ("%s%s: dir deleted in remote\n", basedir, dents[0]->name);
|
||||
return 0;
|
||||
}
|
||||
seaf_debug ("%s%s: dir changed in head but deleted in remote\n",
|
||||
basedir, dents[1]->name);
|
||||
break;
|
||||
case 4:
|
||||
/* only remote is dir, add to result directly, no need to merge. */
|
||||
seaf_debug ("%s%s: only remote is dir\n", basedir, dents[2]->name);
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[2]));
|
||||
return 0;
|
||||
case 5:
|
||||
if (strcmp (dents[0]->id, dents[2]->id) == 0) {
|
||||
/* Base and remote are the same, but deleted in head. */
|
||||
seaf_debug ("%s%s: dir deleted in head\n", basedir, dents[0]->name);
|
||||
return 0;
|
||||
}
|
||||
seaf_debug ("%s%s: dir changed in remote but deleted in head\n",
|
||||
basedir, dents[2]->name);
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
if (strcmp (dents[1]->id, dents[2]->id) == 0) {
|
||||
/* Head and remote match. */
|
||||
seaf_debug ("%s%s: dir is the same in head and remote\n",
|
||||
basedir, dents[1]->name);
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[1]));
|
||||
return 0;
|
||||
} else if (dents[0] && strcmp(dents[0]->id, dents[1]->id) == 0) {
|
||||
seaf_debug ("%s%s: dir changed in remote but unchanged in head\n",
|
||||
basedir, dents[1]->name);
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[2]));
|
||||
return 0;
|
||||
} else if (dents[0] && strcmp(dents[0]->id, dents[2]->id) == 0) {
|
||||
seaf_debug ("%s%s: dir changed in head but unchanged in remote\n",
|
||||
basedir, dents[1]->name);
|
||||
*dents_out = g_list_prepend (*dents_out, seaf_dirent_dup(dents[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
seaf_debug ("%s%s: dir is changed in both head and remote, "
|
||||
"merge recursively\n", basedir, dents[1]->name);
|
||||
break;
|
||||
default:
|
||||
g_assert (0);
|
||||
}
|
||||
}
|
||||
|
||||
memset (sub_dirs, 0, sizeof(sub_dirs[0])*n);
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (dents[i] != NULL && S_ISDIR(dents[i]->mode)) {
|
||||
dir = seaf_fs_manager_get_seafdir (seaf->fs_mgr, dents[i]->id);
|
||||
if (!dir) {
|
||||
seaf_warning ("Failed to find dir %s.\n", dents[i]->id);
|
||||
ret = -1;
|
||||
goto free_sub_dirs;
|
||||
}
|
||||
sub_dirs[i] = dir;
|
||||
|
||||
dirname = dents[i]->name;
|
||||
}
|
||||
}
|
||||
|
||||
new_basedir = g_strconcat (basedir, dirname, "/", NULL);
|
||||
|
||||
ret = merge_trees_recursive (n, sub_dirs, new_basedir, opt);
|
||||
|
||||
g_free (new_basedir);
|
||||
|
||||
if (n == 3 && opt->do_merge) {
|
||||
if (dir_mask == 3 || dir_mask == 6 || dir_mask == 7) {
|
||||
merged_dent = seaf_dirent_dup (dents[1]);
|
||||
memcpy (merged_dent->id, opt->merged_tree_root, 40);
|
||||
*dents_out = g_list_prepend (*dents_out, merged_dent);
|
||||
} else if (dir_mask == 5) {
|
||||
merged_dent = seaf_dirent_dup (dents[2]);
|
||||
memcpy (merged_dent->id, opt->merged_tree_root, 40);
|
||||
*dents_out = g_list_prepend (*dents_out, merged_dent);
|
||||
}
|
||||
}
|
||||
|
||||
free_sub_dirs:
|
||||
for (i = 0; i < n; ++i)
|
||||
seaf_dir_free (sub_dirs[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gint
|
||||
compare_dirents (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const SeafDirent *denta = a, *dentb = b;
|
||||
|
||||
return strcmp (dentb->name, denta->name);
|
||||
}
|
||||
|
||||
static int
|
||||
merge_trees_recursive (int n, SeafDir *trees[],
|
||||
const char *basedir,
|
||||
MergeOptions *opt)
|
||||
{
|
||||
GList *ptrs[3];
|
||||
SeafDirent *dents[3];
|
||||
int i;
|
||||
SeafDirent *dent;
|
||||
char *first_name;
|
||||
gboolean done;
|
||||
int ret = 0;
|
||||
SeafDir *merged_tree;
|
||||
GList *merged_dents = NULL;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (trees[i])
|
||||
ptrs[i] = trees[i]->entries;
|
||||
else
|
||||
ptrs[i] = NULL;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
first_name = NULL;
|
||||
memset (dents, 0, sizeof(dents[0])*n);
|
||||
done = TRUE;
|
||||
|
||||
/* Find the "largest" name, assuming dirents are sorted. */
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (ptrs[i] != NULL) {
|
||||
done = FALSE;
|
||||
dent = ptrs[i]->data;
|
||||
if (!first_name)
|
||||
first_name = dent->name;
|
||||
else if (strcmp(dent->name, first_name) > 0)
|
||||
first_name = dent->name;
|
||||
}
|
||||
}
|
||||
|
||||
if (done)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Setup dir entries for all names that equal to first_name
|
||||
*/
|
||||
int n_files = 0, n_dirs = 0;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (ptrs[i] != NULL) {
|
||||
dent = ptrs[i]->data;
|
||||
if (strcmp(first_name, dent->name) == 0) {
|
||||
if (S_ISREG(dent->mode))
|
||||
++n_files;
|
||||
else if (S_ISDIR(dent->mode))
|
||||
++n_dirs;
|
||||
|
||||
dents[i] = dent;
|
||||
ptrs[i] = ptrs[i]->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Merge entries of this level. */
|
||||
if (n_files > 0) {
|
||||
ret = merge_entries (n, dents, basedir, &merged_dents, opt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Recurse into sub level. */
|
||||
if (n_dirs > 0) {
|
||||
ret = merge_directories (n, dents, basedir, &merged_dents, opt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (n == 3 && opt->do_merge) {
|
||||
merged_dents = g_list_sort (merged_dents, compare_dirents);
|
||||
merged_tree = seaf_dir_new (NULL, merged_dents, 0);
|
||||
|
||||
memcpy (opt->merged_tree_root, merged_tree->dir_id, 40);
|
||||
|
||||
if ((trees[1] && strcmp (trees[1]->dir_id, merged_tree->dir_id) == 0) ||
|
||||
(trees[2] && strcmp (trees[2]->dir_id, merged_tree->dir_id) == 0)) {
|
||||
seaf_dir_free (merged_tree);
|
||||
} else {
|
||||
ret = seaf_dir_save (seaf->fs_mgr, merged_tree);
|
||||
seaf_dir_free (merged_tree);
|
||||
if (ret < 0) {
|
||||
seaf_warning ("Failed to save merged tree %s.\n", basedir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
seaf_merge_trees (int n, const char *roots[], MergeOptions *opt)
|
||||
{
|
||||
SeafDir **trees, *root;
|
||||
int i, ret;
|
||||
|
||||
g_assert (n == 2 || n == 3);
|
||||
|
||||
trees = g_new0 (SeafDir *, n);
|
||||
for (i = 0; i < n; ++i) {
|
||||
root = seaf_fs_manager_get_seafdir (seaf->fs_mgr, roots[i]);
|
||||
if (!root) {
|
||||
seaf_warning ("Failed to find dir %s.\n", roots[i]);
|
||||
g_free (trees);
|
||||
return -1;
|
||||
}
|
||||
trees[i] = root;
|
||||
}
|
||||
|
||||
ret = merge_trees_recursive (n, trees, "", opt);
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
seaf_dir_free (trees[i]);
|
||||
g_free (trees);
|
||||
|
||||
return ret;
|
||||
}
|
30
common/merge-new.h
Normal file
30
common/merge-new.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef MERGE_NEW_H
|
||||
#define MERGE_NEW_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "fs-mgr.h"
|
||||
|
||||
struct MergeOptions;
|
||||
|
||||
typedef int (*MergeCallback) (const char *basedir,
|
||||
SeafDirent *dirents[],
|
||||
struct MergeOptions *opt);
|
||||
|
||||
typedef struct MergeOptions {
|
||||
int n_ways; /* only 2 and 3 way merges are supported. */
|
||||
|
||||
MergeCallback callback;
|
||||
void * data;
|
||||
|
||||
/* options only used in 3-way merge. */
|
||||
char remote_head[41];
|
||||
gboolean do_merge; /* really merge the contents
|
||||
* and handle conflicts */
|
||||
char merged_tree_root[41]; /* merge result */
|
||||
} MergeOptions;
|
||||
|
||||
int
|
||||
seaf_merge_trees (int n, const char *roots[], MergeOptions *opt);
|
||||
|
||||
#endif
|
@ -3,6 +3,9 @@
|
||||
#include "seafile-session.h"
|
||||
#include "vc-common.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "seafile-error.h"
|
||||
|
||||
static GList *
|
||||
merge_bases_many (SeafCommit *one, int n, SeafCommit **twos);
|
||||
|
||||
@ -306,3 +309,197 @@ vc_compare_commits (const char *c1, const char *c2)
|
||||
seaf_commit_unref (commit2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Diff a specific file with parent(s).
|
||||
* If @commit is a merge, both parents will be compared.
|
||||
* @commit must have this file and it's id is given in @file_id.
|
||||
*
|
||||
* Returns 0 if there is no difference; 1 otherwise.
|
||||
* If returns 0, @parent will point to the next commit to traverse.
|
||||
* If I/O error occurs, @error will be set.
|
||||
*/
|
||||
static int
|
||||
diff_parents_with_path (SeafCommit *commit,
|
||||
const char *path,
|
||||
const char *file_id,
|
||||
char *parent,
|
||||
GError **error)
|
||||
{
|
||||
SeafCommit *p1 = NULL, *p2 = NULL;
|
||||
char *file_id_p1 = NULL, *file_id_p2 = NULL;
|
||||
int ret = 0;
|
||||
|
||||
g_assert (commit->parent_id != NULL);
|
||||
|
||||
p1 = seaf_commit_manager_get_commit (seaf->commit_mgr, commit->parent_id);
|
||||
if (!p1) {
|
||||
g_warning ("Failed to find commit %s.\n", commit->parent_id);
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, " ");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp (p1->root_id, EMPTY_SHA1) == 0) {
|
||||
seaf_commit_unref (p1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (commit->second_parent_id) {
|
||||
p2 = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
||||
commit->second_parent_id);
|
||||
if (!p2) {
|
||||
g_warning ("Failed to find commit %s.\n", commit->second_parent_id);
|
||||
seaf_commit_unref (p1);
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, " ");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!p2) {
|
||||
file_id_p1 = seaf_fs_manager_path_to_file_id (seaf->fs_mgr,
|
||||
p1->root_id, path,
|
||||
NULL,
|
||||
error);
|
||||
if (*error)
|
||||
goto out;
|
||||
if (!file_id_p1 || strcmp (file_id, file_id_p1) != 0)
|
||||
ret = 1;
|
||||
else
|
||||
memcpy (parent, p1->commit_id, 41);
|
||||
} else {
|
||||
file_id_p1 = seaf_fs_manager_path_to_file_id (seaf->fs_mgr,
|
||||
p1->root_id, path,
|
||||
NULL, error);
|
||||
if (*error)
|
||||
goto out;
|
||||
file_id_p2 = seaf_fs_manager_path_to_file_id (seaf->fs_mgr,
|
||||
p2->root_id, path,
|
||||
NULL, error);
|
||||
if (*error)
|
||||
goto out;
|
||||
|
||||
if (file_id_p1 && file_id_p2) {
|
||||
if (strcmp(file_id, file_id_p1) != 0 &&
|
||||
strcmp(file_id, file_id_p2) != 0)
|
||||
ret = 1;
|
||||
else if (strcmp(file_id, file_id_p1) == 0)
|
||||
memcpy (parent, p1->commit_id, 41);
|
||||
else
|
||||
memcpy (parent, p2->commit_id, 41);
|
||||
} else if (file_id_p1 && !file_id_p2) {
|
||||
if (strcmp(file_id, file_id_p1) != 0)
|
||||
ret = 1;
|
||||
else
|
||||
memcpy (parent, p1->commit_id, 41);
|
||||
} else if (!file_id_p1 && file_id_p2) {
|
||||
if (strcmp(file_id, file_id_p2) != 0)
|
||||
ret = 1;
|
||||
else
|
||||
memcpy (parent, p2->commit_id, 41);
|
||||
} else {
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
g_free (file_id_p1);
|
||||
g_free (file_id_p2);
|
||||
|
||||
if (p1)
|
||||
seaf_commit_unref (p1);
|
||||
if (p2)
|
||||
seaf_commit_unref (p2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who last changed a file.
|
||||
* @head: head commit to start the search.
|
||||
* @path: path of the file.
|
||||
*/
|
||||
char *
|
||||
get_last_changer_of_file (const char *head, const char *path)
|
||||
{
|
||||
char commit_id[41];
|
||||
SeafCommit *commit = NULL;
|
||||
char *file_id = NULL;
|
||||
int changed;
|
||||
char *ret = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
memcpy (commit_id, head, 41);
|
||||
|
||||
while (1) {
|
||||
commit = seaf_commit_manager_get_commit (seaf->commit_mgr, commit_id);
|
||||
if (!commit)
|
||||
break;
|
||||
|
||||
/* We hit the initial commit. */
|
||||
if (!commit->parent_id)
|
||||
break;
|
||||
|
||||
file_id = seaf_fs_manager_path_to_file_id (seaf->fs_mgr,
|
||||
commit->root_id,
|
||||
path,
|
||||
NULL,
|
||||
&error);
|
||||
if (error) {
|
||||
g_clear_error (&error);
|
||||
break;
|
||||
}
|
||||
/* We expect commit to have this file. */
|
||||
if (!file_id)
|
||||
break;
|
||||
|
||||
changed = diff_parents_with_path (commit, path, file_id,
|
||||
commit_id, &error);
|
||||
if (error) {
|
||||
g_clear_error (&error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
ret = g_strdup (commit->creator_name);
|
||||
break;
|
||||
} else {
|
||||
/* If this commit doesn't change the file, commit_id will be set
|
||||
* to the parent commit to traverse.
|
||||
*/
|
||||
g_free (file_id);
|
||||
seaf_commit_unref (commit);
|
||||
}
|
||||
}
|
||||
|
||||
g_free (file_id);
|
||||
if (commit)
|
||||
seaf_commit_unref (commit);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *
|
||||
gen_conflict_path (const char *origin_path, const char *suffix)
|
||||
{
|
||||
char time_buf[64];
|
||||
time_t t = time(NULL);
|
||||
char *copy = g_strdup (origin_path);
|
||||
GString *conflict_path = g_string_new (NULL);
|
||||
char *dot, *ext;
|
||||
|
||||
strftime(time_buf, 64, "%Y-%m-%d-%H-%M-%S", localtime(&t));
|
||||
|
||||
dot = strrchr (copy, '.');
|
||||
|
||||
if (dot != NULL) {
|
||||
*dot = '\0';
|
||||
ext = dot + 1;
|
||||
g_string_printf (conflict_path, "%s (%s %s).%s",
|
||||
copy, suffix, time_buf, ext);
|
||||
} else {
|
||||
g_string_printf (conflict_path, "%s (%s %s)",
|
||||
copy, suffix, time_buf);
|
||||
}
|
||||
|
||||
g_free (copy);
|
||||
return g_string_free (conflict_path, FALSE);
|
||||
}
|
||||
|
@ -36,4 +36,10 @@ typedef enum {
|
||||
VCCompareResult
|
||||
vc_compare_commits (const char *c1, const char *c2);
|
||||
|
||||
char *
|
||||
gen_conflict_path (const char *origin_path, const char *suffix);
|
||||
|
||||
char *
|
||||
get_last_changer_of_file (const char *head, const char *path);
|
||||
|
||||
#endif
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "unpack-trees.h"
|
||||
#include "merge-recursive.h"
|
||||
#include "vc-utils.h"
|
||||
#include "vc-common.h"
|
||||
#include "utils.h"
|
||||
|
||||
/*
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "fs-mgr.h"
|
||||
#include "merge.h"
|
||||
#include "vc-utils.h"
|
||||
#include "vc-common.h"
|
||||
|
||||
static gint
|
||||
compare_dirents (gconstpointer a, gconstpointer b)
|
||||
@ -362,33 +363,6 @@ update_worktree (struct unpack_trees_options *o,
|
||||
return errs != 0;
|
||||
}
|
||||
|
||||
char *
|
||||
gen_conflict_path (const char *origin_path, const char *suffix)
|
||||
{
|
||||
char time_buf[64];
|
||||
time_t t = time(NULL);
|
||||
char *copy = g_strdup (origin_path);
|
||||
GString *conflict_path = g_string_new (NULL);
|
||||
char *dot, *ext;
|
||||
|
||||
strftime(time_buf, 64, "%Y-%m-%d-%H-%M-%S", localtime(&t));
|
||||
|
||||
dot = strrchr (copy, '.');
|
||||
|
||||
if (dot != NULL) {
|
||||
*dot = '\0';
|
||||
ext = dot + 1;
|
||||
g_string_printf (conflict_path, "%s (%s %s).%s",
|
||||
copy, suffix, time_buf, ext);
|
||||
} else {
|
||||
g_string_printf (conflict_path, "%s (%s %s)",
|
||||
copy, suffix, time_buf);
|
||||
}
|
||||
|
||||
g_free (copy);
|
||||
return g_string_free (conflict_path, FALSE);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
static gboolean
|
||||
@ -469,173 +443,6 @@ files_locked_on_windows (struct index_state *index, const char *worktree)
|
||||
|
||||
#endif /* WIN32 */
|
||||
|
||||
/**
|
||||
* Diff a specific file with parent(s).
|
||||
* If @commit is a merge, both parents will be compared.
|
||||
* @commit must have this file and it's id is given in @file_id.
|
||||
*
|
||||
* Returns 0 if there is no difference; 1 otherwise.
|
||||
* If returns 0, @parent will point to the next commit to traverse.
|
||||
* If I/O error occurs, @error will be set.
|
||||
*/
|
||||
static int
|
||||
diff_parents_with_path (SeafCommit *commit,
|
||||
const char *path,
|
||||
const char *file_id,
|
||||
char *parent,
|
||||
GError **error)
|
||||
{
|
||||
SeafCommit *p1 = NULL, *p2 = NULL;
|
||||
char *file_id_p1 = NULL, *file_id_p2 = NULL;
|
||||
int ret = 0;
|
||||
|
||||
g_assert (commit->parent_id != NULL);
|
||||
|
||||
p1 = seaf_commit_manager_get_commit (seaf->commit_mgr, commit->parent_id);
|
||||
if (!p1) {
|
||||
g_warning ("Failed to find commit %s.\n", commit->parent_id);
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, " ");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp (p1->root_id, EMPTY_SHA1) == 0) {
|
||||
seaf_commit_unref (p1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (commit->second_parent_id) {
|
||||
p2 = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
||||
commit->second_parent_id);
|
||||
if (!p2) {
|
||||
g_warning ("Failed to find commit %s.\n", commit->second_parent_id);
|
||||
seaf_commit_unref (p1);
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, " ");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!p2) {
|
||||
file_id_p1 = seaf_fs_manager_path_to_file_id (seaf->fs_mgr,
|
||||
p1->root_id, path,
|
||||
NULL,
|
||||
error);
|
||||
if (*error)
|
||||
goto out;
|
||||
if (!file_id_p1 || strcmp (file_id, file_id_p1) != 0)
|
||||
ret = 1;
|
||||
else
|
||||
memcpy (parent, p1->commit_id, 41);
|
||||
} else {
|
||||
file_id_p1 = seaf_fs_manager_path_to_file_id (seaf->fs_mgr,
|
||||
p1->root_id, path,
|
||||
NULL, error);
|
||||
if (*error)
|
||||
goto out;
|
||||
file_id_p2 = seaf_fs_manager_path_to_file_id (seaf->fs_mgr,
|
||||
p2->root_id, path,
|
||||
NULL, error);
|
||||
if (*error)
|
||||
goto out;
|
||||
|
||||
if (file_id_p1 && file_id_p2) {
|
||||
if (strcmp(file_id, file_id_p1) != 0 &&
|
||||
strcmp(file_id, file_id_p2) != 0)
|
||||
ret = 1;
|
||||
else if (strcmp(file_id, file_id_p1) == 0)
|
||||
memcpy (parent, p1->commit_id, 41);
|
||||
else
|
||||
memcpy (parent, p2->commit_id, 41);
|
||||
} else if (file_id_p1 && !file_id_p2) {
|
||||
if (strcmp(file_id, file_id_p1) != 0)
|
||||
ret = 1;
|
||||
else
|
||||
memcpy (parent, p1->commit_id, 41);
|
||||
} else if (!file_id_p1 && file_id_p2) {
|
||||
if (strcmp(file_id, file_id_p2) != 0)
|
||||
ret = 1;
|
||||
else
|
||||
memcpy (parent, p2->commit_id, 41);
|
||||
} else {
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
g_free (file_id_p1);
|
||||
g_free (file_id_p2);
|
||||
|
||||
if (p1)
|
||||
seaf_commit_unref (p1);
|
||||
if (p2)
|
||||
seaf_commit_unref (p2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who last changed a file.
|
||||
* @head: head commit to start the search.
|
||||
* @path: path of the file.
|
||||
*/
|
||||
char *
|
||||
get_last_changer_of_file (const char *head, const char *path)
|
||||
{
|
||||
char commit_id[41];
|
||||
SeafCommit *commit = NULL;
|
||||
char *file_id = NULL;
|
||||
int changed;
|
||||
char *ret = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
memcpy (commit_id, head, 41);
|
||||
|
||||
while (1) {
|
||||
commit = seaf_commit_manager_get_commit (seaf->commit_mgr, commit_id);
|
||||
if (!commit)
|
||||
break;
|
||||
|
||||
/* We hit the initial commit. */
|
||||
if (!commit->parent_id)
|
||||
break;
|
||||
|
||||
file_id = seaf_fs_manager_path_to_file_id (seaf->fs_mgr,
|
||||
commit->root_id,
|
||||
path,
|
||||
NULL,
|
||||
&error);
|
||||
if (error) {
|
||||
g_clear_error (&error);
|
||||
break;
|
||||
}
|
||||
/* We expect commit to have this file. */
|
||||
if (!file_id)
|
||||
break;
|
||||
|
||||
changed = diff_parents_with_path (commit, path, file_id,
|
||||
commit_id, &error);
|
||||
if (error) {
|
||||
g_clear_error (&error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
ret = g_strdup (commit->creator_name);
|
||||
break;
|
||||
} else {
|
||||
/* If this commit doesn't change the file, commit_id will be set
|
||||
* to the parent commit to traverse.
|
||||
*/
|
||||
g_free (file_id);
|
||||
seaf_commit_unref (commit);
|
||||
}
|
||||
}
|
||||
|
||||
g_free (file_id);
|
||||
if (commit)
|
||||
seaf_commit_unref (commit);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
fill_seafile_blocks (const unsigned char *sha1, BlockList *bl)
|
||||
{
|
||||
|
@ -33,15 +33,9 @@ update_worktree (struct unpack_trees_options *o,
|
||||
const char *default_conflict_suffix,
|
||||
int *finished_entries);
|
||||
|
||||
char *
|
||||
gen_conflict_path (const char *origin_path, const char *suffix);
|
||||
|
||||
gboolean
|
||||
files_locked_on_windows (struct index_state *index, const char *worktree);
|
||||
|
||||
char *
|
||||
get_last_changer_of_file (const char *head, const char *path);
|
||||
|
||||
int
|
||||
compare_file_content (const char *path, struct stat *st,
|
||||
const unsigned char *ce_sha1,
|
||||
|
@ -76,6 +76,7 @@ seaf_server_SOURCES = \
|
||||
../common/block-backend.c \
|
||||
../common/block-backend-fs.c \
|
||||
../common/block-backend-ceph.c \
|
||||
../common/merge-new.c \
|
||||
processors/recvcommit-proc.c \
|
||||
processors/recvfs-proc.c \
|
||||
processors/recvblock-proc.c \
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "index/cache-tree.h"
|
||||
#include "unpack-trees.h"
|
||||
#include "diff-simple.h"
|
||||
#include "merge-new.h"
|
||||
#include "monitor-rpc-wrappers.h"
|
||||
|
||||
#include "seaf-db.h"
|
||||
@ -2519,24 +2520,128 @@ check_file_exists (const char *root_id,
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define GEN_NEW_COMMIT(repo,root_id,user,buf) \
|
||||
do { \
|
||||
new_commit = seaf_commit_new(NULL, repo->id, root_id, \
|
||||
user, EMPTY_SHA1, \
|
||||
buf, 0); \
|
||||
new_commit->parent_id = g_strdup (repo->head->commit_id); \
|
||||
seaf_repo_to_commit (repo, new_commit); \
|
||||
\
|
||||
if (seaf_commit_manager_add_commit (seaf->commit_mgr, \
|
||||
new_commit) < 0) { \
|
||||
seaf_warning ("Failed to add commit.\n"); \
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, \
|
||||
"Failed to add commit"); \
|
||||
ret = -1; \
|
||||
goto out; \
|
||||
} \
|
||||
seaf_branch_set_commit(repo->head, new_commit->commit_id); \
|
||||
} while (0);
|
||||
static int
|
||||
gen_new_commit (const char *repo_id,
|
||||
SeafCommit *base,
|
||||
const char *new_root,
|
||||
const char *user,
|
||||
const char *desc,
|
||||
GError **error)
|
||||
{
|
||||
SeafRepo *repo = NULL;
|
||||
SeafCommit *new_commit = NULL, *current_head = NULL;
|
||||
int ret = 0;
|
||||
|
||||
repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
|
||||
if (!repo) {
|
||||
seaf_warning ("Repo %s doesn't exist.\n", repo_id);
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Invalid repo");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Create a new commit pointing to new_root. */
|
||||
new_commit = seaf_commit_new(NULL, repo->id, new_root,
|
||||
user, EMPTY_SHA1,
|
||||
desc, 0);
|
||||
new_commit->parent_id = g_strdup (base->commit_id);
|
||||
seaf_repo_to_commit (repo, new_commit);
|
||||
|
||||
if (seaf_commit_manager_add_commit (seaf->commit_mgr, new_commit) < 0) {
|
||||
seaf_warning ("Failed to add commit.\n");
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
||||
"Failed to add commit");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retry:
|
||||
current_head = seaf_commit_manager_get_commit (seaf->commit_mgr,
|
||||
repo->head->commit_id);
|
||||
if (!current_head) {
|
||||
seaf_warning ("Failed to find head commit of %s.\n", repo_id);
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Invalid repo");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Merge if base and head are not the same. */
|
||||
if (strcmp (base->commit_id, current_head->commit_id) != 0) {
|
||||
MergeOptions opt;
|
||||
const char *roots[3];
|
||||
SeafCommit *merged_commit;
|
||||
|
||||
memset (&opt, 0, sizeof(opt));
|
||||
opt.n_ways = 3;
|
||||
memcpy (opt.remote_head, new_commit->commit_id, 40);
|
||||
opt.do_merge = TRUE;
|
||||
|
||||
roots[0] = base->root_id; /* base */
|
||||
roots[1] = current_head->root_id; /* head */
|
||||
roots[2] = new_root; /* remote */
|
||||
|
||||
if (seaf_merge_trees (3, roots, &opt) < 0) {
|
||||
seaf_warning ("Failed to merge.\n");
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
||||
"Internal error");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
GString *merge_desc = g_string_new(NULL);
|
||||
g_string_printf (merge_desc, "Merged %s's changes.", user);
|
||||
merged_commit = seaf_commit_new(NULL, repo->id, opt.merged_tree_root,
|
||||
user, EMPTY_SHA1,
|
||||
merge_desc->str, 0);
|
||||
g_string_free (merge_desc, TRUE);
|
||||
|
||||
merged_commit->parent_id = g_strdup (current_head->commit_id);
|
||||
merged_commit->second_parent_id = g_strdup (new_commit->commit_id);
|
||||
seaf_repo_to_commit (repo, merged_commit);
|
||||
|
||||
if (seaf_commit_manager_add_commit (seaf->commit_mgr, merged_commit) < 0) {
|
||||
seaf_warning ("Failed to add commit.\n");
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
||||
"Failed to add commit");
|
||||
seaf_commit_unref (new_commit);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* replace new_commit with merged_commit. */
|
||||
seaf_commit_unref (new_commit);
|
||||
new_commit = merged_commit;
|
||||
}
|
||||
|
||||
seaf_branch_set_commit(repo->head, new_commit->commit_id);
|
||||
|
||||
if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr,
|
||||
repo->head,
|
||||
current_head->commit_id) < 0)
|
||||
{
|
||||
seaf_message ("Concurrent branch update, retry.\n");
|
||||
|
||||
seaf_repo_unref (repo);
|
||||
repo = NULL;
|
||||
seaf_commit_unref (current_head);
|
||||
current_head = NULL;
|
||||
|
||||
repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
|
||||
if (!repo) {
|
||||
seaf_warning ("Repo %s doesn't exist.\n", repo_id);
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "Invalid repo");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
out:
|
||||
seaf_commit_unref (new_commit);
|
||||
seaf_commit_unref (current_head);
|
||||
seaf_repo_unref (repo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
update_repo_size(const char *repo_id)
|
||||
@ -2594,13 +2699,12 @@ seaf_repo_manager_post_file (SeafRepoManager *mgr,
|
||||
GError **error)
|
||||
{
|
||||
SeafRepo *repo = NULL;
|
||||
SeafCommit *new_commit = NULL, *head_commit = NULL;
|
||||
SeafCommit *head_commit = NULL;
|
||||
char *canon_path = NULL;
|
||||
unsigned char sha1[20];
|
||||
char buf[PATH_MAX];
|
||||
char *root_id = NULL;
|
||||
SeafileCrypt *crypt = NULL;
|
||||
gboolean write_blocks = TRUE;
|
||||
SeafDirent *new_dent = NULL;
|
||||
char hex[41];
|
||||
int ret = 0;
|
||||
@ -2613,7 +2717,6 @@ seaf_repo_manager_post_file (SeafRepoManager *mgr,
|
||||
return -1;
|
||||
}
|
||||
|
||||
retry:
|
||||
GET_REPO_OR_FAIL(repo, repo_id);
|
||||
GET_COMMIT_OR_FAIL(head_commit,repo->head->commit_id);
|
||||
|
||||
@ -2638,36 +2741,33 @@ retry:
|
||||
|
||||
FAIL_IF_FILE_EXISTS(head_commit->root_id, canon_path, file_name, NULL);
|
||||
|
||||
/* Write blocks. We don't need to write blocks in retry.
|
||||
*/
|
||||
if (write_blocks) {
|
||||
if (repo->encrypted) {
|
||||
unsigned char key[16], iv[16];
|
||||
if (seaf_passwd_manager_get_decrypt_key_raw (seaf->passwd_mgr,
|
||||
repo_id, user,
|
||||
key, iv) < 0) {
|
||||
seaf_warning ("Passwd for repo %s is not set.\n", repo_id);
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
||||
"Passwd is not set");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
crypt = seafile_crypt_new (repo->enc_version, key, iv);
|
||||
}
|
||||
|
||||
if (seaf_fs_manager_index_blocks (seaf->fs_mgr, temp_file_path,
|
||||
sha1, crypt) < 0) {
|
||||
seaf_warning ("failed to index blocks");
|
||||
/* Write blocks. */
|
||||
if (repo->encrypted) {
|
||||
unsigned char key[16], iv[16];
|
||||
if (seaf_passwd_manager_get_decrypt_key_raw (seaf->passwd_mgr,
|
||||
repo_id, user,
|
||||
key, iv) < 0) {
|
||||
seaf_warning ("Passwd for repo %s is not set.\n", repo_id);
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
||||
"Failed to index blocks");
|
||||
"Passwd is not set");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rawdata_to_hex(sha1, hex, 20);
|
||||
new_dent = seaf_dirent_new (hex, S_IFREG, file_name);
|
||||
crypt = seafile_crypt_new (repo->enc_version, key, iv);
|
||||
}
|
||||
|
||||
if (seaf_fs_manager_index_blocks (seaf->fs_mgr, temp_file_path,
|
||||
sha1, crypt) < 0) {
|
||||
seaf_warning ("failed to index blocks");
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
||||
"Failed to index blocks");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rawdata_to_hex(sha1, hex, 20);
|
||||
new_dent = seaf_dirent_new (hex, S_IFREG, file_name);
|
||||
|
||||
root_id = do_post_file (head_commit->root_id, canon_path, new_dent);
|
||||
if (!root_id) {
|
||||
seaf_warning ("[post file] Failed to put file.\n");
|
||||
@ -2677,35 +2777,16 @@ retry:
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Commit. */
|
||||
snprintf(buf, PATH_MAX, "Added \"%s\"", file_name);
|
||||
GEN_NEW_COMMIT(repo, root_id, user, buf);
|
||||
|
||||
if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr,
|
||||
repo->head,
|
||||
head_commit->commit_id) < 0)
|
||||
{
|
||||
seaf_warning ("[post file] Concurrent branch update, retry.\n");
|
||||
seaf_repo_unref (repo);
|
||||
seaf_commit_unref (head_commit);
|
||||
seaf_commit_unref (new_commit);
|
||||
g_free (root_id);
|
||||
g_free (crypt);
|
||||
repo = NULL;
|
||||
head_commit = new_commit = NULL;
|
||||
root_id = NULL;
|
||||
crypt = NULL;
|
||||
write_blocks = FALSE;
|
||||
goto retry;
|
||||
}
|
||||
if (gen_new_commit (repo_id, head_commit, root_id,
|
||||
user, buf, error) < 0)
|
||||
ret = -1;
|
||||
|
||||
out:
|
||||
if (repo)
|
||||
seaf_repo_unref (repo);
|
||||
if (head_commit)
|
||||
seaf_commit_unref(head_commit);
|
||||
if (new_commit)
|
||||
seaf_commit_unref(new_commit);
|
||||
if (new_dent)
|
||||
g_free (new_dent);
|
||||
g_free (root_id);
|
||||
@ -2824,14 +2905,13 @@ seaf_repo_manager_del_file (SeafRepoManager *mgr,
|
||||
GError **error)
|
||||
{
|
||||
SeafRepo *repo = NULL;
|
||||
SeafCommit *new_commit = NULL, *head_commit = NULL;
|
||||
SeafCommit *head_commit = NULL;
|
||||
char *canon_path = NULL;
|
||||
char buf[PATH_MAX];
|
||||
char *root_id = NULL;
|
||||
int mode = 0;
|
||||
int ret = 0;
|
||||
|
||||
retry:
|
||||
|
||||
GET_REPO_OR_FAIL(repo, repo_id);
|
||||
GET_COMMIT_OR_FAIL(head_commit, repo->head->commit_id);
|
||||
|
||||
@ -2860,30 +2940,15 @@ retry:
|
||||
snprintf(buf, PATH_MAX, "Deleted \"%s\"", file_name);
|
||||
}
|
||||
|
||||
GEN_NEW_COMMIT(repo, root_id, user, buf);
|
||||
|
||||
if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr,
|
||||
repo->head,
|
||||
head_commit->commit_id) < 0)
|
||||
{
|
||||
seaf_warning ("[del file] Concurrent branch update, retry.\n");
|
||||
seaf_repo_unref (repo);
|
||||
seaf_commit_unref (head_commit);
|
||||
seaf_commit_unref (new_commit);
|
||||
g_free (root_id);
|
||||
repo = NULL;
|
||||
head_commit = new_commit = NULL;
|
||||
root_id = NULL;
|
||||
goto retry;
|
||||
}
|
||||
if (gen_new_commit (repo_id, head_commit, root_id,
|
||||
user, buf, error) < 0)
|
||||
ret = -1;
|
||||
|
||||
out:
|
||||
if (repo)
|
||||
seaf_repo_unref (repo);
|
||||
if (head_commit)
|
||||
seaf_commit_unref(head_commit);
|
||||
if (new_commit)
|
||||
seaf_commit_unref(new_commit);
|
||||
g_free (root_id);
|
||||
g_free (canon_path);
|
||||
|
||||
@ -2948,12 +3013,11 @@ put_dirent_and_commit (const char *repo_id,
|
||||
GError **error)
|
||||
{
|
||||
SeafRepo *repo = NULL;
|
||||
SeafCommit *head_commit = NULL, *new_commit = NULL;
|
||||
SeafCommit *head_commit = NULL;
|
||||
char *root_id = NULL;
|
||||
char buf[PATH_MAX];
|
||||
int ret = 0;
|
||||
|
||||
retry:
|
||||
GET_REPO_OR_FAIL(repo, repo_id);
|
||||
GET_COMMIT_OR_FAIL(head_commit, repo->head->commit_id);
|
||||
|
||||
@ -2973,27 +3037,15 @@ retry:
|
||||
snprintf(buf, sizeof(buf), "Added \"%s\"", dent->name);
|
||||
}
|
||||
|
||||
GEN_NEW_COMMIT(repo, root_id, user, buf);
|
||||
if (gen_new_commit (repo_id, head_commit, root_id,
|
||||
user, buf, error) < 0)
|
||||
ret = -1;
|
||||
|
||||
if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr,
|
||||
repo->head,
|
||||
head_commit->commit_id) < 0)
|
||||
{
|
||||
seaf_warning ("Concurrent update, retry.\n");
|
||||
seaf_repo_unref (repo);
|
||||
seaf_commit_unref (head_commit);
|
||||
seaf_commit_unref (new_commit);
|
||||
g_free (root_id);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
out:
|
||||
if (repo)
|
||||
seaf_repo_unref (repo);
|
||||
if (head_commit)
|
||||
seaf_commit_unref (head_commit);
|
||||
if (new_commit)
|
||||
seaf_commit_unref (new_commit);
|
||||
if (root_id)
|
||||
g_free (root_id);
|
||||
|
||||
@ -3103,12 +3155,11 @@ move_file_same_repo (const char *repo_id,
|
||||
GError **error)
|
||||
{
|
||||
SeafRepo *repo = NULL;
|
||||
SeafCommit *head_commit = NULL, *new_commit = NULL;
|
||||
SeafCommit *head_commit = NULL;
|
||||
char *root_id_after_put = NULL, *root_id = NULL;
|
||||
char buf[PATH_MAX];
|
||||
int ret = 0;
|
||||
|
||||
retry:
|
||||
GET_REPO_OR_FAIL(repo, repo_id);
|
||||
GET_COMMIT_OR_FAIL(head_commit, repo->head->commit_id);
|
||||
|
||||
@ -3133,31 +3184,15 @@ retry:
|
||||
snprintf(buf, PATH_MAX, "Moved \"%s\"", src_dent->name);
|
||||
}
|
||||
|
||||
GEN_NEW_COMMIT(repo, root_id, user, buf);
|
||||
|
||||
if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr,
|
||||
repo->head,
|
||||
head_commit->commit_id) < 0)
|
||||
{
|
||||
seaf_warning ("[move file] Concurrent branch update, retry.\n");
|
||||
seaf_repo_unref (repo);
|
||||
seaf_commit_unref (head_commit);
|
||||
seaf_commit_unref (new_commit);
|
||||
g_free (root_id_after_put);
|
||||
g_free (root_id);
|
||||
repo = NULL;
|
||||
head_commit = new_commit = NULL;
|
||||
root_id_after_put = root_id = NULL;
|
||||
goto retry;
|
||||
}
|
||||
if (gen_new_commit (repo_id, head_commit, root_id,
|
||||
user, buf, error) < 0)
|
||||
ret = -1;
|
||||
|
||||
out:
|
||||
if (repo)
|
||||
seaf_repo_unref (repo);
|
||||
if (head_commit)
|
||||
seaf_commit_unref (head_commit);
|
||||
if (new_commit)
|
||||
seaf_commit_unref (new_commit);
|
||||
g_free (root_id_after_put);
|
||||
g_free (root_id);
|
||||
|
||||
@ -3275,14 +3310,13 @@ seaf_repo_manager_post_dir (SeafRepoManager *mgr,
|
||||
GError **error)
|
||||
{
|
||||
SeafRepo *repo = NULL;
|
||||
SeafCommit *new_commit = NULL, *head_commit = NULL;
|
||||
SeafCommit *head_commit = NULL;
|
||||
char *canon_path = NULL;
|
||||
char buf[PATH_MAX];
|
||||
char *root_id = NULL;
|
||||
SeafDirent *new_dent = NULL;
|
||||
int ret = 0;
|
||||
|
||||
retry:
|
||||
GET_REPO_OR_FAIL(repo, repo_id);
|
||||
GET_COMMIT_OR_FAIL(head_commit, repo->head->commit_id);
|
||||
|
||||
@ -3305,31 +3339,15 @@ retry:
|
||||
|
||||
/* Commit. */
|
||||
snprintf(buf, PATH_MAX, "Added directory \"%s\"", new_dir_name);
|
||||
GEN_NEW_COMMIT(repo, root_id, user, buf);
|
||||
|
||||
if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr,
|
||||
repo->head,
|
||||
head_commit->commit_id) < 0)
|
||||
{
|
||||
seaf_warning ("[post dir] Concurrent branch update, retry.\n");
|
||||
seaf_repo_unref (repo);
|
||||
seaf_commit_unref (head_commit);
|
||||
seaf_commit_unref (new_commit);
|
||||
g_free (root_id);
|
||||
g_free (canon_path);
|
||||
repo = NULL;
|
||||
head_commit = new_commit = NULL;
|
||||
root_id = canon_path = NULL;
|
||||
goto retry;
|
||||
}
|
||||
if (gen_new_commit (repo_id, head_commit, root_id,
|
||||
user, buf, error) < 0)
|
||||
ret = -1;
|
||||
|
||||
out:
|
||||
if (repo)
|
||||
seaf_repo_unref (repo);
|
||||
if (head_commit)
|
||||
seaf_commit_unref(head_commit);
|
||||
if (new_commit)
|
||||
seaf_commit_unref(new_commit);
|
||||
if (new_dent)
|
||||
g_free (new_dent);
|
||||
g_free (root_id);
|
||||
@ -3347,14 +3365,13 @@ seaf_repo_manager_post_empty_file (SeafRepoManager *mgr,
|
||||
GError **error)
|
||||
{
|
||||
SeafRepo *repo = NULL;
|
||||
SeafCommit *new_commit = NULL, *head_commit = NULL;
|
||||
SeafCommit *head_commit = NULL;
|
||||
char *canon_path = NULL;
|
||||
char buf[PATH_MAX];
|
||||
char *root_id = NULL;
|
||||
SeafDirent *new_dent = NULL;
|
||||
int ret = 0;
|
||||
|
||||
retry:
|
||||
GET_REPO_OR_FAIL(repo, repo_id);
|
||||
GET_COMMIT_OR_FAIL(head_commit, repo->head->commit_id);
|
||||
|
||||
@ -3379,31 +3396,15 @@ retry:
|
||||
|
||||
/* Commit. */
|
||||
snprintf(buf, PATH_MAX, "Added \"%s\"", new_file_name);
|
||||
GEN_NEW_COMMIT(repo, root_id, user, buf);
|
||||
|
||||
if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr,
|
||||
repo->head,
|
||||
head_commit->commit_id) < 0)
|
||||
{
|
||||
seaf_warning ("[post dir] Concurrent branch update, retry.\n");
|
||||
seaf_repo_unref (repo);
|
||||
seaf_commit_unref (head_commit);
|
||||
seaf_commit_unref (new_commit);
|
||||
g_free (root_id);
|
||||
g_free (canon_path);
|
||||
repo = NULL;
|
||||
head_commit = new_commit = NULL;
|
||||
root_id = canon_path = NULL;
|
||||
goto retry;
|
||||
}
|
||||
if (gen_new_commit (repo_id, head_commit, root_id,
|
||||
user, buf, error) < 0)
|
||||
ret = -1;
|
||||
|
||||
out:
|
||||
if (repo)
|
||||
seaf_repo_unref (repo);
|
||||
if (head_commit)
|
||||
seaf_commit_unref(head_commit);
|
||||
if (new_commit)
|
||||
seaf_commit_unref(new_commit);
|
||||
if (new_dent)
|
||||
g_free (new_dent);
|
||||
g_free (root_id);
|
||||
@ -3533,7 +3534,7 @@ seaf_repo_manager_rename_file (SeafRepoManager *mgr,
|
||||
GError **error)
|
||||
{
|
||||
SeafRepo *repo = NULL;
|
||||
SeafCommit *head_commit = NULL, *new_commit = NULL;
|
||||
SeafCommit *head_commit = NULL;
|
||||
char *root_id = NULL;
|
||||
char *canon_path = NULL;
|
||||
char buf[PATH_MAX];
|
||||
@ -3543,7 +3544,6 @@ seaf_repo_manager_rename_file (SeafRepoManager *mgr,
|
||||
if (strcmp(oldname, newname) == 0)
|
||||
return 0;
|
||||
|
||||
retry:
|
||||
GET_REPO_OR_FAIL(repo, repo_id);
|
||||
GET_COMMIT_OR_FAIL(head_commit, repo->head->commit_id);
|
||||
|
||||
@ -3569,30 +3569,15 @@ retry:
|
||||
snprintf(buf, PATH_MAX, "Renamed \"%s\"", oldname);
|
||||
}
|
||||
|
||||
GEN_NEW_COMMIT(repo, root_id, user, buf);
|
||||
|
||||
if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr,
|
||||
repo->head,
|
||||
head_commit->commit_id) < 0)
|
||||
{
|
||||
seaf_warning ("[rename file] Concurrent branch update, retry.\n");
|
||||
seaf_repo_unref (repo);
|
||||
seaf_commit_unref (head_commit);
|
||||
seaf_commit_unref (new_commit);
|
||||
g_free (root_id);
|
||||
repo = NULL;
|
||||
head_commit = new_commit = NULL;
|
||||
root_id = NULL;
|
||||
goto retry;
|
||||
}
|
||||
if (gen_new_commit (repo_id, head_commit, root_id,
|
||||
user, buf, error) < 0)
|
||||
ret = -1;
|
||||
|
||||
out:
|
||||
if (repo)
|
||||
seaf_repo_unref (repo);
|
||||
if (head_commit)
|
||||
seaf_commit_unref (head_commit);
|
||||
if (new_commit)
|
||||
seaf_commit_unref (new_commit);
|
||||
g_free (canon_path);
|
||||
g_free (root_id);
|
||||
|
||||
@ -3869,13 +3854,12 @@ seaf_repo_manager_put_file (SeafRepoManager *mgr,
|
||||
GError **error)
|
||||
{
|
||||
SeafRepo *repo = NULL;
|
||||
SeafCommit *new_commit = NULL, *head_commit = NULL;
|
||||
SeafCommit *head_commit = NULL;
|
||||
char *canon_path = NULL;
|
||||
unsigned char sha1[20];
|
||||
char buf[PATH_MAX];
|
||||
char *root_id = NULL;
|
||||
SeafileCrypt *crypt = NULL;
|
||||
gboolean write_blocks = TRUE;
|
||||
SeafDirent *new_dent = NULL;
|
||||
char hex[41];
|
||||
char *old_file_id = NULL, *fullpath = NULL;
|
||||
@ -3889,7 +3873,6 @@ seaf_repo_manager_put_file (SeafRepoManager *mgr,
|
||||
return -1;
|
||||
}
|
||||
|
||||
retry:
|
||||
GET_REPO_OR_FAIL(repo, repo_id);
|
||||
GET_COMMIT_OR_FAIL(head_commit, repo->head->commit_id);
|
||||
|
||||
@ -3914,36 +3897,33 @@ retry:
|
||||
|
||||
FAIL_IF_FILE_NOT_EXISTS(head_commit->root_id, canon_path, file_name, NULL);
|
||||
|
||||
/* Write blocks. We don't need to write blocks in retry.
|
||||
*/
|
||||
if (write_blocks) {
|
||||
if (repo->encrypted) {
|
||||
unsigned char key[16], iv[16];
|
||||
if (seaf_passwd_manager_get_decrypt_key_raw (seaf->passwd_mgr,
|
||||
repo_id, user,
|
||||
key, iv) < 0) {
|
||||
seaf_warning ("Passwd for repo %s is not set.\n", repo_id);
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
||||
"Passwd is not set");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
crypt = seafile_crypt_new (repo->enc_version, key, iv);
|
||||
}
|
||||
|
||||
if (seaf_fs_manager_index_blocks (seaf->fs_mgr, temp_file_path,
|
||||
sha1, crypt) < 0) {
|
||||
seaf_warning ("failed to index blocks");
|
||||
/* Write blocks. */
|
||||
if (repo->encrypted) {
|
||||
unsigned char key[16], iv[16];
|
||||
if (seaf_passwd_manager_get_decrypt_key_raw (seaf->passwd_mgr,
|
||||
repo_id, user,
|
||||
key, iv) < 0) {
|
||||
seaf_warning ("Passwd for repo %s is not set.\n", repo_id);
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
||||
"Failed to index blocks");
|
||||
"Passwd is not set");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rawdata_to_hex(sha1, hex, 20);
|
||||
new_dent = seaf_dirent_new (hex, S_IFREG, file_name);
|
||||
crypt = seafile_crypt_new (repo->enc_version, key, iv);
|
||||
}
|
||||
|
||||
if (seaf_fs_manager_index_blocks (seaf->fs_mgr, temp_file_path,
|
||||
sha1, crypt) < 0) {
|
||||
seaf_warning ("failed to index blocks");
|
||||
g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL,
|
||||
"Failed to index blocks");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rawdata_to_hex(sha1, hex, 20);
|
||||
new_dent = seaf_dirent_new (hex, S_IFREG, file_name);
|
||||
|
||||
if (!fullpath)
|
||||
fullpath = g_build_filename(parent_dir, file_name, NULL);
|
||||
|
||||
@ -3967,33 +3947,15 @@ retry:
|
||||
|
||||
/* Commit. */
|
||||
snprintf(buf, PATH_MAX, "Modified \"%s\"", file_name);
|
||||
GEN_NEW_COMMIT(repo, root_id, user, buf);
|
||||
|
||||
if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr,
|
||||
repo->head,
|
||||
head_commit->commit_id) < 0)
|
||||
{
|
||||
seaf_warning ("[put file] Concurrent branch update, retry.\n");
|
||||
seaf_repo_unref (repo); repo = NULL;
|
||||
seaf_commit_unref (head_commit);
|
||||
seaf_commit_unref (new_commit);
|
||||
head_commit = new_commit = NULL;
|
||||
|
||||
g_free (root_id); root_id = NULL;
|
||||
g_free (crypt); crypt = NULL;
|
||||
g_free (old_file_id); old_file_id = NULL;
|
||||
|
||||
write_blocks = FALSE;
|
||||
goto retry;
|
||||
}
|
||||
if (gen_new_commit (repo_id, head_commit, root_id,
|
||||
user, buf, error) < 0)
|
||||
ret = -1;
|
||||
|
||||
out:
|
||||
if (repo)
|
||||
seaf_repo_unref (repo);
|
||||
if (head_commit)
|
||||
seaf_commit_unref(head_commit);
|
||||
if (new_commit)
|
||||
seaf_commit_unref(new_commit);
|
||||
if (new_dent)
|
||||
g_free (new_dent);
|
||||
g_free (root_id);
|
||||
@ -4207,7 +4169,7 @@ seaf_repo_manager_revert_file (SeafRepoManager *mgr,
|
||||
GError **error)
|
||||
{
|
||||
SeafRepo *repo = NULL;
|
||||
SeafCommit *head_commit = NULL, *old_commit = NULL, *new_commit = NULL;
|
||||
SeafCommit *head_commit = NULL, *old_commit = NULL;
|
||||
char *parent_dir = NULL, *filename = NULL;
|
||||
char *revert_to_file_id = NULL;
|
||||
char *canon_path = NULL, *root_id = NULL;
|
||||
@ -4218,7 +4180,6 @@ seaf_repo_manager_revert_file (SeafRepoManager *mgr,
|
||||
gboolean skipped = FALSE;
|
||||
int ret = 0;
|
||||
|
||||
retry:
|
||||
GET_REPO_OR_FAIL(repo, repo_id);
|
||||
GET_COMMIT_OR_FAIL(head_commit, repo->head->commit_id);
|
||||
|
||||
@ -4312,21 +4273,9 @@ retry:
|
||||
strftime (time_str, sizeof(time_str), "%F %T",
|
||||
localtime((time_t *)(&old_commit->ctime)));
|
||||
snprintf(buf, PATH_MAX, "Reverted file \"%s\" to status at %s", filename, time_str);
|
||||
GEN_NEW_COMMIT(repo, root_id, user, buf);
|
||||
|
||||
if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr,
|
||||
repo->head,
|
||||
head_commit->commit_id) < 0)
|
||||
{
|
||||
g_debug ("[revert file] Concurrent branch update, retry.\n");
|
||||
seaf_repo_unref (repo); repo = NULL;
|
||||
seaf_commit_unref (head_commit); head_commit = NULL;
|
||||
seaf_commit_unref (new_commit); new_commit = NULL;
|
||||
|
||||
g_free (root_id); root_id = NULL;
|
||||
|
||||
goto retry;
|
||||
}
|
||||
if (gen_new_commit (repo_id, head_commit, root_id,
|
||||
user, buf, error) < 0)
|
||||
ret = -1;
|
||||
|
||||
out:
|
||||
if (repo)
|
||||
@ -4335,8 +4284,6 @@ out:
|
||||
seaf_commit_unref (head_commit);
|
||||
if (old_commit)
|
||||
seaf_commit_unref (old_commit);
|
||||
if (new_commit)
|
||||
seaf_commit_unref (new_commit);
|
||||
|
||||
g_free (root_id);
|
||||
g_free (parent_dir);
|
||||
@ -4423,7 +4370,7 @@ seaf_repo_manager_revert_dir (SeafRepoManager *mgr,
|
||||
GError **error)
|
||||
{
|
||||
SeafRepo *repo = NULL;
|
||||
SeafCommit *head_commit = NULL, *old_commit = NULL, *new_commit = NULL;
|
||||
SeafCommit *head_commit = NULL, *old_commit = NULL;
|
||||
char *parent_dir = NULL, *dirname = NULL;
|
||||
char *revert_to_dir_id = NULL;
|
||||
char *canon_path = NULL, *root_id = NULL;
|
||||
@ -4433,7 +4380,6 @@ seaf_repo_manager_revert_dir (SeafRepoManager *mgr,
|
||||
gboolean skipped = FALSE;
|
||||
int ret = 0;
|
||||
|
||||
retry:
|
||||
GET_REPO_OR_FAIL(repo, repo_id);
|
||||
GET_COMMIT_OR_FAIL(head_commit, repo->head->commit_id);
|
||||
|
||||
@ -4522,21 +4468,9 @@ retry:
|
||||
|
||||
/* Commit. */
|
||||
snprintf(buf, PATH_MAX, "Recovered deleted directory \"%s\"", dirname);
|
||||
GEN_NEW_COMMIT(repo, root_id, user, buf);
|
||||
|
||||
if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr,
|
||||
repo->head,
|
||||
head_commit->commit_id) < 0)
|
||||
{
|
||||
g_debug ("[revert dir] Concurrent branch update, retry.\n");
|
||||
seaf_repo_unref (repo); repo = NULL;
|
||||
seaf_commit_unref (head_commit); head_commit = NULL;
|
||||
seaf_commit_unref (new_commit); new_commit = NULL;
|
||||
|
||||
g_free (root_id); root_id = NULL;
|
||||
|
||||
goto retry;
|
||||
}
|
||||
if (gen_new_commit (repo_id, head_commit, root_id,
|
||||
user, buf, error) < 0)
|
||||
ret = -1;
|
||||
|
||||
out:
|
||||
if (repo)
|
||||
@ -4545,8 +4479,6 @@ out:
|
||||
seaf_commit_unref (head_commit);
|
||||
if (old_commit)
|
||||
seaf_commit_unref (old_commit);
|
||||
if (new_commit)
|
||||
seaf_commit_unref (new_commit);
|
||||
|
||||
g_free (root_id);
|
||||
g_free (parent_dir);
|
||||
|
Loading…
Reference in New Issue
Block a user