diff --git a/common/commit-mgr.c b/common/commit-mgr.c index 780a46c..aae721f 100644 --- a/common/commit-mgr.c +++ b/common/commit-mgr.c @@ -376,6 +376,7 @@ seaf_commit_manager_traverse_commit_tree_with_limit (SeafCommitManager *mgr, CommitTraverseFunc func, int limit, void *data, + char **next_start_commit, gboolean skip_errors) { SeafCommit *commit; @@ -389,6 +390,7 @@ seaf_commit_manager_traverse_commit_tree_with_limit (SeafCommitManager *mgr, commit = seaf_commit_manager_get_commit (mgr, repo_id, version, head); if (!commit) { seaf_warning ("Failed to find commit %s.\n", head); + g_hash_table_destroy (commit_hash); return FALSE; } @@ -413,12 +415,6 @@ seaf_commit_manager_traverse_commit_tree_with_limit (SeafCommitManager *mgr, } } - /* Stop when limit is reached. If limit < 0, there is no limit; */ - if (limit > 0 && ++count == limit) { - seaf_commit_unref (commit); - break; - } - if (stop) { seaf_commit_unref (commit); /* stop traverse down from this commit, @@ -448,6 +444,26 @@ seaf_commit_manager_traverse_commit_tree_with_limit (SeafCommitManager *mgr, } } seaf_commit_unref (commit); + + /* Stop when limit is reached and don't stop at unmerged branch. + * If limit < 0, there is no limit; + */ + if (limit > 0 && ++count >= limit && (!list || !list->next)) { + break; + } + } + /* + * two scenarios: + * 1. list is empty, indicate scan end + * 2. list only have one commit, as start for next scan + */ + if (list) { + commit = list->data; + if (next_start_commit) { + *next_start_commit= g_strdup (commit->commit_id); + } + seaf_commit_unref (commit); + list = g_list_delete_link (list, list); } out: diff --git a/common/commit-mgr.h b/common/commit-mgr.h index 6d03bea..6b0a246 100644 --- a/common/commit-mgr.h +++ b/common/commit-mgr.h @@ -180,6 +180,7 @@ seaf_commit_manager_traverse_commit_tree_with_limit (SeafCommitManager *mgr, CommitTraverseFunc func, int limit, void *data, + char **next_start_commit, gboolean skip_errors); gboolean diff --git a/common/rpc-service.c b/common/rpc-service.c index 6c6baba..2fc98a0 100644 --- a/common/rpc-service.c +++ b/common/rpc-service.c @@ -4289,10 +4289,9 @@ seafile_list_dir (const char *repo_id, GList * seafile_list_file_revisions (const char *repo_id, + const char *commit_id, const char *path, - int max_revision, int limit, - int show_days, GError **error) { if (!repo_id || !path) { @@ -4310,9 +4309,8 @@ seafile_list_file_revisions (const char *repo_id, GList *commit_list; commit_list = seaf_repo_manager_list_file_revisions (seaf->repo_mgr, - repo_id, NULL, rpath, - max_revision, - limit, show_days, FALSE, error); + repo_id, commit_id, rpath, + limit, FALSE, FALSE, error); g_free (rpath); return commit_list; diff --git a/include/seafile-rpc.h b/include/seafile-rpc.h index b83d4db..ad9e33c 100644 --- a/include/seafile-rpc.h +++ b/include/seafile-rpc.h @@ -827,10 +827,9 @@ seafile_get_dirent_by_path (const char *repo_id, const char *path, */ GList * seafile_list_file_revisions (const char *repo_id, + const char *commit_id, const char *path, - int max_revision, int limit, - int show_days, GError **error); GList * diff --git a/lib/commit.vala b/lib/commit.vala index 43ae6da..e43a1e0 100644 --- a/lib/commit.vala +++ b/lib/commit.vala @@ -66,6 +66,9 @@ public class Commit : Object { public string device_name { get; set; } public string client_version { get; set; } + + //Only used for file history pagination + public string next_start_commit { get; set; } } } // namespace diff --git a/lib/rpc_table.py b/lib/rpc_table.py index d822585..e85c022 100644 --- a/lib/rpc_table.py +++ b/lib/rpc_table.py @@ -83,6 +83,7 @@ func_table = [ [ "objlist", ["string", "string", "string", "int"] ], [ "objlist", ["string", "string", "int", "int"] ], [ "objlist", ["string", "string", "int", "int", "int"] ], + [ "objlist", ["string", "string", "string", "int", "int", "int"] ], [ "objlist", ["int", "string", "string", "int", "int"] ], [ "objlist", ["string", "int", "string", "string", "string"] ], [ "objlist", ["string", "int", "string", "int", "int"] ], diff --git a/python/seafile/rpcclient.py b/python/seafile/rpcclient.py index 38cba60..079e9a5 100644 --- a/python/seafile/rpcclient.py +++ b/python/seafile/rpcclient.py @@ -509,8 +509,8 @@ class SeafServerThreadedRpcClient(ccnet.RpcClientBase): pass get_dirent_by_path = seafile_get_dirent_by_path - @searpc_func("objlist", ["string", "string", "int", "int", "int"]) - def seafile_list_file_revisions(repo_id, path, max_revision, limit, show_days): + @searpc_func("objlist", ["string", "string", "string", "int"]) + def seafile_list_file_revisions(repo_id, commit_id, path, limit): pass list_file_revisions = seafile_list_file_revisions diff --git a/python/seaserv/api.py b/python/seaserv/api.py index 2b07569..fcd76ee 100644 --- a/python/seaserv/api.py +++ b/python/seaserv/api.py @@ -305,13 +305,12 @@ class SeafileAPI(object): """ return seafserv_threaded_rpc.get_deleted(repo_id, show_days, path, scan_stat, limit) - def get_file_revisions(self, repo_id, path, max_revision, limit, show_days=-1): + def get_file_revisions(self, repo_id, commit_id, path, limit): """ Get revisions of a file. - @max_revision: maximum number of revisions returned + @commit_id: start traversing from this commit @limit: maximum number of commits to traverse when looking for revisions - @show_days: only return revisions in the @show_days Return a list of Commit objects (lib/commit.vala) related to the revisions. A few special attributes are added to the commit object: @@ -319,10 +318,10 @@ class SeafileAPI(object): @rev_file_size: size of the file revision @rev_renamed_old_path: set if this revision is made by a rename operation. It's set to the old path before rename. + @next_start_commit: commit_id for next page. An extra commit which only contains @next_start_commit + will be appended to the list. """ - return seafserv_threaded_rpc.list_file_revisions(repo_id, path, - max_revision, limit, - show_days) + return seafserv_threaded_rpc.list_file_revisions(repo_id, commit_id, path, limit) # This api is slow and should only be used for version 0 repos. def get_files_last_modified(self, repo_id, parent_dir, limit): diff --git a/server/repo-mgr.h b/server/repo-mgr.h index 3735ab7..df00793 100644 --- a/server/repo-mgr.h +++ b/server/repo-mgr.h @@ -510,10 +510,9 @@ seaf_repo_manager_list_file_revisions (SeafRepoManager *mgr, const char *repo_id, const char *start_commit_id, const char *path, - int max_revision, int limit, - int show_days, gboolean got_latest, + gboolean got_second, GError **error); GList * diff --git a/server/repo-op.c b/server/repo-op.c index 3cd8ab3..fc0ac55 100644 --- a/server/repo-op.c +++ b/server/repo-op.c @@ -4742,18 +4742,14 @@ struct CollectRevisionParam { int n_commits; GHashTable *file_info_cache; - /* - * > 0: stop collect when this amount of revisions are collected. - * <= 0: no limit - */ - int max_revision; - /* > 0: keep a period of history; * == 0: N/A * < 0: keep all history data. */ gint64 truncate_time; gboolean got_latest; + gboolean got_second; + gboolean not_found_file; GError **error; }; @@ -5011,17 +5007,13 @@ collect_file_revisions (SeafCommit *commit, void *vdata, gboolean *stop) if (data->got_latest && data->truncate_time > 0 && - (gint64)(commit->ctime) < data->truncate_time) + (gint64)(commit->ctime) < data->truncate_time && + data->got_second) { *stop = TRUE; return TRUE; } - if (data->max_revision > 0 && data->n_commits > data->max_revision) { - *stop = TRUE; - return TRUE; - } - g_clear_error (error); file_info = get_file_info (data->repo, commit, path, @@ -5039,6 +5031,7 @@ collect_file_revisions (SeafCommit *commit, void *vdata, gboolean *stop) * Deleted files with the same path are not included in history. */ *stop = TRUE; + data->not_found_file = TRUE; goto out; } @@ -5100,9 +5093,12 @@ collect_file_revisions (SeafCommit *commit, void *vdata, gboolean *stop) } } - if (!data->got_latest) + if (!data->got_latest) { data->got_latest = TRUE; - + } else { + if (!data->got_second) + data->got_second = TRUE; + } add_revision_info (data, commit, file_info->file_id, file_info->file_size); out: @@ -5278,10 +5274,9 @@ seaf_repo_manager_list_file_revisions (SeafRepoManager *mgr, const char *repo_id, const char *start_commit_id, const char *path, - int max_revision, int limit, - int show_days, gboolean got_latest, + gboolean got_second, GError **error) { SeafRepo *repo = NULL; @@ -5292,9 +5287,7 @@ seaf_repo_manager_list_file_revisions (SeafRepoManager *mgr, const char *head_id; gboolean is_renamed = FALSE; char *parent_id = NULL, *old_path = NULL; - GList *old_revisions = NULL; - gint64 truncate_time; - gint64 show_time; + char *next_start_commit= NULL; repo = seaf_repo_manager_get_repo (mgr, repo_id); if (!repo) { @@ -5312,21 +5305,15 @@ seaf_repo_manager_list_file_revisions (SeafRepoManager *mgr, data.path = path; data.error = error; - data.max_revision = max_revision; - truncate_time = seaf_repo_manager_get_repo_truncate_time (mgr, repo_id); - if (truncate_time == 0) { - // Don't keep history - data.truncate_time = 0; - } else { - show_time = show_days > 0 ? time(NULL) - show_days*24*3600 : -1; - data.truncate_time = MAX (truncate_time, show_time); - } + data.truncate_time = seaf_repo_manager_get_repo_truncate_time (mgr, repo_id); data.wanted_commits = NULL; data.file_id_list = NULL; data.file_size_list = NULL; data.got_latest = got_latest; + data.got_second = got_second; + data.not_found_file = FALSE; /* A hash table to cache caculated file info of in */ data.file_info_cache = g_hash_table_new_full (g_str_hash, g_str_equal, @@ -5337,52 +5324,48 @@ seaf_repo_manager_list_file_revisions (SeafRepoManager *mgr, repo->version, head_id, (CommitTraverseFunc)collect_file_revisions, - limit, &data, TRUE)) { + limit, &data, &next_start_commit, TRUE)) { g_clear_error (error); g_set_error (error, SEAFILE_DOMAIN, SEAF_ERR_GENERAL, "failed to traverse commit of repo %s", repo_id); goto out; } - if (!data.wanted_commits) { - g_clear_error (error); - goto out; - } - - /* commit list in descending commit time order. */ - last_commit = data.wanted_commits->data; - - is_renamed = detect_rename_revision (repo, - last_commit, path, &parent_id, &old_path); - - commit_list = g_list_reverse (data.wanted_commits); - file_id_list = g_list_reverse (data.file_id_list); - file_size_list = g_list_reverse (data.file_size_list); - - ret = convert_rpc_commit_list (commit_list, file_id_list, file_size_list, - is_renamed, old_path); - - if (is_renamed) { - if (ret) { - // if previous scan got revision then set got_latest True for renamend scan - /* Get the revisions of the old path, starting from parent commit. */ - old_revisions = seaf_repo_manager_list_file_revisions (mgr, repo_id, - parent_id, old_path, - -1, -1, show_days, - TRUE, error); - } else { - /* Get the revisions of the old path, starting from parent commit. */ - old_revisions = seaf_repo_manager_list_file_revisions (mgr, repo_id, - parent_id, old_path, - -1, -1, show_days, - FALSE, error); + if (data.wanted_commits) { + last_commit = data.wanted_commits->data; + is_renamed = detect_rename_revision (repo, + last_commit, path, &parent_id, &old_path); + if (data.not_found_file && !is_renamed) { // reached file initial commit. + g_free (next_start_commit); + next_start_commit = NULL; + } else if (is_renamed){ // file renamed. + g_free (next_start_commit); + next_start_commit = g_strdup (parent_id); + } + commit_list = g_list_reverse (data.wanted_commits); + file_id_list = g_list_reverse (data.file_id_list); + file_size_list = g_list_reverse (data.file_size_list); + + char *rename_path = NULL; + if (old_path && *old_path != '/') + rename_path = g_strconcat ("/", old_path, NULL); + else + rename_path = g_strdup (old_path); + + ret = convert_rpc_commit_list (commit_list, file_id_list, file_size_list, + is_renamed, rename_path); + g_free (rename_path); + } else { + if (data.not_found_file) { + g_free (next_start_commit); + next_start_commit = NULL; } - ret = g_list_concat (ret, old_revisions); - g_free (parent_id); - g_free (old_path); } - g_clear_error (error); + /* Append one commit that only contains 'next_start_commit' */ + SeafileCommit *commit = seafile_commit_new (); + g_object_set (commit, "next_start_commit", next_start_commit, NULL); + ret = g_list_append (ret, commit); out: if (repo) @@ -5396,6 +5379,9 @@ out: g_list_free (file_size_list); if (data.file_info_cache) g_hash_table_destroy (data.file_info_cache); + g_free (old_path); + g_free (parent_id); + g_free (next_start_commit); return ret; } @@ -5587,7 +5573,7 @@ seaf_repo_manager_calc_files_last_modified (SeafRepoManager *mgr, repo->id, repo->version, repo->head->commit_id, (CommitTraverseFunc)collect_files_last_modified, - limit, &data, FALSE)) { + limit, &data, NULL, FALSE)) { if (*error) seaf_warning ("error when traversing commits: %s\n", (*error)->message); else diff --git a/server/seaf-server.c b/server/seaf-server.c index 86cd917..e4d6b6d 100644 --- a/server/seaf-server.c +++ b/server/seaf-server.c @@ -331,7 +331,7 @@ static void start_rpc_service (CcnetClient *client, int cloud_mode) searpc_server_register_function ("seafserv-threaded-rpcserver", seafile_list_file_revisions, "seafile_list_file_revisions", - searpc_signature_objlist__string_string_int_int_int()); + searpc_signature_objlist__string_string_string_int()); searpc_server_register_function ("seafserv-threaded-rpcserver", seafile_calc_files_last_modified,