Author: rinrab
Date: Wed Apr  8 14:08:39 2026
New Revision: 1932903

Log:
svnbrowse: Support proper revision/target system in the command-line handling.

The model, when created, will accept a path_or_url parameter and, if a path is
supplied, go and get the URL from WC. It also receives a revision/peg_revision
as svn_opt_revision_t. The standard client framework will be used to resolve
those. If both of those revisions are HEAD, we'll directly read directories at
HEAD.

* subversion/svnbrowse/svnbrowse.h
  (svn_browse__opt_state_t): Add revision, peg_revision, and path_or_url to
   the struct.
  (svn_browse__model_create): Update function declaration.
* subversion/svnbrowse/model.c
  (includes): Update includes.
  (svn_browse__model_create): Update arguments that the function accepts, use
   svn_client__ra_session_from_path2() instead of svn_client_open_ra_session2()
   for initializing RA sessions, and use the revision/URL it resolves for the
   newly created model.
* subversion/svnbrowse/svnbrowse.c
  (sub_main): Handle all extra args with svn_client_args_to_target_array2() and
   extract "the target" from it (the array should only contain of those). Parse
   this target onto peg_revision and path_or_url using svn_opt_parse_path().

Modified:
   subversion/trunk/subversion/svnbrowse/model.c
   subversion/trunk/subversion/svnbrowse/svnbrowse.c
   subversion/trunk/subversion/svnbrowse/svnbrowse.h

Modified: subversion/trunk/subversion/svnbrowse/model.c
==============================================================================
--- subversion/trunk/subversion/svnbrowse/model.c       Wed Apr  8 14:08:31 
2026        (r1932902)
+++ subversion/trunk/subversion/svnbrowse/model.c       Wed Apr  8 14:08:39 
2026        (r1932903)
@@ -24,12 +24,14 @@
 #include <apr.h>
 
 #include "svn_client.h"
-#include "svn_cmdline.h"
 #include "svn_dirent_uri.h"
+#include "svn_path.h"
 #include "svn_ra.h"
 #include "svn_pools.h"
 #include "svn_error.h"
 
+#include "private/svn_client_private.h"
+
 #include "svnbrowse.h"
 
 svn_error_t *
