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