00001
00002
00003
00004
00005
00006
00007
00008
00068 #include <apr_md5.h>
00069 #include <apr_pools.h>
00070 #include <apr_user.h>
00071 #include <apr_file_io.h>
00072 #include <subversion-1/svn_delta.h>
00073 #include <subversion-1/svn_ra.h>
00074 #include <subversion-1/svn_error.h>
00075 #include <subversion-1/svn_string.h>
00076 #include <subversion-1/svn_time.h>
00077
00078 #include <sys/types.h>
00079 #include <sys/mman.h>
00080 #include <unistd.h>
00081 #include <time.h>
00082 #include <fcntl.h>
00083
00084
00085 #include "global.h"
00086 #include "status.h"
00087 #include "checksum.h"
00088 #include "waa.h"
00089 #include "cache.h"
00090 #include "est_ops.h"
00091 #include "props.h"
00092 #include "options.h"
00093 #include "ignore.h"
00094 #include "cp_mv.h"
00095 #include "racallback.h"
00096 #include "url.h"
00097 #include "helper.h"
00098
00099
00100
00102 typedef svn_error_t *(*change_any_prop_t) (void *baton,
00103 const char *name,
00104 const svn_string_t *value,
00105 apr_pool_t *pool);
00106
00107
00109 unsigned committed_entries;
00111 char *missing_path_utf8;
00113 int missing_path_utf8_len;
00114
00115
00118 int ci__set_revision(struct estat *this, svn_revnum_t rev)
00119 {
00120 int i;
00121
00122
00123
00124 if (this->url == current_url)
00125 this->repos_rev=rev;
00126
00127 if (S_ISDIR(this->st.mode))
00128 for(i=0; i<this->entry_count; i++)
00129 ci__set_revision(this->by_inode[i], rev);
00130
00131 return 0;
00132 }
00133
00134
00143 svn_error_t * ci__callback (
00144 svn_revnum_t new_revision,
00145 const char *utf8_date,
00146 const char *utf8_author,
00147 void *baton)
00148 {
00149 struct estat *root UNUSED=baton;
00150 int status;
00151
00152
00153 status=0;
00154 if (opt__get_int(OPT__VERBOSE) > VERBOSITY_VERYQUIET)
00155 printf("committed revision\t%ld on %s as %s\n",
00156 new_revision, utf8_date, utf8_author);
00157
00158
00159
00160 current_url->current_rev = new_revision;
00161
00162
00163 RETURN_SVNERR(status);
00164 }
00165
00166
00170 int ci__action(struct estat *sts)
00171 {
00172 int status;
00173 char *path;
00174
00175
00176 STOPIF( ops__build_path(&path, sts), NULL);
00177
00178 STOPIF_CODE_ERR( sts->flags & RF_CONFLICT, EBUSY,
00179 "!The entry \"%s\" is still marked as conflict.", path);
00180
00181 if (sts->entry_status ||
00182 (sts->flags & RF___COMMIT_MASK) )
00183 ops__mark_parent_cc(sts, entry_status);
00184
00185 STOPIF( st__progress(sts), NULL);
00186
00187 ex:
00188 return status;
00189 }
00190
00191
00198 void ci___unset_copyflags(struct estat *root)
00199 {
00200 struct estat **sts;
00201
00202
00203
00204 root->flags &= ~(RF_ADD | RF_COPY_BASE | RF_COPY_SUB);
00205
00206 root->url=current_url;
00207
00208 if (ops__has_children(root))
00209 {
00210 sts=root->by_inode;
00211 while (*sts)
00212 {
00213 if (! ( (*sts)->flags & RF_COPY_BASE) )
00214 {
00215 ci___unset_copyflags(*sts);
00216 }
00217
00218 sts++;
00219 }
00220 }
00221 }
00222
00223
00235 int ci___send_user_props(void *baton,
00236 struct estat *sts,
00237 change_any_prop_t function,
00238 int store_encoder,
00239 apr_pool_t *pool)
00240 {
00241 int status;
00242 svn_error_t *status_svn;
00243 datum key, value;
00244 hash_t db;
00245 svn_string_t *str;
00246
00247
00248 db=NULL;
00249
00250
00251
00252 int send_a_prop(char *key, svn_string_t *value)
00253 {
00254 int status;
00255
00256 status=0;
00257
00258
00259 if (store_encoder && strcmp(key, propval_commitpipe) == 0)
00260 {
00261 if (value)
00262 STOPIF( hlp__strdup( &sts->decoder, value->data), NULL);
00263 else
00264 sts->decoder=NULL;
00265 }
00266
00267 STOPIF_SVNERR( function, (baton, key, value, pool) );
00268
00269 ex:
00270 return status;
00271 }
00272
00273
00274
00275 STOPIF( ops__apply_group(sts, &db, pool), NULL);
00276
00277
00278
00279 if (db)
00280 {
00281 status=prp__first(db, &key);
00282 while (status==0)
00283 {
00284 STOPIF( prp__fetch(db, key, &value), NULL);
00285
00286 if (hlp__is_special_property_name(key.dptr))
00287 {
00288 DEBUGP("ignoring %s - should not have been taken?", key.dptr);
00289 }
00290 else if (prp__prop_will_be_removed(value))
00291 {
00292 DEBUGP("removing property %s", key.dptr);
00293
00294 STOPIF( send_a_prop(key.dptr, NULL), NULL);
00295 STOPIF( hsh__register_delete(db, key), NULL);
00296 }
00297 else
00298 {
00299 DEBUGP("sending property %s=(%d)%.*s", key.dptr,
00300 value.dsize, value.dsize, value.dptr);
00301
00302 str=svn_string_ncreate(value.dptr, value.dsize-1, pool);
00303 STOPIF( send_a_prop(key.dptr, str), NULL);
00304 }
00305
00306 status=prp__next( db, &key, &key);
00307 }
00308
00309
00310 if (status != ENOENT)
00311 STOPIF(status, NULL);
00312 status=0;
00313 }
00314
00315
00316 STOPIF( hsh__close(db, status), NULL);
00317
00318 ex:
00319 return status;
00320 }
00321
00322
00332 svn_error_t *ci___set_props(void *baton,
00333 struct estat *sts,
00334 change_any_prop_t function,
00335 apr_pool_t *pool)
00336 {
00337 const char *ccp;
00338 svn_string_t *str;
00339 int status;
00340 svn_error_t *status_svn;
00341
00342
00343 status=0;
00344
00345
00346 if (!S_ISLNK(sts->st.mode))
00347 {
00348
00349 str=svn_string_createf (pool, "0%03o", (int)(sts->st.mode & 07777));
00350 STOPIF_SVNERR( function, (baton, propname_umode, str, pool) );
00351 }
00352
00353
00354 str=svn_string_createf (pool, "%lu %s",
00355 (unsigned long)sts->st.uid, hlp__get_uname(sts->st.uid, "") );
00356 STOPIF_SVNERR( function, (baton, propname_owner, str, pool) );
00357
00358
00359 str=svn_string_createf (pool, "%lu %s",
00360 (unsigned long)sts->st.gid, hlp__get_grname(sts->st.gid, "") );
00361 STOPIF_SVNERR( function, (baton, propname_group, str, pool) );
00362
00363
00364 ccp=(char *)svn_time_to_cstring (
00365 apr_time_make( sts->st.mtim.tv_sec, sts->st.mtim.tv_nsec/1000),
00366 pool);
00367 str=svn_string_create(ccp, pool);
00368 STOPIF_SVNERR( function, (baton, propname_mtime, str, pool) );
00369
00370 ex:
00371 RETURN_SVNERR(status);
00372 }
00373
00374
00383 svn_error_t *ci__nondir(const svn_delta_editor_t *editor,
00384 struct estat *sts,
00385 void *baton,
00386 apr_pool_t *pool)
00387 {
00388 svn_txdelta_window_handler_t delta_handler;
00389 void *delta_baton;
00390 svn_error_t *status_svn;
00391 svn_stream_t *s_stream;
00392 char *cp;
00393 char *filename;
00394 int status;
00395 svn_string_t *stg;
00396 apr_file_t *a_stream;
00397 svn_stringbuf_t *str;
00398 struct encoder_t *encoder;
00399 int transfer_text, has_manber;
00400
00401
00402 str=NULL;
00403 a_stream=NULL;
00404 s_stream=NULL;
00405 encoder=NULL;
00406
00407 STOPIF( ops__build_path(&filename, sts), NULL);
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419 STOPIF( ci___send_user_props(baton, sts,
00420 editor->change_file_prop, 1, pool), NULL);
00421
00422 STOPIF_SVNERR( ci___set_props,
00423 (baton, sts, editor->change_file_prop, pool) );
00424
00425
00426 BUG_ON( sts->entry_status & FS_LIKELY );
00427
00428
00429
00430
00431
00432 DEBUGP("%s: status %s; flags %s", sts->name,
00433 st__status_string(sts),
00434 st__flags_string_fromint(sts->flags));
00435
00436
00437 transfer_text= sts->entry_status & (FS_CHANGED | FS_NEW | FS_REMOVED);
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456 if (!transfer_text && !(sts->flags & RF___IS_COPY))
00457 {
00458 DEBUGP("hasn't changed, and no copy.");
00459 }
00460 else
00461 {
00462 has_manber=0;
00463 switch (sts->st.mode & S_IFMT)
00464 {
00465 case S_IFLNK:
00466 STOPIF( ops__link_to_string(sts, filename, &cp), NULL);
00467 STOPIF( hlp__local2utf8(cp, &cp, -1), NULL);
00468
00469
00470
00471 str=svn_stringbuf_create(cp, pool);
00472 break;
00473 case S_IFBLK:
00474 case S_IFCHR:
00475
00476
00477 str=svn_stringbuf_create(
00478 ops__dev_to_filedata(sts), pool);
00479 break;
00480 case S_IFREG:
00481 STOPIF( apr_file_open(&a_stream, filename, APR_READ, 0, pool),
00482 "open file \"%s\" for reading", filename);
00483
00484 s_stream=svn_stream_from_aprfile (a_stream, pool);
00485
00486
00487
00488 has_manber= (sts->st.size >= CS__MIN_FILE_SIZE);
00489 if (has_manber)
00490 STOPIF( cs__new_manber_filter(sts, s_stream, &s_stream, pool), NULL );
00491
00492
00493
00494 if (transfer_text && sts->decoder)
00495 {
00496
00497
00498
00499 STOPIF( hlp__encode_filter(s_stream, sts->decoder, 0,
00500 filename, &s_stream, &encoder, pool), NULL );
00501 encoder->output_md5= &(sts->md5);
00502
00503 IF_FREE(sts->decoder);
00504 }
00505 break;
00506 default:
00507 BUG("invalid/unknown file type 0%o", sts->st.mode);
00508 }
00509
00510
00511 if (str) s_stream=svn_stream_from_stringbuf (str, pool);
00512
00513 BUG_ON(!s_stream);
00514
00515 if (transfer_text)
00516 {
00517 DEBUGP("really sending ...");
00518 STOPIF_SVNERR( editor->apply_textdelta,
00519 (baton,
00520 NULL,
00521 pool,
00522 &delta_handler,
00523 &delta_baton));
00524
00525
00526
00527 STOPIF_SVNERR( svn_txdelta_send_stream,
00528 (s_stream, delta_handler,
00529 delta_baton,
00530 sts->md5, pool) );
00531 DEBUGP("after sending encoder=%p", encoder);
00532 }
00533 else
00534 {
00535 DEBUGP("doing local MD5.");
00536
00537
00538
00539
00540 STOPIF( hlp__stream_md5(s_stream,
00541 has_manber ? NULL : sts->md5), NULL);
00542 }
00543
00544 STOPIF_SVNERR( svn_stream_close, (s_stream) );
00545
00546
00547
00548 if (str)
00549 {
00550 stg=svn_string_create(propval_special, pool);
00551 STOPIF_SVNERR( editor->change_file_prop,
00552 (baton, propname_special, stg, pool) );
00553 }
00554
00555
00556 if (encoder)
00557 {
00558 cp=cs__md5tohex_buffered(sts->md5);
00559 DEBUGP("Sending original MD5 as %s", cp);
00560
00561 stg=svn_string_create(cp, pool);
00562 STOPIF_SVNERR( editor->change_file_prop,
00563 (baton, propname_origmd5, stg, pool) );
00564 }
00565
00566 }
00567
00568
00569 STOPIF( cs__set_file_committed(sts), NULL);
00570
00571 ex:
00572 if (a_stream)
00573 {
00574
00575
00576 apr_file_close(a_stream);
00577 }
00578
00579 RETURN_SVNERR(status);
00580 }
00581
00582
00585 svn_error_t *ci__directory(const svn_delta_editor_t *editor,
00586 struct estat *dir,
00587 void *dir_baton,
00588 apr_pool_t *pool)
00589 {
00590 void *baton;
00591 int status;
00592 struct estat *sts;
00593 apr_pool_t *subpool;
00594 int i, exists_now;
00595 char *filename;
00596 char *utf8_filename, *tmp;
00597 svn_error_t *status_svn;
00598 char *src_path;
00599 svn_revnum_t src_rev;
00600 struct sstat_t stat;
00601 struct cache_entry_t *utf8fn_plus_missing;
00602 int utf8fn_len;
00603
00604
00605 status=0;
00606 utf8fn_plus_missing=NULL;
00607 subpool=NULL;
00608 DEBUGP("commit_dir with baton %p", dir_baton);
00609 for(i=0; i<dir->entry_count; i++)
00610 {
00611 sts=dir->by_inode[i];
00612
00613
00614
00615 if ( (sts->flags & RF___COMMIT_MASK) && sts->do_this_entry)
00616 {
00617
00618
00619 if (sts->flags & RF_PUSHPROPS)
00620 sts->entry_status |= FS_PROPERTIES;
00621 }
00622 else if (sts->entry_status)
00623 {
00624
00625
00626
00627
00628 }
00629 else
00630
00631 continue;
00632
00633
00634
00635 if (subpool) apr_pool_destroy(subpool);
00636
00637 STOPIF( apr_pool_create_ex(&subpool, pool, NULL, NULL),
00638 "no pool");
00639
00640 STOPIF( ops__build_path(&filename, sts), NULL);
00641
00642
00643 STOPIF( hlp__local2utf8(filename+2, &utf8_filename, -1), NULL );
00644 if (missing_path_utf8)
00645 {
00646 utf8fn_len=strlen(utf8_filename);
00647 STOPIF( cch__entry_set(&utf8fn_plus_missing, 0, NULL,
00648 missing_path_utf8_len + 1 + utf8fn_len + 1,
00649 0, &tmp), NULL);
00650 strcpy(tmp, missing_path_utf8);
00651 tmp[missing_path_utf8_len]='/';
00652 strcpy(tmp + missing_path_utf8_len +1, utf8_filename);
00653 utf8_filename=tmp;
00654 }
00655
00656 DEBUGP("%s: action %X, updated mode 0%o, flags %X, filter %d",
00657 filename, sts->entry_status, sts->st.mode, sts->flags,
00658 ops__allowed_by_filter(sts));
00659
00660 if (ops__allowed_by_filter(sts))
00661 STOPIF( st__status(sts), NULL);
00662
00663 exists_now= !(sts->flags & RF_UNVERSION) &&
00664 ( (sts->entry_status & (FS_NEW | FS_CHANGED | FS_META_CHANGED)) ||
00665 (sts->flags & (RF_ADD | RF_PUSHPROPS | RF_COPY_BASE))
00666 );
00667
00668 if ( (sts->flags & RF_UNVERSION) ||
00669 (sts->entry_status & FS_REMOVED) )
00670 {
00671 DEBUGP("deleting %s", sts->name);
00672
00673 STOPIF_SVNERR( editor->delete_entry,
00674 (utf8_filename, SVN_INVALID_REVNUM, dir_baton, subpool) );
00675
00676 committed_entries++;
00677
00678 if (!exists_now)
00679 {
00680 DEBUGP("%s=%d doesn't exist anymore", sts->name, i);
00681
00682
00683 STOPIF( ops__delete_entry(dir, NULL, i, UNKNOWN_INDEX),
00684 NULL);
00685 STOPIF( waa__delete_byext(filename, WAA__FILE_MD5s_EXT, 1), NULL);
00686 STOPIF( waa__delete_byext(filename, WAA__PROP_EXT, 1), NULL);
00687 i--;
00688 continue;
00689 }
00690 }
00691
00692
00693
00694
00695 if (!exists_now && !(sts->entry_status & FS_CHILD_CHANGED))
00696 continue;
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713 if (hlp__lstat(filename, &stat))
00714 {
00715
00716
00717
00718 STOPIF_CODE_ERR( sts->flags & RF_ADD, ENOENT,
00719 "Entry %s should be added, but doesn't exist.",
00720 filename);
00721
00722 DEBUGP("%s doesn't exist, ignoring (%d)", filename, errno);
00723 continue;
00724 }
00725
00726
00727
00728
00729 if (sts->do_this_entry && ops__allowed_by_filter(sts))
00730 {
00731 sts->st=stat;
00732 DEBUGP("set st for %s", sts->name);
00733 }
00734
00735
00736
00737 baton=NULL;
00738
00739
00740
00741
00742 if ((sts->flags & (RF_ADD | RF_COPY_BASE) ) ||
00743 (sts->entry_status & FS_NEW) )
00744 {
00745
00746 }
00747 else
00748 {
00749 status_svn=
00750 (S_ISDIR(sts->st.mode) ?
00751 editor->open_directory : editor->open_file)
00752 ( utf8_filename, dir_baton,
00753 current_url->current_rev,
00754 subpool, &baton);
00755
00756 if (status_svn)
00757 {
00758 DEBUGP("got error %d %d", status_svn->apr_err, SVN_ERR_FS_PATH_SYNTAX);
00759
00760
00761
00762
00763
00764
00765 if (S_ISDIR(sts->st.mode) &&
00766 (
00767 status_svn->apr_err == SVN_ERR_FS_PATH_SYNTAX ||
00768 status_svn->apr_err == SVN_ERR_FS_NOT_DIRECTORY ) &&
00769 urllist_count>1)
00770 {
00771 DEBUGP("dir %s didn't exist, trying to create", filename);
00772
00773 baton=NULL;
00774 status_svn=NULL;
00775 }
00776 else
00777 STOPIF_CODE_ERR(1, status_svn->apr_err,
00778 S_ISDIR(sts->st.mode) ?
00779 "open_directory" : "open_file");
00780 }
00781
00782 DEBUGP("baton for mod %s %p (parent %p)",
00783 sts->name, baton, dir_baton);
00784 }
00785
00786 if (!baton)
00787 {
00788 DEBUGP("new %s (parent %p)",
00789 sts->name, dir_baton);
00790
00791
00792
00793
00794
00795
00796 if (sts->flags & RF_COPY_BASE)
00797 {
00798 status=cm__get_source(sts, filename, &src_path, &src_rev, 1);
00799 BUG_ON(status == ENOENT, "copy but not copied?");
00800 STOPIF(status, NULL);
00801 }
00802 else
00803 {
00804
00805 src_path=NULL;
00806 src_rev=SVN_INVALID_REVNUM;
00807 }
00808
00809
00810
00811
00812
00813 DEBUGP("adding %s with %s:%ld",
00814 filename, src_path, src_rev);
00816 STOPIF_SVNERR_TEXT(
00817 (S_ISDIR(sts->st.mode) ?
00818 editor->add_directory : editor->add_file),
00819 (utf8_filename, dir_baton,
00820 src_path, src_rev,
00821 subpool, &baton),
00822 "%s(\"%s\", source=\"%s\"@%s)",
00823 S_ISDIR(sts->st.mode) ? "add_directory" : "add_file",
00824 filename, src_path, hlp__rev_to_string(src_rev));
00825 DEBUGP("baton for new %s %p (parent %p)",
00826 sts->name, baton, dir_baton);
00827
00828
00829
00830 if (!(sts->flags & RF_COPY_BASE))
00831 {
00832 sts->flags &= ~RF_ADD;
00833 sts->entry_status |= FS_NEW | FS_META_CHANGED;
00834 }
00835 }
00836
00837
00838 committed_entries++;
00839 DEBUGP("doing changes, flags=%X", sts->flags);
00840
00841 if (S_ISDIR(sts->st.mode))
00842 {
00843 STOPIF_SVNERR( ci__directory, (editor, sts, baton, subpool) );
00844 STOPIF_SVNERR( editor->close_directory, (baton, subpool) );
00845 }
00846 else
00847 {
00848 STOPIF_SVNERR( ci__nondir, (editor, sts, baton, subpool) );
00849 STOPIF_SVNERR( editor->close_file, (baton, NULL, subpool) );
00850 }
00851
00852
00853
00854
00855
00856
00857 if (sts->flags & RF_COPY_BASE)
00858 ci___unset_copyflags(sts);
00859
00860
00861
00862 if (url__current_has_precedence(sts->url))
00863 {
00864 DEBUGP("setting URL of %s", filename);
00865 sts->url=current_url;
00866 sts->repos_rev = SET_REVNUM;
00867 }
00868 }
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878 if (! (dir->do_this_entry && ops__allowed_by_filter(dir)) )
00879 dir->flags |= RF_CHECK;
00880 else
00881 dir->flags &= ~RF_CHECK;
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906 if ((dir->do_this_entry &&
00907 ops__allowed_by_filter(dir) &&
00908 dir->parent &&
00909
00910 (dir->entry_status & (FS_META_CHANGED | FS_PROPERTIES))) ||
00911 (dir->entry_status & FS_NEW))
00912 {
00913 STOPIF_SVNERR( ci___set_props,
00914 (dir_baton, dir, editor->change_dir_prop, pool) );
00915
00916 STOPIF( ci___send_user_props(dir_baton, dir,
00917 editor->change_dir_prop, 0, pool), NULL);
00918 }
00919
00920
00921 ex:
00922 if (subpool)
00923 apr_pool_destroy(subpool);
00924 RETURN_SVNERR(status);
00925 }
00926
00927
00931 int ci__getmsg(char **filename)
00932 {
00933 char *editor_cmd, *cp;
00934 int l,status;
00935 apr_file_t *af;
00936
00937
00938 status=0;
00939 STOPIF( waa__get_tmp_name( NULL, filename, &af, global_pool), NULL);
00940
00941
00942
00943 STOPIF( apr_file_close(af), "close commit message file");
00944
00945 editor_cmd=getenv("EDITOR");
00946 if (!editor_cmd) editor_cmd=getenv("VISUAL");
00947 if (!editor_cmd) editor_cmd="vi";
00948
00949 l=strlen(editor_cmd) + 1 + strlen(opt_commitmsgfile) + 1;
00950
00951 STOPIF( hlp__strmnalloc(l, &cp,
00952 editor_cmd, " ", opt_commitmsgfile, NULL), NULL);
00953
00954 l=system(cp);
00955 STOPIF_CODE_ERR(l == -1, errno, "fork() failed");
00956
00957 STOPIF_CODE_ERR(l, WEXITSTATUS(l),
00958 "spawned editor exited with %d, signal %d",
00959 WEXITSTATUS(l),
00960 WIFSIGNALED(l) ? WTERMSIG(l) : 0);
00961 status=0;
00962
00963 ex:
00964 return status;
00965 }
00966
00967
00978 svn_error_t *ci___base_dirs(char *current_missing,
00979 const svn_delta_editor_t *editor,
00980 struct estat *root,
00981 void *dir_baton)
00982 {
00983 int status;
00984 svn_error_t *status_svn;
00985 char *delim;
00986 void *child_baton;
00987
00988
00989 status=0;
00990 if (current_missing && *current_missing)
00991 {
00992
00993 delim=strchr(current_missing, '/');
00994 if (delim)
00995 {
00996 *delim=0;
00997 delim++;
00998
00999 BUG_ON(!*delim || *delim=='/');
01000 }
01001
01002 DEBUGP("adding %s", missing_path_utf8);
01003 STOPIF_SVNERR( editor->add_directory,
01004 (missing_path_utf8, dir_baton,
01005 NULL, SVN_INVALID_REVNUM,
01006 current_url->pool, &child_baton));
01007
01008 if (delim)
01009 delim[-1]='/';
01010
01011 STOPIF_SVNERR( ci___base_dirs,
01012 (delim, editor, root, child_baton));
01013
01014 STOPIF_SVNERR( editor->close_directory,
01015 (child_baton, current_url->pool));
01016 }
01017 else
01018 STOPIF_SVNERR( ci__directory,
01019 (editor, root, dir_baton, current_url->pool));
01020
01021
01022 ex:
01023 RETURN_SVNERR(status);
01024 }
01025
01026
01027
01028
01038 int ci__work(struct estat *root, int argc, char *argv[])
01039 {
01040 int status;
01041 svn_error_t *status_svn;
01042 const svn_delta_editor_t *editor;
01043 void *edit_baton;
01044 void *root_baton;
01045 struct stat st;
01046 int commitmsg_fh,
01047 commitmsg_is_temp;
01048 char *utf8_commit_msg;
01049 char **normalized;
01050 const char *url_name;
01051 time_t delay_start;
01052 char *missing_dirs;
01053
01054
01055 status=0;
01056 status_svn=NULL;
01057 edit_baton=NULL;
01058 editor=NULL;
01059
01060 commitmsg_fh=-1;
01061
01062
01063 opt__set_int(OPT__CHANGECHECK,
01064 PRIO_MUSTHAVE,
01065 opt__get_int(OPT__CHANGECHECK) | CHCHECK_DIRS | CHCHECK_FILE);
01066
01067
01068
01069 commitmsg_is_temp=!opt_commitmsg && !opt_commitmsgfile;
01070 if (commitmsg_is_temp)
01071 STOPIF( ci__getmsg(&opt_commitmsgfile), NULL);
01072
01073
01074
01075
01076
01077
01078
01079
01080 if (opt_commitmsgfile)
01081 {
01082 commitmsg_fh=open(opt_commitmsgfile, O_RDONLY);
01083 STOPIF_CODE_ERR( commitmsg_fh<0, errno,
01084 "cannot open file %s", opt_commitmsgfile);
01085 }
01086
01087
01088 STOPIF( waa__find_common_base(argc, argv, &normalized), NULL);
01089
01090
01091 STOPIF( url__load_nonempty_list(NULL, 0), NULL);
01092
01093 if (urllist_count==1)
01094 current_url=urllist[0];
01095 else
01096 {
01097 url_name=opt__get_string(OPT__COMMIT_TO);
01098 STOPIF_CODE_ERR( !url_name || !*url_name, EINVAL,
01099 "!Which URL would you like to commit to?\n"
01100 "Please choose one (config option \"commit_to\").");
01101
01102 STOPIF( url__find_by_name(url_name, ¤t_url),
01103 "!No URL named \"%s\" could be found.", url_name);
01104 }
01105
01106 STOPIF_CODE_ERR( current_url->is_readonly, EROFS,
01107 "!Cannot commit to \"%s\",\n"
01108 "because it is marked read-only.",
01109 current_url->url);
01110
01111
01112 STOPIF(ign__load_list(NULL), NULL);
01113
01114
01115 STOPIF( url__open_session(NULL, &missing_dirs), NULL);
01116
01117 if (missing_dirs)
01118 STOPIF_CODE_ERR( opt__get_int(OPT__MKDIR_BASE) == OPT__NO, ENOENT,
01119 "!The given URL \"%s\" does not exist (yet).\n"
01120 "The missing directories \"%s\" could possibly be created, if\n"
01121 "you enable the \"mkdir_base\" option (with \"-o mkdir_base=yes\").",
01122 current_url->url, missing_dirs);
01123
01124
01125
01126 opt__set_int( OPT__CHANGECHECK, PRIO_MUSTHAVE,
01127 opt__get_int(OPT__CHANGECHECK) | CHCHECK_DIRS | CHCHECK_FILE);
01128
01129
01130
01131 STOPIF( waa__read_or_build_tree(root, argc, normalized, argv,
01132 NULL, 0), NULL);
01133
01134
01135 if (opt_commitmsgfile)
01136 {
01137 STOPIF_CODE_ERR( fstat(commitmsg_fh, &st) == -1, errno,
01138 "cannot estimate size of %s", opt_commitmsgfile);
01139
01140 if (st.st_size == 0)
01141 {
01142
01143 DEBUGP("empty file");
01144 opt_commitmsg="";
01145 }
01146 else
01147 {
01148 DEBUGP("file is %llu bytes", (t_ull)st.st_size);
01149
01150 opt_commitmsg=mmap(NULL, st.st_size, PROT_READ, MAP_SHARED,
01151 commitmsg_fh, 0);
01152 STOPIF_CODE_ERR(!opt_commitmsg, errno,
01153 "mmap commit message (%s, %llu bytes)",
01154 opt_commitmsgfile, (t_ull)st.st_size);
01155 }
01156 close(commitmsg_fh);
01157 }
01158
01159 if (!*opt_commitmsg)
01160 {
01161 STOPIF_CODE_ERR( opt__get_int(OPT__EMPTY_MESSAGE)==OPT__NO, EINVAL,
01162 "!Empty commit messages are defined as invalid, "
01163 "see \"empty_message\" option.");
01164 }
01165
01166 STOPIF( hlp__local2utf8(opt_commitmsg, &utf8_commit_msg, -1),
01167 "Conversion of the commit message to utf8 failed");
01168
01169 if (opt__get_int(OPT__VERBOSE) > VERBOSITY_VERYQUIET)
01170 printf("Committing to %s\n", current_url->url);
01171
01172
01173 STOPIF_SVNERR( svn_ra_get_commit_editor,
01174 (current_url->session,
01175 &editor,
01176 &edit_baton,
01177 utf8_commit_msg,
01178 ci__callback,
01179 root,
01180 NULL,
01181 FALSE,
01182 global_pool) );
01183
01184 if (opt_commitmsgfile && st.st_size != 0)
01185 STOPIF_CODE_ERR( munmap(opt_commitmsg, st.st_size) == -1, errno,
01186 "munmap()");
01187 if (commitmsg_is_temp)
01188 STOPIF_CODE_ERR( unlink(opt_commitmsgfile) == -1, errno,
01189 "Cannot remove temporary message file %s", opt_commitmsgfile);
01190
01191
01192
01193 STOPIF_SVNERR( editor->open_root,
01194 (edit_baton, current_url->current_rev, global_pool, &root_baton) );
01195
01196
01197 if (ops__allowed_by_filter(root))
01198 STOPIF( hlp__lstat( root->name, &root->st), NULL);
01199
01200
01201 committed_entries=0;
01202 if (missing_dirs)
01203 {
01204 STOPIF( hlp__local2utf8( missing_dirs, &missing_dirs, -1), NULL);
01205
01206
01207 missing_path_utf8_len=strlen(missing_dirs);
01208 STOPIF( hlp__strnalloc(missing_path_utf8_len+1,
01209 &missing_path_utf8, missing_dirs), NULL);
01210 }
01211
01212
01213
01214 STOPIF_SVNERR( ci___base_dirs,
01215 (missing_path_utf8, editor, root, root_baton));
01216
01217
01218
01219 if (!status)
01220 {
01221 if (opt__get_int(OPT__EMPTY_COMMIT)==OPT__NO &&
01222 committed_entries==0)
01223 {
01224 if (opt__get_int(OPT__VERBOSE) > VERBOSITY_VERYQUIET)
01225 printf("Avoiding empty commit as requested.\n");
01226 goto abort_commit;
01227 }
01228
01229
01230 STOPIF_SVNERR( editor->close_edit, (edit_baton, global_pool) );
01231 edit_baton=NULL;
01232
01233 delay_start=time(NULL);
01234
01235
01236 if (!status)
01237 {
01238
01239
01240
01241
01242
01243
01244
01245 STOPIF( waa__output_tree(root), NULL);
01246 STOPIF( url__output_list(), NULL);
01247 }
01248
01249
01250
01251 STOPIF( hlp__delay(delay_start, DELAY_COMMIT), NULL);
01252 }
01253
01254 ex:
01255 STOP_HANDLE_SVNERR(status_svn);
01256
01257 ex2:
01258 if (status && edit_baton)
01259 {
01260 abort_commit:
01261
01262
01263 editor->abort_edit(edit_baton, global_pool);
01264 }
01265
01266 return status;
01267 }
01268