@@ -130,36 +132,65 @@ svn_browse__model_move_selection(svn_bro
 svn_error_t *
 svn_browse__model_create(svn_browse__model_t **model_p,
                          svn_client_ctx_t *ctx,
-                         const char *url,
-                         svn_revnum_t revision,
+                         const char *path_or_url,
+                         const svn_opt_revision_t *peg_revision,
+                         const svn_opt_revision_t *revision,
                          apr_pool_t *result_pool,
                          apr_pool_t *scratch_pool)
 {
   svn_browse__model_t *model = apr_pcalloc(result_pool, sizeof(*model));
   svn_ra_session_t *session;
   apr_pool_t *state_pool;
-  svn_browse__state_t *state;
   const char *root, *relpath;
 
-  SVN_ERR(svn_client_open_ra_session2(&session, url, NULL, ctx, result_pool,
-                                      scratch_pool));
+  svn_opt_revision_t peg_rev = *peg_revision;
+  svn_opt_revision_t start_rev = *revision;
+  svn_client__pathrev_t *loc;
+
+  /* Default revisions: peg -> working or head; operative -> peg. */
+  SVN_ERR(svn_opt_resolve_revisions(&peg_rev, &start_rev,
+                                    svn_path_is_url(path_or_url),
+                                    TRUE /* notice_local_mods */,
+                                    scratch_pool));
+
+  SVN_ERR(svn_client__ra_session_from_path2(&session, &loc, path_or_url, NULL,
+                                            &peg_rev, &start_rev, ctx,
+                                            result_pool));
+
+  model->root = apr_pstrdup(result_pool, loc->repos_root_url);
+  model->client = ctx;
+  model->session = session;
 
-  SVN_ERR(svn_ra_get_repos_root2(session, &root, scratch_pool));
-  SVN_ERR(svn_ra_reparent(session, root, scratch_pool));
+  /* If the revision is ovbiously HEAD, we are not going to use the resolved
+   * revnum, but rely on svn_ra_get_dir2() doing that for us. It allows the
+   * browser to always browse on the latest revision and catch-up recent
+   * commits as session is active.
+   *
+   * Note: only head+head combination of peg_revision and revision should be
+   * considered as the "real" head. If we were to for example go to r123 and
+   * then resolve HEAD, its value may change as session progresses. This logic
+   * would be too complicated and rather confusing. That's why we will fallback
+   * to what the client is doing instead of reinventing the wheel.
+   *
+   * When invoked from a working copy, it's not HEAD either. It's should point
+   * to WORKING, i.e. revnum should not change. Again rescanning the working
+   * copy is possible but we assume it never changes.
+   */
+
+  if (peg_rev.kind == svn_opt_revision_head &&
+      start_rev.kind == svn_opt_revision_head)
+    model->revision = SVN_INVALID_REVNUM;
+  else
+    model->revision = loc->rev;
 
-  relpath = svn_uri_skip_ancestor(root, url, scratch_pool);
+  SVN_ERR(svn_ra_reparent(session, loc->repos_root_url, scratch_pool));
+
+  relpath = svn_uri_skip_ancestor(loc->repos_root_url, loc->url, scratch_pool);
 
   /* the state should be in a separate pool so it's safe to free it */
   state_pool = svn_pool_create(result_pool);
-  SVN_ERR(svn_browse__state_create(&state, session, relpath, revision,
-                                   state_pool, scratch_pool));
-
-  model->root = apr_pstrdup(result_pool, root);
-  model->revision = revision;
-  model->client = ctx;
-  model->session = session;
-  model->current = state;
-  model->pool = result_pool;
+  SVN_ERR(svn_browse__state_create(&model->current, session, relpath,
+                                   model->revision, state_pool, scratch_pool));
 
   *model_p = model;
   return SVN_NO_ERROR;

Modified: subversion/trunk/subversion/svnbrowse/svnbrowse.c
==============================================================================
--- subversion/trunk/subversion/svnbrowse/svnbrowse.c   Wed Apr  8 14:08:31 
2026        (r1932902)
+++ subversion/trunk/subversion/svnbrowse/svnbrowse.c   Wed Apr  8 14:08:39 
2026        (r1932903)
@@ -217,7 +217,6 @@ show_version(apr_pool_t *scratch_pool,
 static svn_error_t *
 sub_main(int *code, int argc, const char *argv[], apr_pool_t *pool)
 {
-  const char *url;
   svn_client_ctx_t *client;
   svn_auth_baton_t *auth;
   svn_browse__model_t *ctx;
@@ -226,6 +225,7 @@ sub_main(int *code, int argc, const char
   svn_boolean_t read_pass_from_stdin = FALSE;
   apr_pool_t *iterpool;
   apr_getopt_t *os;
+  apr_array_header_t *targets = NULL;
 
   opt_state.revision.kind = svn_opt_revision_head;
   opt_state.config_options =
@@ -313,20 +313,6 @@ sub_main(int *code, int argc, const char
       return SVN_NO_ERROR;
     }
 
-  /* TODO: WC paths are not implemented; svn_uri_canonicalize_safe() will just
-   * fail in case of one */
-  url = (os->ind < argc) ? os->argv[os->ind++] : ".";
-  SVN_ERR(svn_uri_canonicalize_safe(&url, NULL, url, pool, pool));
-
-  /* we must fail if there are extra arguments */
-  if (os->ind < argc - 1)
-    {
-      printf("%d\n", os->ind);
-      *code = EXIT_FAILURE;
-      SVN_ERR(show_usage(pool));
-      return SVN_NO_ERROR;
-    }
-
   SVN_ERR(svn_config_ensure(opt_state.config_dir, pool));
 
   /* Set up Authentication stuff. */
@@ -348,8 +334,23 @@ sub_main(int *code, int argc, const char
   SVN_ERR(svn_client_create_context2(&client, NULL, pool));
   client->auth_baton = auth;
 
-  SVN_ERR(svn_browse__model_create(&ctx, client, url, SVN_INVALID_REVNUM, pool,
-                                   pool));
+  SVN_ERR(svn_client_args_to_target_array2(&targets, os, NULL, client, FALSE,
+                                           pool));
+
+  /* we must fail if there are extra arguments */
+  if (targets->nelts != 1)
+    {
+      *code = EXIT_FAILURE;
+      SVN_ERR(show_usage(pool));
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(svn_opt_parse_path(&opt_state.peg_revision, &opt_state.path_or_url,
+                             APR_ARRAY_IDX(targets, 0, const char *), pool));
+
+  SVN_ERR(svn_browse__model_create(&ctx, client, opt_state.path_or_url,
+                                   &opt_state.peg_revision,
+                                   &opt_state.revision, pool, pool));
 
   /* init the display */
   initscr();

Modified: subversion/trunk/subversion/svnbrowse/svnbrowse.h
==============================================================================
--- subversion/trunk/subversion/svnbrowse/svnbrowse.h   Wed Apr  8 14:08:31 
2026        (r1932902)
+++ subversion/trunk/subversion/svnbrowse/svnbrowse.h   Wed Apr  8 14:08:39 
2026        (r1932903)
@@ -48,11 +48,12 @@ typedef struct svn_browse__opt_state_t {
 
   const char *auth_username;     /* auth username */
   const char *auth_password;     /* auth password */
-  apr_array_header_t *targets;   /* target list from file */
   svn_boolean_t no_auth_cache;   /* do not cache authentication information */
   const char *config_dir;        /* over-riding configuration directory */
   apr_array_header_t *config_options; /* over-riding configuration options */
-  svn_opt_revision_t revision;
+  svn_opt_revision_t revision;   /* --revision */
+  const char *path_or_url;
+  svn_opt_revision_t peg_revision; /* @PEGREV*/
 
   /* trust server SSL certs that would otherwise be rejected as "untrusted" */
   svn_boolean_t trust_server_cert_unknown_ca;
@@ -125,7 +126,8 @@ svn_browse__model_move_selection(svn_bro
 svn_error_t *
 svn_browse__model_create(svn_browse__model_t **model_p,
                          svn_client_ctx_t *ctx,
-                         const char *url,
-                         svn_revnum_t revision,
+                         const char *path_or_url,
+                         const svn_opt_revision_t *peg_revision,
+                         const svn_opt_revision_t *revision,
                          apr_pool_t *result_pool,
                          apr_pool_t *scratch_pool);

Reply via email to