static void do_one_diff(apr_file_t *source_file, apr_file_t *target_file, int *count, apr_off_t *len, int quiet, apr_pool_t *pool, const char *tag, FILE* stream) { svn_txdelta_stream_t *delta_stream = NULL; svn_txdelta_window_t *delta_window = NULL; apr_pool_t *fpool = svn_pool_create(pool); apr_pool_t *wpool = svn_pool_create(pool); *count = 0; *len = 0; svn_txdelta(&delta_stream, svn_stream_from_aprfile(source_file, fpool), svn_stream_from_aprfile(target_file, fpool), fpool); do { svn_error_t *err; err = svn_txdelta_next_window(&delta_window, delta_stream, wpool); if (err) svn_handle_error2(err, stderr, TRUE, "vdelta-test: "); if (delta_window != NULL) { *len += print_delta_window(delta_window, tag, quiet, stream); svn_pool_clear(wpool); ++*count; } } while (delta_window != NULL); fprintf(stream, "%s: (LENGTH %" APR_OFF_T_FMT " +%d)\n", tag, *len, *count); svn_pool_destroy(fpool); svn_pool_destroy(wpool); }
void Client::get(Path & dstPath, const Path & path, const Revision & revision, const Revision & peg_revision) throw(ClientException) { Pool pool; // create a new file and suppose we only want // this users to be able to read and write the file apr_file_t * file = openTempFile(dstPath, path, revision, pool); // now create a stream and let svn_client_cat write to the // stream svn_stream_t * stream = svn_stream_from_aprfile(file, pool); if (stream != 0) { svn_error_t * error = svn_client_cat2( stream, path.c_str(), peg_revision.revision() , revision.revision(), *m_context, pool); if (error != 0) throw ClientException(error); svn_stream_close(stream); } // finalize stuff apr_file_close(file); }
int main(int argc, char **argv) { apr_file_t *source_file_A = NULL; apr_file_t *target_file_A = NULL; int count_A = 0; apr_off_t len_A = 0; apr_file_t *source_file_B = NULL; apr_file_t *target_file_B = NULL; int count_B = 0; apr_off_t len_B = 0; apr_pool_t *pool; int quiet = 0; if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'q') { quiet = 1; --argc; ++argv; } apr_initialize(); pool = svn_pool_create(NULL); if (argc == 2) { target_file_A = open_binary_read(argv[1], pool); } else if (argc == 3) { source_file_A = open_binary_read(argv[1], pool); target_file_A = open_binary_read(argv[2], pool); } else if (argc == 4) { source_file_A = open_binary_read(argv[1], pool); target_file_A = open_binary_read(argv[2], pool); source_file_B = open_binary_read(argv[2], pool); target_file_B = open_binary_read(argv[3], pool); } else { fprintf(stderr, "Usage: vdelta-test [-q] <target>\n" " or: vdelta-test [-q] <source> <target>\n" " or: vdelta-test [-q] <source> <intermediate> <target>\n"); exit(1); } do_one_diff(source_file_A, target_file_A, &count_A, &len_A, quiet, pool, "A ", stdout); if (source_file_B) { apr_pool_t *fpool = svn_pool_create(pool); apr_pool_t *wpool = svn_pool_create(pool); svn_txdelta_stream_t *stream_A = NULL; svn_txdelta_stream_t *stream_B = NULL; svn_txdelta_window_t *window_A = NULL; svn_txdelta_window_t *window_B = NULL; svn_txdelta_window_t *window_AB = NULL; int count_AB = 0; apr_off_t len_AB = 0; putc('\n', stdout); do_one_diff(source_file_B, target_file_B, &count_B, &len_B, quiet, pool, "B ", stdout); putc('\n', stdout); { apr_off_t offset = 0; apr_file_seek(source_file_A, APR_SET, &offset); apr_file_seek(target_file_A, APR_SET, &offset); apr_file_seek(source_file_B, APR_SET, &offset); apr_file_seek(target_file_B, APR_SET, &offset); } svn_txdelta(&stream_A, svn_stream_from_aprfile(source_file_A, fpool), svn_stream_from_aprfile(target_file_A, fpool), fpool); svn_txdelta(&stream_B, svn_stream_from_aprfile(source_file_B, fpool), svn_stream_from_aprfile(target_file_B, fpool), fpool); for (count_AB = 0; count_AB < count_B; ++count_AB) { svn_error_t *err; err = svn_txdelta_next_window(&window_A, stream_A, wpool); if (err) svn_handle_error2(err, stderr, TRUE, "vdelta-test: "); err = svn_txdelta_next_window(&window_B, stream_B, wpool); if (err) svn_handle_error2(err, stderr, TRUE, "vdelta-test: "); /* Note: It's not possible that window_B is null, we already counted the number of windows in the second delta. */ assert(window_A != NULL || window_B->src_ops == 0); if (window_B->src_ops == 0) { window_AB = window_B; window_AB->sview_len = 0; } else window_AB = svn_txdelta_compose_windows(window_A, window_B, wpool); len_AB += print_delta_window(window_AB, "AB", quiet, stdout); svn_pool_clear(wpool); } fprintf(stdout, "AB: (LENGTH %" APR_OFF_T_FMT " +%d)\n", len_AB, count_AB); } if (source_file_A) apr_file_close(source_file_A); if (target_file_A) apr_file_close(target_file_A); if (source_file_B) apr_file_close(source_file_B); if (target_file_B) apr_file_close(source_file_B); svn_pool_destroy(pool); apr_terminate(); exit(0); }
svn_error_t * svn_client_blame4(const char *target, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *start, const svn_opt_revision_t *end, const svn_diff_file_options_t *diff_options, svn_boolean_t ignore_mime_type, svn_boolean_t include_merged_revisions, svn_client_blame_receiver2_t receiver, void *receiver_baton, svn_client_ctx_t *ctx, apr_pool_t *pool) { struct file_rev_baton frb; svn_ra_session_t *ra_session; const char *url; svn_revnum_t start_revnum, end_revnum; struct blame *walk, *walk_merged = NULL; apr_file_t *file; apr_pool_t *iterpool; svn_stream_t *stream; if (start->kind == svn_opt_revision_unspecified || end->kind == svn_opt_revision_unspecified) return svn_error_create (SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL); else if (start->kind == svn_opt_revision_working || end->kind == svn_opt_revision_working) return svn_error_create (SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("blame of the WORKING revision is not supported")); /* Get an RA plugin for this filesystem object. */ SVN_ERR(svn_client__ra_session_from_path(&ra_session, &end_revnum, &url, target, NULL, peg_revision, end, ctx, pool)); SVN_ERR(svn_client__get_revision_number(&start_revnum, NULL, ra_session, start, target, pool)); if (end_revnum < start_revnum) return svn_error_create (SVN_ERR_CLIENT_BAD_REVISION, NULL, _("Start revision must precede end revision")); frb.start_rev = start_revnum; frb.end_rev = end_revnum; frb.target = target; frb.ctx = ctx; frb.diff_options = diff_options; frb.ignore_mime_type = ignore_mime_type; frb.include_merged_revisions = include_merged_revisions; frb.last_filename = NULL; frb.last_original_filename = NULL; frb.chain = apr_palloc(pool, sizeof(*frb.chain)); frb.chain->blame = NULL; frb.chain->avail = NULL; frb.chain->pool = pool; if (include_merged_revisions) { frb.merged_chain = apr_palloc(pool, sizeof(*frb.merged_chain)); frb.merged_chain->blame = NULL; frb.merged_chain->avail = NULL; frb.merged_chain->pool = pool; } SVN_ERR(svn_io_temp_dir(&frb.tmp_path, pool)); frb.tmp_path = svn_path_join(frb.tmp_path, "tmp", pool), frb.mainpool = pool; /* The callback will flip the following two pools, because it needs information from the previous call. Obviously, it can't rely on the lifetime of the pool provided by get_file_revs. */ frb.lastpool = svn_pool_create(pool); frb.currpool = svn_pool_create(pool); if (include_merged_revisions) { frb.filepool = svn_pool_create(pool); frb.prevfilepool = svn_pool_create(pool); } /* Collect all blame information. We need to ensure that we get one revision before the start_rev, if available so that we can know what was actually changed in the start revision. */ SVN_ERR(svn_ra_get_file_revs2(ra_session, "", start_revnum - (start_revnum > 0 ? 1 : 0), end_revnum, include_merged_revisions, file_rev_handler, &frb, pool)); /* Report the blame to the caller. */ /* The callback has to have been called at least once. */ assert(frb.last_filename != NULL); /* Create a pool for the iteration below. */ iterpool = svn_pool_create(pool); /* Open the last file and get a stream. */ SVN_ERR(svn_io_file_open(&file, frb.last_filename, APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool)); stream = svn_subst_stream_translated(svn_stream_from_aprfile(file, pool), "\n", TRUE, NULL, FALSE, pool); /* Perform optional merged chain normalization. */ if (include_merged_revisions) { /* If we never created any blame for the original chain, create it now, with the most recent changed revision. This could occur if a file was created on a branch and them merged to another branch. This is semanticly a copy, and we want to use the revision on the branch as the most recently changed revision. ### Is this really what we want to do here? Do the sematics of copy change? */ if (!frb.chain->blame) frb.chain->blame = blame_create(frb.chain, frb.rev, 0); normalize_blames(frb.chain, frb.merged_chain, pool); walk_merged = frb.merged_chain->blame; } /* Process each blame item. */ for (walk = frb.chain->blame; walk; walk = walk->next) { apr_off_t line_no; svn_revnum_t merged_rev; const char *merged_author, *merged_date, *merged_path; if (walk_merged) { merged_rev = walk_merged->rev->revision; merged_author = walk_merged->rev->author; merged_date = walk_merged->rev->date; merged_path = walk_merged->rev->path; } else { merged_rev = SVN_INVALID_REVNUM; merged_author = NULL; merged_date = NULL; merged_path = NULL; } for (line_no = walk->start; !walk->next || line_no < walk->next->start; ++line_no) { svn_boolean_t eof; svn_stringbuf_t *sb; svn_pool_clear(iterpool); SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, iterpool)); if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); if (!eof || sb->len) SVN_ERR(receiver(receiver_baton, line_no, walk->rev->revision, walk->rev->author, walk->rev->date, merged_rev, merged_author, merged_date, merged_path, sb->data, iterpool)); if (eof) break; } if (walk_merged) walk_merged = walk_merged->next; } SVN_ERR(svn_stream_close(stream)); /* We don't need the temp file any more. */ SVN_ERR(svn_io_file_close(file, pool)); svn_pool_destroy(frb.lastpool); svn_pool_destroy(frb.currpool); if (include_merged_revisions) { svn_pool_destroy(frb.filepool); svn_pool_destroy(frb.prevfilepool); } svn_pool_destroy(iterpool); return SVN_NO_ERROR; }
static svn_error_t * file_rev_handler(void *baton, const char *path, svn_revnum_t revnum, apr_hash_t *rev_props, svn_boolean_t merged_revision, svn_txdelta_window_handler_t *content_delta_handler, void **content_delta_baton, apr_array_header_t *prop_diffs, apr_pool_t *pool) { struct file_rev_baton *frb = baton; svn_stream_t *last_stream; svn_stream_t *cur_stream; struct delta_baton *delta_baton; apr_pool_t *filepool; /* Clear the current pool. */ svn_pool_clear(frb->currpool); /* If this file has a non-textual mime-type, bail out. */ if (! frb->ignore_mime_type) SVN_ERR(check_mimetype(prop_diffs, frb->target, frb->currpool)); if (frb->ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(path, svn_wc_notify_blame_revision, pool); notify->kind = svn_node_none; notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; notify->revision = revnum; frb->ctx->notify_func2(frb->ctx->notify_baton2, notify, pool); } if (frb->ctx->cancel_func) SVN_ERR(frb->ctx->cancel_func(frb->ctx->cancel_baton)); /* If there were no content changes, we couldn't care less about this revision now. Note that we checked the mime type above, so things work if the user just changes the mime type in a commit. Also note that we don't switch the pools in this case. This is important, since the tempfile will be removed by the pool and we need the tempfile from the last revision with content changes. */ if (!content_delta_handler) return SVN_NO_ERROR; frb->merged_revision = merged_revision; /* Create delta baton. */ delta_baton = apr_palloc(frb->currpool, sizeof(*delta_baton)); /* Prepare the text delta window handler. */ if (frb->last_filename) SVN_ERR(svn_io_file_open(&delta_baton->source_file, frb->last_filename, APR_READ, APR_OS_DEFAULT, frb->currpool)); else /* Means empty stream below. */ delta_baton->source_file = NULL; last_stream = svn_stream_from_aprfile(delta_baton->source_file, pool); if (frb->include_merged_revisions && !frb->merged_revision) filepool = frb->filepool; else filepool = frb->currpool; SVN_ERR(svn_io_open_unique_file2(&delta_baton->file, &delta_baton->filename, frb->tmp_path, ".tmp", svn_io_file_del_on_pool_cleanup, filepool)); cur_stream = svn_stream_from_aprfile(delta_baton->file, frb->currpool); /* Get window handler for applying delta. */ svn_txdelta_apply(last_stream, cur_stream, NULL, NULL, frb->currpool, &delta_baton->wrapped_handler, &delta_baton->wrapped_baton); /* Wrap the window handler with our own. */ delta_baton->file_rev_baton = frb; *content_delta_handler = window_handler; *content_delta_baton = delta_baton; /* Create the rev structure. */ frb->rev = apr_palloc(frb->mainpool, sizeof(struct rev)); if (revnum < frb->start_rev) { /* We shouldn't get more than one revision before start. */ assert(frb->last_filename == NULL); /* The file existed before start_rev; generate no blame info for lines from this revision (or before). */ frb->rev->revision = SVN_INVALID_REVNUM; frb->rev->author = NULL; frb->rev->date = NULL; } else { svn_string_t *str; assert(revnum <= frb->end_rev); /* Set values from revision props. */ frb->rev->revision = revnum; if ((str = apr_hash_get(rev_props, SVN_PROP_REVISION_AUTHOR, sizeof(SVN_PROP_REVISION_AUTHOR) - 1))) frb->rev->author = apr_pstrdup(frb->mainpool, str->data); else frb->rev->author = NULL; if ((str = apr_hash_get(rev_props, SVN_PROP_REVISION_DATE, sizeof(SVN_PROP_REVISION_DATE) - 1))) frb->rev->date = apr_pstrdup(frb->mainpool, str->data); else frb->rev->date = NULL; } if (frb->include_merged_revisions) frb->rev->path = apr_pstrdup(frb->mainpool, path); return SVN_NO_ERROR; }
int main(int argc, char **argv) { svn_error_t *err; apr_status_t apr_err; apr_file_t *source_file; apr_file_t *target_file; svn_stream_t *stdout_stream; svn_txdelta_stream_t *txdelta_stream; svn_txdelta_window_handler_t svndiff_handler; svn_stream_t *encoder; void *svndiff_baton; apr_pool_t *pool; int version = 0; if (argc < 3) { printf("usage: %s source target [version]\n", argv[0]); exit(0); } apr_initialize(); pool = svn_pool_create(NULL); apr_err = apr_file_open(&source_file, argv[1], (APR_READ | APR_BINARY), APR_OS_DEFAULT, pool); if (apr_err) { fprintf(stderr, "unable to open \"%s\" for reading\n", argv[1]); exit(1); } apr_err = apr_file_open(&target_file, argv[2], (APR_READ | APR_BINARY), APR_OS_DEFAULT, pool); if (apr_err) { fprintf(stderr, "unable to open \"%s\" for reading\n", argv[2]); exit(1); } if (argc == 4) version = atoi(argv[3]); svn_txdelta(&txdelta_stream, svn_stream_from_aprfile(source_file, pool), svn_stream_from_aprfile(target_file, pool), pool); err = svn_stream_for_stdout(&stdout_stream, pool); if (err) svn_handle_error2(err, stdout, TRUE, "svndiff-test: "); #ifdef QUOPRINT_SVNDIFFS encoder = svn_quoprint_encode(stdout_stream, pool); #else encoder = svn_base64_encode(stdout_stream, pool); #endif /* use maximum compression level */ svn_txdelta_to_svndiff3(&svndiff_handler, &svndiff_baton, encoder, version, 9, pool); err = svn_txdelta_send_txstream(txdelta_stream, svndiff_handler, svndiff_baton, pool); if (err) svn_handle_error2(err, stdout, TRUE, "svndiff-test: "); apr_file_close(source_file); apr_file_close(target_file); svn_pool_destroy(pool); apr_terminate(); exit(0); }
svn_error_t * svn_ra_blame (svn_ra_plugin_t *ra_lib, void *ra_session, const char *target, svn_revnum_t start_revnum, svn_revnum_t end_revnum, svn_client_blame_receiver_t receiver, void *receiver_baton, apr_pool_t *pool) { struct file_rev_baton frb; struct blame *walk; apr_file_t *tempfile; apr_pool_t *iterpool; svn_stream_t *stream; svn_error_t *err; // SVN_INVALID_REVNUM (=head revision) if ((end_revnum < start_revnum) && (end_revnum != SVN_INVALID_REVNUM)) return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, ("Start revision must precede end revision")); frb.start_rev = start_revnum; frb.end_rev = end_revnum; frb.target = target; frb.last_filename = NULL; frb.blame = NULL; frb.avail = NULL; frb.mainpool = pool; /* The callback will flip the following two pools, because it needs information from the previous call. Obviously, it can't rely on the lifetime of the pool provided by get_file_revs. */ frb.lastpool = svn_pool_create (pool); frb.currpool = svn_pool_create (pool); // do the actual blame err = do_blame (target, ra_lib, ra_session, &frb); if (err) return err; /* Report the blame to the caller. */ /* The callback has to have been called at least once. */ assert (frb.last_filename != NULL); /* Create a pool for the iteration below. */ iterpool = svn_pool_create (pool); /* Open the last file and get a stream. */ err = svn_io_file_open (&tempfile, frb.last_filename, APR_READ, APR_OS_DEFAULT, pool); if (err) return err; stream = svn_stream_from_aprfile(tempfile, pool); /* Process each blame item. */ for (walk = frb.blame; walk; walk = walk->next) { apr_off_t line_no; for (line_no = walk->start; !walk->next || line_no < walk->next->start; ++line_no) { svn_boolean_t eof; svn_stringbuf_t *sb; apr_pool_clear (iterpool); err = svn_stream_readline (stream, &sb, "\n", &eof, iterpool); if (err) return err; if (!eof || sb->len) SVN_ERR (receiver (receiver_baton, line_no, walk->rev->revision, walk->rev->author, walk->rev->date, sb->data, iterpool)); if (eof) break; } } err = svn_stream_close (stream); err = svn_io_file_close(tempfile, pool); svn_pool_destroy(frb.lastpool); svn_pool_destroy(frb.currpool); svn_pool_destroy(iterpool); return SVN_NO_ERROR; }
/* (Note: *LAST_SEED is an output parameter.) */ static svn_error_t * do_random_combine_test(const char **msg, svn_boolean_t msg_only, apr_pool_t *pool, apr_uint32_t *last_seed) { static char msg_buff[256]; apr_uint32_t seed, bytes_range, maxlen; int i, iterations, dump_files, print_windows; const char *random_bytes; /* Initialize parameters and print out the seed in case we dump core or something. */ init_params(&seed, &maxlen, &iterations, &dump_files, &print_windows, &random_bytes, &bytes_range, pool); sprintf(msg_buff, "random combine delta test, seed = %lu", (unsigned long) seed); *msg = msg_buff; if (msg_only) return SVN_NO_ERROR; else printf("SEED: %s\n", msg_buff); for (i = 0; i < iterations; i++) { /* Generate source and target for the delta and its application. */ apr_uint32_t subseed_base = svn_test_rand((*last_seed = seed, &seed)); apr_file_t *source = generate_random_file(maxlen, subseed_base, &seed, random_bytes, bytes_range, dump_files, pool); apr_file_t *middle = generate_random_file(maxlen, subseed_base, &seed, random_bytes, bytes_range, dump_files, pool); apr_file_t *target = generate_random_file(maxlen, subseed_base, &seed, random_bytes, bytes_range, dump_files, pool); apr_file_t *source_copy = copy_tempfile(source, pool); apr_file_t *middle_copy = copy_tempfile(middle, pool); apr_file_t *target_regen = open_tempfile(NULL, pool); svn_txdelta_stream_t *txdelta_stream_A; svn_txdelta_stream_t *txdelta_stream_B; svn_txdelta_window_handler_t handler; svn_stream_t *stream; void *handler_baton; /* Set up a four-stage pipeline: create two deltas, combine them and convert the result to svndiff format, parse that back into delta format, and apply it to a copy of the source file to see if we get the same target back. */ apr_pool_t *delta_pool = svn_pool_create(pool); /* Make stage 4: apply the text delta. */ svn_txdelta_apply(svn_stream_from_aprfile(source_copy, delta_pool), svn_stream_from_aprfile(target_regen, delta_pool), NULL, NULL, delta_pool, &handler, &handler_baton); /* Make stage 3: reparse the text delta. */ stream = svn_txdelta_parse_svndiff(handler, handler_baton, TRUE, delta_pool); /* Make stage 2: encode the text delta in svndiff format. */ svn_txdelta_to_svndiff2(&handler, &handler_baton, stream, 1, delta_pool); /* Make stage 1: create the text deltas. */ svn_txdelta(&txdelta_stream_A, svn_stream_from_aprfile(source, delta_pool), svn_stream_from_aprfile(middle, delta_pool), delta_pool); svn_txdelta(&txdelta_stream_B, svn_stream_from_aprfile(middle_copy, delta_pool), svn_stream_from_aprfile(target, delta_pool), delta_pool); { svn_txdelta_window_t *window_A; svn_txdelta_window_t *window_B; svn_txdelta_window_t *composite; apr_pool_t *wpool = svn_pool_create(delta_pool); do { SVN_ERR(svn_txdelta_next_window(&window_A, txdelta_stream_A, wpool)); if (print_windows) delta_window_print(window_A, "A ", stdout); SVN_ERR(svn_txdelta_next_window(&window_B, txdelta_stream_B, wpool)); if (print_windows) delta_window_print(window_B, "B ", stdout); if (!window_B) break; assert(window_A != NULL || window_B->src_ops == 0); if (window_B->src_ops == 0) { composite = window_B; composite->sview_len = 0; } else composite = svn_txdelta_compose_windows(window_A, window_B, wpool); if (print_windows) delta_window_print(composite, "AB", stdout); /* The source view length should not be 0 if there are source copy ops in the window. */ if (composite && composite->sview_len == 0 && composite->src_ops > 0) return svn_error_create (SVN_ERR_FS_GENERAL, NULL, "combined delta window is inconsistent"); SVN_ERR(handler(composite, handler_baton)); svn_pool_clear(wpool); } while (composite != NULL); svn_pool_destroy(wpool); } svn_pool_destroy(delta_pool); SVN_ERR(compare_files(target, target_regen, dump_files)); apr_file_close(source); apr_file_close(middle); apr_file_close(target); apr_file_close(source_copy); apr_file_close(middle_copy); apr_file_close(target_regen); } return SVN_NO_ERROR; }
static svn_error_t * do_blame (const char *target, svn_ra_plugin_t *ra_lib, void *ra_session, struct file_rev_baton *frb) { struct log_message_baton lmb; apr_array_header_t *condensed_targets; apr_file_t *file; svn_stream_t *stream; struct rev *rev; svn_node_kind_t kind; apr_pool_t *pool = frb->mainpool; SVN_ERR (ra_lib->check_path (ra_session, target, frb->end_rev, &kind, pool)); if (kind == svn_node_dir) return svn_error_createf (SVN_ERR_CLIENT_IS_DIRECTORY, NULL, ("URL '%s' refers to a directory"), target); condensed_targets = apr_array_make (pool, 1, sizeof (const char *)); (*((const char **)apr_array_push (condensed_targets))) = ""; lmb.path = apr_pstrcat(pool, "/", target, NULL); lmb.eldest = NULL; lmb.pool = pool; /* Accumulate revision metadata by walking the revisions backwards; this allows us to follow moves/copies correctly. */ SVN_ERR (ra_lib->get_log (ra_session, condensed_targets, frb->end_rev, frb->start_rev, TRUE, FALSE, log_message_receiver, &lmb, pool)); /* Inspect the first revision's change metadata; if there are any prior revisions, compute a new starting revision/path. If no revisions were selected, no blame is assigned. A modified item certainly has a prior revision. It is reasonable for an added item to have none, but anything else is unexpected. */ if (!lmb.eldest) { lmb.eldest = apr_palloc (pool, sizeof (*rev)); lmb.eldest->revision = frb->end_rev; lmb.eldest->path = lmb.path; lmb.eldest->next = NULL; rev = apr_palloc (pool, sizeof (*rev)); rev->revision = SVN_INVALID_REVNUM; rev->author = NULL; rev->date = NULL; frb->blame = blame_create (frb, rev, 0); } else if (lmb.action == 'M' || SVN_IS_VALID_REVNUM (lmb.copyrev)) { rev = apr_palloc (pool, sizeof (*rev)); if (SVN_IS_VALID_REVNUM (lmb.copyrev)) rev->revision = lmb.copyrev; else rev->revision = lmb.eldest->revision - 1; rev->path = lmb.path; rev->next = lmb.eldest; lmb.eldest = rev; rev = apr_palloc (pool, sizeof (*rev)); rev->revision = SVN_INVALID_REVNUM; rev->author = NULL; rev->date = NULL; frb->blame = blame_create (frb, rev, 0); } else if (lmb.action == 'A') { frb->blame = blame_create (frb, lmb.eldest, 0); } else return svn_error_createf (APR_EGENERAL, NULL, ("Revision action '%c' for " "revision %ld of '%s' " "lacks a prior revision"), lmb.action, lmb.eldest->revision, lmb.eldest->path); /* Walk the revision list in chronological order, downloading each fulltext, diffing it with its predecessor, and accumulating the blame information into db.blame. Use two iteration pools rather than one, because the diff routines need to look at a sliding window of revisions. Two pools gives us a ring buffer of sorts. */ for (rev = lmb.eldest; rev; rev = rev->next) { const char *tmp; const char *temp_dir; apr_hash_t *props; svn_string_t *mimetype; apr_pool_clear (frb->currpool); SVN_ERR (svn_io_temp_dir (&temp_dir, frb->currpool)); SVN_ERR (svn_io_open_unique_file (&file, &tmp, svn_path_join (temp_dir, "tmp", frb->currpool), ".tmp", FALSE, frb->currpool)); apr_pool_cleanup_register (frb->currpool, file, cleanup_tempfile, apr_pool_cleanup_null); stream = svn_stream_from_aprfile (file, frb->currpool); SVN_ERR (ra_lib->get_file(ra_session, rev->path + 1, rev->revision, stream, NULL, &props, frb->currpool)); SVN_ERR (svn_stream_close (stream)); SVN_ERR (svn_io_file_close (file, frb->currpool)); /* If this file has a non-textual mime-type, bail out. */ if (props && ((mimetype = apr_hash_get (props, SVN_PROP_MIME_TYPE, sizeof (SVN_PROP_MIME_TYPE) - 1)))) { if (svn_mime_type_is_binary (mimetype->data)) return svn_error_createf (SVN_ERR_CLIENT_IS_BINARY_FILE, 0, ("Cannot calculate blame information for binary file '%s'"),target); } if (frb->last_filename) { frb->rev = rev; SVN_ERR (add_file_blame (frb->last_filename, tmp, frb)); } frb->last_filename = tmp; { apr_pool_t *tmppool = frb->currpool; frb->currpool = frb->lastpool; frb->lastpool = tmppool; } } return SVN_NO_ERROR; }
/* Implements svn_test_driver_t. */ static svn_error_t * random_test(const char **msg, svn_boolean_t msg_only, svn_test_opts_t *opts, apr_pool_t *pool) { static char msg_buff[256]; apr_uint32_t seed, bytes_range, maxlen; int i, iterations, dump_files, print_windows; const char *random_bytes; /* Initialize parameters and print out the seed in case we dump core or something. */ init_params(&seed, &maxlen, &iterations, &dump_files, &print_windows, &random_bytes, &bytes_range, pool); sprintf(msg_buff, "random delta test, seed = %lu", (unsigned long) seed); *msg = msg_buff; if (msg_only) return SVN_NO_ERROR; else printf("SEED: %s\n", msg_buff); for (i = 0; i < iterations; i++) { /* Generate source and target for the delta and its application. */ apr_uint32_t subseed_base = svn_test_rand(&seed); apr_file_t *source = generate_random_file(maxlen, subseed_base, &seed, random_bytes, bytes_range, dump_files, pool); apr_file_t *target = generate_random_file(maxlen, subseed_base, &seed, random_bytes, bytes_range, dump_files, pool); apr_file_t *source_copy = copy_tempfile(source, pool); apr_file_t *target_regen = open_tempfile(NULL, pool); svn_txdelta_stream_t *txdelta_stream; svn_txdelta_window_handler_t handler; svn_stream_t *stream; void *handler_baton; /* Set up a four-stage pipeline: create a delta, convert it to svndiff format, parse it back into delta format, and apply it to a copy of the source file to see if we get the same target back. */ apr_pool_t *delta_pool = svn_pool_create(pool); /* Make stage 4: apply the text delta. */ svn_txdelta_apply(svn_stream_from_aprfile(source_copy, delta_pool), svn_stream_from_aprfile(target_regen, delta_pool), NULL, NULL, delta_pool, &handler, &handler_baton); /* Make stage 3: reparse the text delta. */ stream = svn_txdelta_parse_svndiff(handler, handler_baton, TRUE, delta_pool); /* Make stage 2: encode the text delta in svndiff format. */ svn_txdelta_to_svndiff2(&handler, &handler_baton, stream, 1, delta_pool); /* Make stage 1: create the text delta. */ svn_txdelta(&txdelta_stream, svn_stream_from_aprfile(source, delta_pool), svn_stream_from_aprfile(target, delta_pool), delta_pool); SVN_ERR(svn_txdelta_send_txstream(txdelta_stream, handler, handler_baton, delta_pool)); svn_pool_destroy(delta_pool); SVN_ERR(compare_files(target, target_regen, dump_files)); apr_file_close(source); apr_file_close(target); apr_file_close(source_copy); apr_file_close(target_regen); } return SVN_NO_ERROR; }
int main (int argc, const char **argv) { apr_pool_t *pool; svn_error_t *err; apr_hash_t *dirents; const char *upload_file, *URL; const char *parent_URL, *basename; svn_ra_plugin_t *ra_lib; void *session, *ra_baton; svn_revnum_t rev; const svn_delta_editor_t *editor; void *edit_baton; svn_dirent_t *dirent; svn_ra_callbacks_t *cbtable; apr_hash_t *cfg_hash; svn_auth_baton_t *auth_baton; if (argc <= 2) { printf ("Usage: %s PATH URL\n", argv[0]); printf (" Uploads file at PATH to Subversion repository URL.\n"); return EXIT_FAILURE; } upload_file = argv[1]; URL = argv[2]; /* Initialize the app. Send all error messages to 'stderr'. */ if (svn_cmdline_init ("minimal_client", stderr) != EXIT_SUCCESS) return EXIT_FAILURE; /* Create top-level memory pool. Be sure to read the HACKING file to understand how to properly use/free subpools. */ pool = svn_pool_create (NULL); /* Initialize the FS library. */ err = svn_fs_initialize (pool); if (err) goto hit_error; /* Make sure the ~/.subversion run-time config files exist, and load. */ err = svn_config_ensure (NULL, pool); if (err) goto hit_error; err = svn_config_get_config (&cfg_hash, NULL, pool); if (err) goto hit_error; /* Build an authentication baton. */ { /* There are many different kinds of authentication back-end "providers". See svn_auth.h for a full overview. */ svn_auth_provider_object_t *provider; apr_array_header_t *providers = apr_array_make (pool, 4, sizeof (svn_auth_provider_object_t *)); svn_client_get_simple_prompt_provider (&provider, my_simple_prompt_callback, NULL, /* baton */ 2, /* retry limit */ pool); APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider; svn_client_get_username_prompt_provider (&provider, my_username_prompt_callback, NULL, /* baton */ 2, /* retry limit */ pool); APR_ARRAY_PUSH (providers, svn_auth_provider_object_t *) = provider; /* Register the auth-providers into the context's auth_baton. */ svn_auth_open (&auth_baton, providers, pool); } /* Create a table of callbacks for the RA session, mostly nonexistent. */ cbtable = apr_pcalloc (pool, sizeof(*cbtable)); cbtable->auth_baton = auth_baton; cbtable->open_tmp_file = open_tmp_file; /* Now do the real work. */ /* Open an RA session to the parent URL, fetch current HEAD rev and "lock" onto that revnum for the remainder of the session. */ svn_path_split (URL, &parent_URL, &basename, pool); err = svn_ra_init_ra_libs (&ra_baton, pool); if (err) goto hit_error; err = svn_ra_get_ra_library (&ra_lib, ra_baton, parent_URL, pool); if (err) goto hit_error; err = ra_lib->open (&session, parent_URL, cbtable, NULL, cfg_hash, pool); if (err) goto hit_error; err = ra_lib->get_latest_revnum (session, &rev, pool); if (err) goto hit_error; /* Examine contents of parent dir in the rev. */ err = ra_lib->get_dir (session, "", rev, &dirents, NULL, NULL, pool); if (err) goto hit_error; /* Sanity checks. Don't let the user shoot himself *too* much. */ dirent = apr_hash_get (dirents, basename, APR_HASH_KEY_STRING); if (dirent && dirent->kind == svn_node_dir) { printf ("Sorry, a directory already exists at that URL.\n"); return EXIT_FAILURE; } if (dirent && dirent->kind == svn_node_file) { char answer[5]; printf ("\n*** WARNING ***\n\n"); printf ("You're about to overwrite r%ld of this file.\n", rev); printf ("It was last changed by user '%s',\n", dirent->last_author ? dirent->last_author : "?"); printf ("on %s.\n", svn_time_to_human_cstring (dirent->time, pool)); printf ("\nSomebody *might* have just changed the file seconds ago,\n" "and your upload would be overwriting their changes!\n\n"); err = prompt_and_read_line("Are you SURE you want to upload? [y/n]", answer, sizeof(answer)); if (err) goto hit_error; if (apr_strnatcasecmp (answer, "y")) { printf ("Operation aborted.\n"); return EXIT_SUCCESS; } } /* Fetch a commit editor (it's anchored on the parent URL, because the session is too.) */ /* ### someday add an option for a user-written commit message? */ err = ra_lib->get_commit_editor (session, &editor, &edit_baton, "File upload from 'svnput' program.", my_commit_callback, NULL, pool); if (err) goto hit_error; /* Drive the editor */ { void *root_baton, *file_baton, *handler_baton; svn_txdelta_window_handler_t handler; svn_stream_t *contents; apr_file_t *f = NULL; err = editor->open_root (edit_baton, rev, pool, &root_baton); if (err) goto hit_error; if (! dirent) { err = editor->add_file (basename, root_baton, NULL, SVN_INVALID_REVNUM, pool, &file_baton); } else { err = editor->open_file (basename, root_baton, rev, pool, &file_baton); } if (err) goto hit_error; err = editor->apply_textdelta (file_baton, NULL, pool, &handler, &handler_baton); if (err) goto hit_error; err = svn_io_file_open (&f, upload_file, APR_READ, APR_OS_DEFAULT, pool); if (err) goto hit_error; contents = svn_stream_from_aprfile (f, pool); err = svn_txdelta_send_stream (contents, handler, handler_baton, NULL, pool); if (err) goto hit_error; err = svn_io_file_close (f, pool); if (err) goto hit_error; err = editor->close_file (file_baton, NULL, pool); if (err) goto hit_error; err = editor->close_edit (edit_baton, pool); if (err) goto hit_error; } return EXIT_SUCCESS; hit_error: svn_handle_error2 (err, stderr, FALSE, "svnput: "); return EXIT_FAILURE; }
/* Internal version of svn_wc_merge, also used to (loggily) merge updates from the repository. In the case of updating, the update can have sent new properties, which could affect the way the wc target is detranslated and compared with LEFT and RIGHT for merging. Property changes sent by the update are provided in PROP_DIFF. If COPYFROM_TEXT is non-null, the working text is taken from this file instead of from the actual version in the working copy (and the merge_target is allowed to not be under version control in this case). */ svn_error_t * svn_wc__merge_internal(svn_stringbuf_t **log_accum, enum svn_wc_merge_outcome_t *merge_outcome, const char *left, const char *right, const char *merge_target, const char *copyfrom_text, svn_wc_adm_access_t *adm_access, const char *left_label, const char *right_label, const char *target_label, svn_boolean_t dry_run, const char *diff3_cmd, const apr_array_header_t *merge_options, const apr_array_header_t *prop_diff, svn_wc_conflict_resolver_func_t conflict_func, void *conflict_baton, apr_pool_t *pool) { const char *tmp_target, *result_target, *working_text; const char *adm_path = svn_wc_adm_access_path(adm_access); apr_file_t *result_f; svn_boolean_t is_binary = FALSE; const svn_wc_entry_t *entry; svn_boolean_t contains_conflicts; const svn_prop_t *mimeprop; /* Sanity check: the merge target must be under revision control (unless this is an add-with-history). */ SVN_ERR(svn_wc_entry(&entry, merge_target, adm_access, FALSE, pool)); if (! entry && ! copyfrom_text) { *merge_outcome = svn_wc_merge_no_merge; return SVN_NO_ERROR; } /* Decide if the merge target is a text or binary file. */ if ((mimeprop = get_prop(prop_diff, SVN_PROP_MIME_TYPE)) && mimeprop->value) is_binary = svn_mime_type_is_binary(mimeprop->value->data); else if (! copyfrom_text) SVN_ERR(svn_wc_has_binary_prop(&is_binary, merge_target, adm_access, pool)); working_text = copyfrom_text ? copyfrom_text : merge_target; SVN_ERR(detranslate_wc_file(&tmp_target, working_text, adm_access, (! is_binary) && diff3_cmd != NULL, prop_diff, pool)); /* We cannot depend on the left file to contain the same eols as the right file. If the merge target has mods, this will mark the entire file as conflicted, so we need to compensate. */ SVN_ERR(maybe_update_target_eols(&left, left, adm_access, prop_diff, pool)); if (! is_binary) /* this is a text file */ { /* Open a second temporary file for writing; this is where diff3 will write the merged results. */ SVN_ERR(svn_wc_create_tmp_file2(&result_f, &result_target, adm_path, svn_io_file_del_none, pool)); /* Run an external merge if requested. */ if (diff3_cmd) { int exit_code; SVN_ERR(svn_io_run_diff3_2(&exit_code, ".", tmp_target, left, right, target_label, left_label, right_label, result_f, diff3_cmd, merge_options, pool)); contains_conflicts = exit_code == 1; } else { svn_diff_t *diff; const char *target_marker; const char *left_marker; const char *right_marker; svn_stream_t *ostream; svn_diff_file_options_t *options; ostream = svn_stream_from_aprfile(result_f, pool); options = svn_diff_file_options_create(pool); if (merge_options) SVN_ERR(svn_diff_file_options_parse(options, merge_options, pool)); SVN_ERR(svn_diff_file_diff3_2(&diff, left, tmp_target, right, options, pool)); /* Labels fall back to sensible defaults if not specified. */ if (target_label) target_marker = apr_psprintf(pool, "<<<<<<< %s", target_label); else target_marker = "<<<<<<< .working"; if (left_label) left_marker = apr_psprintf(pool, "||||||| %s", left_label); else left_marker = "||||||| .old"; if (right_label) right_marker = apr_psprintf(pool, ">>>>>>> %s", right_label); else right_marker = ">>>>>>> .new"; SVN_ERR(svn_diff_file_output_merge(ostream, diff, left, tmp_target, right, left_marker, target_marker, right_marker, "=======", /* seperator */ FALSE, /* display original */ FALSE, /* resolve conflicts */ pool)); SVN_ERR(svn_stream_close(ostream)); contains_conflicts = svn_diff_contains_conflicts(diff); } /* Close the output file */ SVN_ERR(svn_io_file_close(result_f, pool)); if (contains_conflicts && ! dry_run) /* got a conflict */ { const char *left_copy, *right_copy, *target_copy; const char *tmp_left, *tmp_right, *tmp_target_copy; const char *parentt, *target_base; svn_wc_adm_access_t *parent_access; svn_wc_entry_t tmp_entry; /* Give the conflict resolution callback a chance to clean up the conflict before we mark the file 'conflicted' */ if (conflict_func) { svn_wc_conflict_result_t *result = NULL; svn_wc_conflict_description_t cdesc; cdesc.path = merge_target; cdesc.node_kind = svn_node_file; cdesc.kind = svn_wc_conflict_kind_text; cdesc.is_binary = FALSE; cdesc.mime_type = (mimeprop && mimeprop->value) ? mimeprop->value->data : NULL; cdesc.access = adm_access; cdesc.action = svn_wc_conflict_action_edit; cdesc.reason = svn_wc_conflict_reason_edited; cdesc.base_file = left; cdesc.their_file = right; cdesc.my_file = tmp_target; cdesc.merged_file = result_target; cdesc.property_name = NULL; SVN_ERR(conflict_func(&result, &cdesc, conflict_baton, pool)); if (result == NULL) return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("Conflict callback violated API:" " returned no results")); switch (result->choice) { /* If the callback wants to use one of the fulltexts to resolve the conflict, so be it.*/ case svn_wc_conflict_choose_base: { SVN_ERR(svn_wc__loggy_copy (log_accum, NULL, adm_access, svn_wc__copy_translate, left, merge_target, FALSE, pool)); *merge_outcome = svn_wc_merge_merged; contains_conflicts = FALSE; goto merge_complete; } case svn_wc_conflict_choose_theirs_full: { SVN_ERR(svn_wc__loggy_copy (log_accum, NULL, adm_access, svn_wc__copy_translate, right, merge_target, FALSE, pool)); *merge_outcome = svn_wc_merge_merged; contains_conflicts = FALSE; goto merge_complete; } case svn_wc_conflict_choose_mine_full: { /* Do nothing to merge_target, let it live untouched! */ *merge_outcome = svn_wc_merge_merged; contains_conflicts = FALSE; goto merge_complete; } /* For the case of 3-way file merging, we don't really distinguish between these return values; if the callback claims to have "generally resolved" the situation, we still interpret that as "OK, we'll assume the merged version is good to use". */ case svn_wc_conflict_choose_merged: { SVN_ERR(svn_wc__loggy_copy (log_accum, NULL, adm_access, svn_wc__copy_translate, /* Look for callback's own merged-file first: */ result->merged_file ? result->merged_file : result_target, merge_target, FALSE, pool)); *merge_outcome = svn_wc_merge_merged; contains_conflicts = FALSE; goto merge_complete; } case svn_wc_conflict_choose_postpone: default: { /* Assume conflict remains, fall through to code below. */ } } } /* Preserve the three pre-merge files, and modify the entry (mark as conflicted, track the preserved files). */ /* I miss Lisp. */ SVN_ERR(svn_io_open_unique_file2(NULL, &left_copy, merge_target, left_label, svn_io_file_del_none, pool)); /* Have I mentioned how much I miss Lisp? */ SVN_ERR(svn_io_open_unique_file2(NULL, &right_copy, merge_target, right_label, svn_io_file_del_none, pool)); /* Why, how much more pleasant to be forced to unroll my loops. If I'd been writing in Lisp, I might have mapped an inline lambda form over a list, or something equally disgusting. Thank goodness C was here to protect me! */ SVN_ERR(svn_io_open_unique_file2(NULL, &target_copy, merge_target, target_label, svn_io_file_del_none, pool)); /* We preserve all the files with keywords expanded and line endings in local (working) form. */ svn_path_split(target_copy, &parentt, &target_base, pool); SVN_ERR(svn_wc_adm_retrieve(&parent_access, adm_access, parentt, pool)); /* Log files require their paths to be in the subtree relative to the adm_access path they are executed in. Make our LEFT and RIGHT files 'local' if they aren't... */ if (! svn_path_is_child(adm_path, left, pool)) { SVN_ERR(svn_wc_create_tmp_file2 (NULL, &tmp_left, adm_path, svn_io_file_del_none, pool)); SVN_ERR(svn_io_copy_file(left, tmp_left, TRUE, pool)); } else tmp_left = left; if (! svn_path_is_child(adm_path, right, pool)) { SVN_ERR(svn_wc_create_tmp_file2 (NULL, &tmp_right, adm_path, svn_io_file_del_none, pool)); SVN_ERR(svn_io_copy_file(right, tmp_right, TRUE, pool)); } else tmp_right = right; /* NOTE: Callers must ensure that the svn:eol-style and svn:keywords property values are correct in the currently installed props. With 'svn merge', it's no big deal. But when 'svn up' calls this routine, it needs to make sure that this routine is using the newest property values that may have been received *during* the update. Since this routine will be run from within a log-command, merge_file() needs to make sure that a previous log-command to 'install latest props' has already executed first. Ben and I just checked, and that is indeed the order in which the log items are written, so everything should be fine. Really. */ /* Create LEFT and RIGHT backup files, in expanded form. We use merge_target's current properties to do the translation. */ /* Derive the basenames of the 3 backup files. */ SVN_ERR(svn_wc__loggy_translated_file(log_accum, adm_access, left_copy, tmp_left, merge_target, pool)); SVN_ERR(svn_wc__loggy_translated_file(log_accum, adm_access, right_copy, tmp_right, merge_target, pool)); /* Back up MERGE_TARGET through detranslation/retranslation: the new translation properties may not match the current ones */ SVN_ERR(svn_wc_translated_file2(&tmp_target_copy, merge_target, merge_target, adm_access, SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP, pool)); SVN_ERR(svn_wc__loggy_translated_file (log_accum, adm_access, target_copy, tmp_target_copy, merge_target, pool)); tmp_entry.conflict_old = svn_path_is_child(adm_path, left_copy, pool); tmp_entry.conflict_new = svn_path_is_child(adm_path, right_copy, pool); tmp_entry.conflict_wrk = target_base; /* Mark merge_target's entry as "Conflicted", and start tracking the backup files in the entry as well. */ SVN_ERR(svn_wc__loggy_entry_modify (log_accum, adm_access, merge_target, &tmp_entry, SVN_WC__ENTRY_MODIFY_CONFLICT_OLD | SVN_WC__ENTRY_MODIFY_CONFLICT_NEW | SVN_WC__ENTRY_MODIFY_CONFLICT_WRK, pool)); *merge_outcome = svn_wc_merge_conflict; } else if (contains_conflicts && dry_run) { *merge_outcome = svn_wc_merge_conflict; } /* end of conflict handling */ else if (copyfrom_text) { *merge_outcome = svn_wc_merge_merged; } else { svn_boolean_t same, special; /* If 'special', then use the detranslated form of the target file. This is so we don't try to follow symlinks, but the same treatment is probably also appropriate for whatever special file types we may invent in the future. */ SVN_ERR(svn_wc__get_special(&special, merge_target, adm_access, pool)); SVN_ERR(svn_io_files_contents_same_p(&same, result_target, (special ? tmp_target : merge_target), pool)); *merge_outcome = same ? svn_wc_merge_unchanged : svn_wc_merge_merged; } if (*merge_outcome != svn_wc_merge_unchanged && ! dry_run) /* replace MERGE_TARGET with the new merged file, expanding. */ SVN_ERR(svn_wc__loggy_copy(log_accum, NULL, adm_access, svn_wc__copy_translate, result_target, merge_target, FALSE, pool)); } /* end of merging for text files */ else if (! dry_run) /* merging procedure for binary files */ { /* ### when making the binary-file backups, should we be honoring keywords and eol stuff? */ const char *left_copy, *right_copy; const char *parentt, *left_base, *right_base; svn_wc_entry_t tmp_entry; /* Give the conflict resolution callback a chance to clean up the conflict before we mark the file 'conflicted' */ if (conflict_func) { svn_wc_conflict_result_t *result = NULL; svn_wc_conflict_description_t cdesc; cdesc.path = merge_target; cdesc.node_kind = svn_node_file; cdesc.kind = svn_wc_conflict_kind_text; cdesc.is_binary = TRUE; cdesc.mime_type = (mimeprop && mimeprop->value) ? mimeprop->value->data : NULL; cdesc.access = adm_access; cdesc.action = svn_wc_conflict_action_edit; cdesc.reason = svn_wc_conflict_reason_edited; cdesc.base_file = left; cdesc.their_file = right; cdesc.my_file = tmp_target; cdesc.merged_file = NULL; /* notice there is NO merged file! */ cdesc.property_name = NULL; SVN_ERR(conflict_func(&result, &cdesc, conflict_baton, pool)); if (result == NULL) return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("Conflict callback violated API:" " returned no results")); switch (result->choice) { /* For a binary file, there's no merged file to look at, unless the conflict-callback did the merging itself. */ case svn_wc_conflict_choose_base: { SVN_ERR(svn_wc__loggy_copy (log_accum, NULL, adm_access, svn_wc__copy_translate, left, merge_target, FALSE, pool)); *merge_outcome = svn_wc_merge_merged; contains_conflicts = FALSE; goto merge_complete; } case svn_wc_conflict_choose_theirs_full: { SVN_ERR(svn_wc__loggy_copy (log_accum, NULL, adm_access, svn_wc__copy_translate, right, merge_target, FALSE, pool)); *merge_outcome = svn_wc_merge_merged; contains_conflicts = FALSE; goto merge_complete; } /* For a binary file, if the response is to use the user's file, we do nothing. We also do nothing if the response claims to have already resolved the problem.*/ case svn_wc_conflict_choose_mine_full: { *merge_outcome = svn_wc_merge_merged; contains_conflicts = FALSE; goto merge_complete; } case svn_wc_conflict_choose_merged: { if (! result->merged_file) { /* Callback asked us to choose its own merged file, but didn't provide one! */ return svn_error_create (SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, _("Conflict callback violated API:" " returned no merged file")); } else { SVN_ERR(svn_wc__loggy_copy (log_accum, NULL, adm_access, svn_wc__copy_translate, result->merged_file, merge_target, FALSE, pool)); *merge_outcome = svn_wc_merge_merged; contains_conflicts = FALSE; goto merge_complete; } } case svn_wc_conflict_choose_postpone: default: { /* Assume conflict remains, fall through to code below. */ } } } /* reserve names for backups of left and right fulltexts */ SVN_ERR(svn_io_open_unique_file2(NULL, &left_copy, merge_target, left_label, svn_io_file_del_none, pool)); SVN_ERR(svn_io_open_unique_file2(NULL, &right_copy, merge_target, right_label, svn_io_file_del_none, pool)); /* create the backup files */ SVN_ERR(svn_io_copy_file(left, left_copy, TRUE, pool)); SVN_ERR(svn_io_copy_file(right, right_copy, TRUE, pool)); /* Was the merge target detranslated? */ if (merge_target != tmp_target) { /* Create a .mine file too */ const char *mine_copy; SVN_ERR(svn_io_open_unique_file2(NULL, &mine_copy, merge_target, target_label, svn_io_file_del_none, pool)); SVN_ERR(svn_wc__loggy_move(log_accum, NULL, adm_access, tmp_target, mine_copy, FALSE, pool)); mine_copy = svn_path_is_child(adm_path, mine_copy, pool); tmp_entry.conflict_wrk = mine_copy; } else tmp_entry.conflict_wrk = NULL; /* Derive the basenames of the backup files. */ svn_path_split(left_copy, &parentt, &left_base, pool); svn_path_split(right_copy, &parentt, &right_base, pool); /* Mark merge_target's entry as "Conflicted", and start tracking the backup files in the entry as well. */ tmp_entry.conflict_old = left_base; tmp_entry.conflict_new = right_base; SVN_ERR(svn_wc__loggy_entry_modify (log_accum, adm_access, merge_target, &tmp_entry, SVN_WC__ENTRY_MODIFY_CONFLICT_OLD | SVN_WC__ENTRY_MODIFY_CONFLICT_NEW | SVN_WC__ENTRY_MODIFY_CONFLICT_WRK, pool)); *merge_outcome = svn_wc_merge_conflict; /* a conflict happened */ } /* end of binary conflict handling */ else *merge_outcome = svn_wc_merge_conflict; /* dry_run for binary files. */ merge_complete: /* Merging is complete. Regardless of text or binariness, we might need to tweak the executable bit on the new working file. */ if (! dry_run) { SVN_ERR(svn_wc__loggy_maybe_set_executable(log_accum, adm_access, merge_target, pool)); SVN_ERR(svn_wc__loggy_maybe_set_readonly(log_accum, adm_access, merge_target, pool)); } return SVN_NO_ERROR; }
/* Drive EDITOR to affect the change represented by OPERATION. HEAD is the last-known youngest revision in the repository. */ static svn_error_t * drive(struct operation *operation, svn_revnum_t head, const svn_delta_editor_t *editor, apr_pool_t *pool) { apr_pool_t *subpool = svn_pool_create(pool); apr_hash_index_t *hi; struct driver_state state; for (hi = apr_hash_first(pool, operation->children); hi; hi = apr_hash_next(hi)) { const void *key; void *val; struct operation *child; void *file_baton = NULL; svn_pool_clear(subpool); apr_hash_this(hi, &key, NULL, &val); child = val; /* Deletes and replacements are simple -- delete something. */ if (child->operation == OP_DELETE || child->operation == OP_REPLACE) { SVN_ERR(editor->delete_entry(key, head, operation->baton, subpool)); } /* Opens could be for directories or files. */ if (child->operation == OP_OPEN) { if (child->kind == svn_node_dir) { SVN_ERR(editor->open_directory(key, operation->baton, head, subpool, &child->baton)); } else { SVN_ERR(editor->open_file(key, operation->baton, head, subpool, &file_baton)); } } /* Adds and replacements could also be for directories or files. */ if (child->operation == OP_ADD || child->operation == OP_REPLACE || child->operation == OP_PROPSET) { if (child->kind == svn_node_dir) { SVN_ERR(editor->add_directory(key, operation->baton, child->url, child->rev, subpool, &child->baton)); } else { SVN_ERR(editor->add_file(key, operation->baton, child->url, child->rev, subpool, &file_baton)); } } /* If there's a source file and an open file baton, we get to change textual contents. */ if ((child->src_file) && (file_baton)) { svn_txdelta_window_handler_t handler; void *handler_baton; svn_stream_t *contents; apr_file_t *f = NULL; SVN_ERR(editor->apply_textdelta(file_baton, NULL, subpool, &handler, &handler_baton)); if (strcmp(child->src_file, "-")) { SVN_ERR(svn_io_file_open(&f, child->src_file, APR_READ, APR_OS_DEFAULT, pool)); } else { apr_status_t apr_err = apr_file_open_stdin(&f, pool); if (apr_err) return svn_error_wrap_apr(apr_err, "Can't open stdin"); } contents = svn_stream_from_aprfile(f, pool); SVN_ERR(svn_txdelta_send_stream(contents, handler, handler_baton, NULL, pool)); SVN_ERR(svn_io_file_close(f, pool)); } /* If we opened a file, we need to apply outstanding propmods, then close it. */ if (file_baton) { if ((child->kind == svn_node_file) && (! apr_is_empty_table(child->props))) { state.baton = file_baton; state.pool = subpool; state.editor = editor; state.kind = child->kind; if (! apr_table_do(set_props, &state, child->props, NULL)) SVN_ERR(state.err); } SVN_ERR(editor->close_file(file_baton, NULL, subpool)); } /* If we opened, added, or replaced a directory, we need to recurse, apply outstanding propmods, and then close it. */ if ((child->kind == svn_node_dir) && (child->operation == OP_OPEN || child->operation == OP_ADD || child->operation == OP_REPLACE)) { SVN_ERR(drive(child, head, editor, subpool)); if ((child->kind == svn_node_dir) && (! apr_is_empty_table(child->props))) { state.baton = child->baton; state.pool = subpool; state.editor = editor; state.kind = child->kind; if (! apr_table_do(set_props, &state, child->props, NULL)) SVN_ERR(state.err); } SVN_ERR(editor->close_directory(child->baton, subpool)); } } svn_pool_destroy(subpool); return SVN_NO_ERROR; }
/* Implements svn_test_driver_t. */ static svn_error_t * random_test(apr_pool_t *pool) { apr_uint32_t seed, maxlen; apr_size_t bytes_range; int i, iterations, dump_files, print_windows; const char *random_bytes; /* Initialize parameters and print out the seed in case we dump core or something. */ init_params(&seed, &maxlen, &iterations, &dump_files, &print_windows, &random_bytes, &bytes_range, pool); for (i = 0; i < iterations; i++) { /* Generate source and target for the delta and its application. */ apr_uint32_t subseed_base = svn_test_rand(&seed); apr_file_t *source = generate_random_file(maxlen, subseed_base, &seed, random_bytes, bytes_range, dump_files, pool); apr_file_t *target = generate_random_file(maxlen, subseed_base, &seed, random_bytes, bytes_range, dump_files, pool); apr_file_t *source_copy = copy_tempfile(source, pool); apr_file_t *target_regen = open_tempfile(NULL, pool); svn_txdelta_stream_t *txdelta_stream; svn_txdelta_window_handler_t handler; svn_stream_t *stream; void *handler_baton; /* Set up a four-stage pipeline: create a delta, convert it to svndiff format, parse it back into delta format, and apply it to a copy of the source file to see if we get the same target back. */ apr_pool_t *delta_pool = svn_pool_create(pool); /* Make stage 4: apply the text delta. */ svn_txdelta_apply(svn_stream_from_aprfile(source_copy, delta_pool), svn_stream_from_aprfile(target_regen, delta_pool), NULL, NULL, delta_pool, &handler, &handler_baton); /* Make stage 3: reparse the text delta. */ stream = svn_txdelta_parse_svndiff(handler, handler_baton, TRUE, delta_pool); /* Make stage 2: encode the text delta in svndiff format using varying compression levels. */ svn_txdelta_to_svndiff3(&handler, &handler_baton, stream, 1, i % 10, delta_pool); /* Make stage 1: create the text delta. */ svn_txdelta2(&txdelta_stream, svn_stream_from_aprfile(source, delta_pool), svn_stream_from_aprfile(target, delta_pool), FALSE, delta_pool); SVN_ERR(svn_txdelta_send_txstream(txdelta_stream, handler, handler_baton, delta_pool)); svn_pool_destroy(delta_pool); SVN_ERR(compare_files(target, target_regen, dump_files)); apr_file_close(source); apr_file_close(target); apr_file_close(source_copy); apr_file_close(target_regen); } return SVN_NO_ERROR; }