diff options
Diffstat (limited to 'subversion/svn/status.c')
-rw-r--r-- | subversion/svn/status.c | 237 |
1 files changed, 212 insertions, 25 deletions
diff --git a/subversion/svn/status.c b/subversion/svn/status.c index 0c5bddb..9f1ad34 100644 --- a/subversion/svn/status.c +++ b/subversion/svn/status.c @@ -26,6 +26,7 @@ /*** Includes. ***/ +#include "svn_hash.h" #include "svn_cmdline.h" #include "svn_wc.h" #include "svn_dirent_uri.h" @@ -33,7 +34,7 @@ #include "svn_time.h" #include "cl.h" #include "svn_private_config.h" -#include "tree-conflicts.h" +#include "cl-conflicts.h" #include "private/svn_wc_private.h" /* Return the single character representation of STATUS */ @@ -135,11 +136,85 @@ generate_status_desc(enum svn_wc_status_kind status) } } +/* Make a relative path containing '..' elements as needed. + TARGET_ABSPATH shall be the absolute version of TARGET_PATH. + TARGET_ABSPATH, TARGET_PATH and PATH shall be canonical. + + If above conditions are met, a relative path that leads to PATH + from TARGET_PATH is returned, but there is no error checking involved. + + The returned path is allocated from RESULT_POOL, all other + allocations are made in SCRATCH_POOL. */ +static const char * +make_relpath(const char *target_abspath, + const char *target_path, + const char *path, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const char *la; + const char *parent_dir_els = ""; + const char *abspath, *relative; + svn_error_t *err = svn_dirent_get_absolute(&abspath, path, scratch_pool); + + if (err) + { + /* We probably got passed some invalid path. */ + svn_error_clear(err); + return apr_pstrdup(result_pool, path); + } + + relative = svn_dirent_skip_ancestor(target_abspath, abspath); + if (relative) + { + return svn_dirent_join(target_path, relative, result_pool); + } + + /* An example: + * relative_to_path = /a/b/c + * path = /a/x/y/z + * result = ../../x/y/z + * + * Another example (Windows specific): + * relative_to_path = F:/wc + * path = C:/wc + * result = C:/wc + */ + + /* Skip the common ancestor of both paths, here '/a'. */ + la = svn_dirent_get_longest_ancestor(target_abspath, abspath, + scratch_pool); + if (*la == '\0') + { + /* Nothing in common: E.g. C:/ vs F:/ on Windows */ + return apr_pstrdup(result_pool, path); + } + relative = svn_dirent_skip_ancestor(la, target_abspath); + path = svn_dirent_skip_ancestor(la, path); + + /* In above example, we'd now have: + * relative_to_path = b/c + * path = x/y/z */ + + /* Count the elements of relative_to_path and prepend as many '..' elements + * to path. */ + while (*relative) + { + svn_dirent_split(&relative, NULL, relative, + scratch_pool); + parent_dir_els = svn_dirent_join(parent_dir_els, "..", scratch_pool); + } + + return svn_dirent_join(parent_dir_els, path, result_pool); +} + /* Print STATUS and PATH in a format determined by DETAILED and SHOW_LAST_COMMITTED. */ static svn_error_t * -print_status(const char *path, +print_status(const char *target_abspath, + const char *target_path, + const char *path, svn_boolean_t detailed, svn_boolean_t show_last_committed, svn_boolean_t repos_locks, @@ -154,6 +229,10 @@ print_status(const char *path, enum svn_wc_status_kind prop_status = status->prop_status; char tree_status_code = ' '; const char *tree_desc_line = ""; + const char *moved_from_line = ""; + const char *moved_to_line = ""; + + path = make_relpath(target_abspath, target_path, path, pool, pool); /* For historic reasons svn ignores the property status for added nodes, even if these nodes were copied and have local property changes. @@ -222,6 +301,55 @@ print_status(const char *path, (*prop_conflicts)++; } + /* Note that moved-from and moved-to information is only available in STATUS + * for (op-)roots of a move. Those are exactly the nodes we want to show + * move info for in 'svn status'. See also comments in svn_wc_status3_t. */ + if (status->moved_from_abspath && status->moved_to_abspath && + strcmp(status->moved_from_abspath, status->moved_to_abspath) == 0) + { + const char *relpath; + + relpath = make_relpath(target_abspath, target_path, + status->moved_from_abspath, + pool, pool); + relpath = svn_dirent_local_style(relpath, pool); + moved_from_line = apr_pstrcat(pool, "\n > ", + apr_psprintf(pool, + _("swapped places with %s"), + relpath), + (char *)NULL); + } + else if (status->moved_from_abspath || status->moved_to_abspath) + { + const char *relpath; + + if (status->moved_from_abspath) + { + relpath = make_relpath(target_abspath, target_path, + status->moved_from_abspath, + pool, pool); + relpath = svn_dirent_local_style(relpath, pool); + moved_from_line = apr_pstrcat(pool, "\n > ", + apr_psprintf(pool, _("moved from %s"), + relpath), + (char *)NULL); + } + + if (status->moved_to_abspath) + { + relpath = make_relpath(target_abspath, target_path, + status->moved_to_abspath, + pool, pool); + relpath = svn_dirent_local_style(relpath, pool); + moved_to_line = apr_pstrcat(pool, "\n > ", + apr_psprintf(pool, _("moved to %s"), + relpath), + (char *)NULL); + } + } + + path = svn_dirent_local_style(path, pool); + if (detailed) { char ood_status, lock_status; @@ -284,7 +412,7 @@ print_status(const char *path, SVN_ERR (svn_cmdline_printf(pool, - "%c%c%c%c%c%c%c %c %6s %6s %-12s %s%s\n", + "%c%c%c%c%c%c%c %c %8s %8s %-12s %s%s%s%s\n", generate_status_code(combined_status(status)), generate_status_code(prop_status), status->wc_is_locked ? 'L' : ' ', @@ -297,11 +425,13 @@ print_status(const char *path, commit_rev, commit_author, path, + moved_to_line, + moved_from_line, tree_desc_line)); } else SVN_ERR( - svn_cmdline_printf(pool, "%c%c%c%c%c%c%c %c %6s %s%s\n", + svn_cmdline_printf(pool, "%c%c%c%c%c%c%c %c %8s %s%s%s%s\n", generate_status_code(combined_status(status)), generate_status_code(prop_status), status->wc_is_locked ? 'L' : ' ', @@ -312,11 +442,13 @@ print_status(const char *path, ood_status, working_rev, path, + moved_to_line, + moved_from_line, tree_desc_line)); } else SVN_ERR( - svn_cmdline_printf(pool, "%c%c%c%c%c%c%c %s%s\n", + svn_cmdline_printf(pool, "%c%c%c%c%c%c%c %s%s%s%s\n", generate_status_code(combined_status(status)), generate_status_code(prop_status), status->wc_is_locked ? 'L' : ' ', @@ -326,6 +458,8 @@ print_status(const char *path, ? 'K' : ' '), tree_status_code, path, + moved_to_line, + moved_from_line, tree_desc_line)); return svn_cmdline_fflush(stdout); @@ -333,12 +467,14 @@ print_status(const char *path, svn_error_t * -svn_cl__print_status_xml(const char *path, +svn_cl__print_status_xml(const char *target_abspath, + const char *target_path, + const char *path, const svn_client_status_t *status, svn_client_ctx_t *ctx, apr_pool_t *pool) { - svn_stringbuf_t *sb = svn_stringbuf_create("", pool); + svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool); apr_hash_t *att_hash; const char *local_abspath = status->local_abspath; svn_boolean_t tree_conflicted = FALSE; @@ -351,32 +487,54 @@ svn_cl__print_status_xml(const char *path, SVN_ERR(svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted, ctx->wc_ctx, local_abspath, pool)); + path = make_relpath(target_abspath, target_path, path, pool, pool); + svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry", "path", svn_dirent_local_style(path, pool), NULL); att_hash = apr_hash_make(pool); - apr_hash_set(att_hash, "item", APR_HASH_KEY_STRING, - generate_status_desc(combined_status(status))); - - apr_hash_set(att_hash, "props", APR_HASH_KEY_STRING, - generate_status_desc( - (status->node_status != svn_wc_status_deleted) - ? status->prop_status - : svn_wc_status_none)); + svn_hash_sets(att_hash, "item", + generate_status_desc(combined_status(status))); + + svn_hash_sets(att_hash, "props", + generate_status_desc( + (status->node_status != svn_wc_status_deleted) + ? status->prop_status + : svn_wc_status_none)); if (status->wc_is_locked) - apr_hash_set(att_hash, "wc-locked", APR_HASH_KEY_STRING, "true"); + svn_hash_sets(att_hash, "wc-locked", "true"); if (status->copied) - apr_hash_set(att_hash, "copied", APR_HASH_KEY_STRING, "true"); + svn_hash_sets(att_hash, "copied", "true"); if (status->switched) - apr_hash_set(att_hash, "switched", APR_HASH_KEY_STRING, "true"); + svn_hash_sets(att_hash, "switched", "true"); if (status->file_external) - apr_hash_set(att_hash, "file-external", APR_HASH_KEY_STRING, "true"); + svn_hash_sets(att_hash, "file-external", "true"); if (status->versioned && ! status->copied) - apr_hash_set(att_hash, "revision", APR_HASH_KEY_STRING, - apr_psprintf(pool, "%ld", status->revision)); + svn_hash_sets(att_hash, "revision", + apr_psprintf(pool, "%ld", status->revision)); if (tree_conflicted) - apr_hash_set(att_hash, "tree-conflicted", APR_HASH_KEY_STRING, - "true"); + svn_hash_sets(att_hash, "tree-conflicted", "true"); + if (status->moved_from_abspath || status->moved_to_abspath) + { + const char *relpath; + + if (status->moved_from_abspath) + { + relpath = make_relpath(target_abspath, target_path, + status->moved_from_abspath, + pool, pool); + relpath = svn_dirent_local_style(relpath, pool); + svn_hash_sets(att_hash, "moved-from", relpath); + } + if (status->moved_to_abspath) + { + relpath = make_relpath(target_abspath, target_path, + status->moved_to_abspath, + pool, pool); + relpath = svn_dirent_local_style(relpath, pool); + svn_hash_sets(att_hash, "moved-to", relpath); + } + } svn_xml_make_open_tag_hash(&sb, pool, svn_xml_normal, "wc-status", att_hash); @@ -416,8 +574,11 @@ svn_cl__print_status_xml(const char *path, /* Called by status-cmd.c */ svn_error_t * -svn_cl__print_status(const char *path, +svn_cl__print_status(const char *target_abspath, + const char *target_path, + const char *path, const svn_client_status_t *status, + svn_boolean_t suppress_externals_placeholders, svn_boolean_t detailed, svn_boolean_t show_last_committed, svn_boolean_t skip_unrecognized, @@ -437,7 +598,33 @@ svn_cl__print_status(const char *path, && status->repos_node_status == svn_wc_status_none)) return SVN_NO_ERROR; - return print_status(svn_dirent_local_style(path, pool), + /* If we're trying not to print boring "X /path/to/external" + lines..." */ + if (suppress_externals_placeholders) + { + /* ... skip regular externals unmodified in the repository. */ + if ((status->node_status == svn_wc_status_external) + && (status->repos_node_status == svn_wc_status_none) + && (! status->conflicted)) + return SVN_NO_ERROR; + + /* ... skip file externals that aren't modified locally or + remotely, changelisted, or locked (in either sense of the + word). */ + if ((status->file_external) + && (status->repos_node_status == svn_wc_status_none) + && ((status->node_status == svn_wc_status_normal) + || (status->node_status == svn_wc_status_none)) + && ((status->prop_status == svn_wc_status_normal) + || (status->prop_status == svn_wc_status_none)) + && (! status->changelist) + && (! status->lock) + && (! status->wc_is_locked) + && (! status->conflicted)) + return SVN_NO_ERROR; + } + + return print_status(target_abspath, target_path, path, detailed, show_last_committed, repos_locks, status, text_conflicts, prop_conflicts, tree_conflicts, ctx, pool); |